diff --git a/requirements.txt b/requirements.txt index 40ea3c0..7d4b299 100755 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,7 @@ protobuf==3.10.0 PyCryptodome PyMuPDF PyPDF2 -PySimpleGUI +pytz simplekml xmltodict xlrd==1.2.0 - diff --git a/scripts/icon.png b/scripts/icon.png new file mode 100644 index 0000000..c7afa9f Binary files /dev/null and b/scripts/icon.png differ diff --git a/scripts/ilapfuncs.py b/scripts/ilapfuncs.py index 228d035..e9bbe3d 100755 --- a/scripts/ilapfuncs.py +++ b/scripts/ilapfuncs.py @@ -3,7 +3,6 @@ import csv import datetime import os -import pathlib import re import shutil import sqlite3 @@ -138,21 +137,28 @@ def does_table_exist(db, table_name): class GuiWindow: '''This only exists to hold window handle if script is run from GUI''' window_handle = None # static variable - progress_bar_total = 0 - progress_bar_handle = None @staticmethod - def SetProgressBar(n): - if GuiWindow.progress_bar_handle: - GuiWindow.progress_bar_handle.UpdateBar(n) + def SetProgressBar(n, total): + if GuiWindow.window_handle: + progress_bar = GuiWindow.window_handle.nametowidget('!progressbar') + progress_bar.config(value=n) + def logfunc(message=""): + def redirect_logs(string): + log_text.insert('end', string) + log_text.see('end') + log_text.update() + + if GuiWindow.window_handle: + log_text = GuiWindow.window_handle.nametowidget('logs_frame.log_text') + sys.stdout.write = redirect_logs + with open(OutputParameters.screen_output_file_path, 'a', encoding='utf8') as a: print(message) a.write(message + '
' + OutputParameters.nl) - if GuiWindow.window_handle: - GuiWindow.window_handle.refresh() def logdevinfo(message=""): with open(OutputParameters.screen_output_file_path_devinfo, 'a', encoding='utf8') as b: diff --git a/scripts/version_info.py b/scripts/version_info.py index bc8e36f..5c22fb9 100755 --- a/scripts/version_info.py +++ b/scripts/version_info.py @@ -1,4 +1,4 @@ -vleapp_version = '2.0.0' +vleapp_version = '2.1.0' # Contributors List # Format = [ Name, Blog-url, Twitter-handle, Github-url] @@ -8,6 +8,6 @@ [ 'Yogesh Khatri', 'https://swiftforensics.com', '@SwiftForensics', 'https://github.com/ydkhatri'], [ 'Geraldine Blay', 'https://gforce4n6.blogspot.com', '@i_am_the_gia', 'https://github.com/gforce4n6'], [ 'Kevin Pagano', 'https://stark4n6.com', '@KevinPagano3', 'https://github.com/stark4n6'], - ['James Habben', 'https://4n6ir.com/','@JamesHabben','https://github.com/JamesHabben'] - + ['James Habben', 'https://4n6ir.com/','@JamesHabben','https://github.com/JamesHabben'], + ['Johann Polewczyk', 'https://www.linkedin.com/in/johann-polewczyk-6a905425/', '@johannplw', 'https://github.com/Johann-PLW'] ] diff --git a/vleapp.py b/vleapp.py index 1bf000f..fff3e3b 100755 --- a/vleapp.py +++ b/vleapp.py @@ -43,73 +43,70 @@ def validate_args(args): # raise argparse.ArgumentError(None, 'Unknown timezone! Run the program again.') -def create_profile(available_plugins, path): - available_parsers = [] - for parser_data in available_plugins: - available_parsers.append((parser_data.category, parser_data.name)) - - available_parsers.sort() - parsers_in_profile = {} - +def create_profile(plugins, path): + available_modules = [(module_data.category, module_data.name) for module_data in plugins] + available_modules.sort() + modules_in_profile = {} + user_choice = '' print('--- VLEAPP Profile file creation ---\n') instructions = 'You can type:\n' - instructions += ' - \'a\' to add or remove parsers in the profile file\n' - instructions += ' - \'l\' to display the list of all available parsers with their number\n' - instructions += ' - \'p\' to display the parsers added into the profile file\n' + instructions += ' - \'a\' to add or remove modules in the profile file\n' + instructions += ' - \'l\' to display the list of all available modules with their number\n' + instructions += ' - \'p\' to display the modules added into the profile file\n' instructions += ' - \'q\' to quit and save\n' while not user_choice: print(instructions) user_choice = input('Please enter your choice: ').lower() print() if user_choice == "l": - print('Available parsers:') - for number, available_plugin in enumerate(available_parsers): - print(number + 1, available_plugin) + print('Available modules:') + for number, available_module in enumerate(available_modules): + print(number + 1, available_module) print() user_choice = '' elif user_choice == "p": - if parsers_in_profile: - for number, parser in parsers_in_profile.items(): - print(number, parser) + if modules_in_profile: + for number, module in modules_in_profile.items(): + print(number, module) print() else: - print('No parser added to the profile file\n') + print('No module added to the profile file\n') user_choice = '' elif user_choice == 'a': - parser_numbers = input('Enter the numbers of parsers, seperated by a comma, to add or remove in the profile file: ') - parser_numbers = parser_numbers.split(',') - parser_numbers = [parser_number.strip() for parser_number in parser_numbers] - for parser_number in parser_numbers: - if parser_number.isdigit(): - parser_number = int(parser_number) - if parser_number > 0 and parser_number <= len(available_parsers): - if parser_number not in parsers_in_profile: - parser_to_add = available_parsers[parser_number - 1] - parsers_in_profile[parser_number] = parser_to_add - print(f'parser number {parser_number} {parser_to_add} was added') + modules_numbers = input('Enter the numbers of modules, seperated by a comma, to add or remove in the profile file: ') + modules_numbers = modules_numbers.split(',') + modules_numbers = [module_number.strip() for module_number in modules_numbers] + for module_number in modules_numbers: + if module_number.isdigit(): + module_number = int(module_number) + if module_number > 0 and module_number <= len(available_modules): + if module_number not in modules_in_profile: + module_to_add = available_modules[module_number - 1] + modules_in_profile[module_number] = module_to_add + print(f'module number {module_number} {module_to_add} was added') else: - parser_to_remove = parsers_in_profile[parser_number] - print(f'parser number {parser_number} {parser_to_remove} was removed') - del parsers_in_profile[parser_number] + module_to_remove = modules_in_profile[module_number] + print(f'module number {module_number} {module_to_remove} was removed') + del modules_in_profile[module_number] else: - print('Please enter the number of a parser!!!\n') + print('Please enter the number of a module!!!\n') print() user_choice = '' elif user_choice == "q": - if parsers_in_profile: - parsers = [parser_info[1] for parser_info in parsers_in_profile.values()] + if modules_in_profile: + modules = [module_info[1] for module_info in modules_in_profile.values()] profile_filename = '' while not profile_filename: profile_filename = input('Enter the name of the profile: ') profile_filename += '.vlprofile' filename = os.path.join(path, profile_filename) with open(filename, "wt", encoding="utf-8") as profile_file: - json.dump({"leapp": "vleapp", "format_version": 1, "plugins": parsers}, profile_file) + json.dump({"leapp": "vleapp", "format_version": 1, "plugins": modules}, profile_file) print('\nProfile saved:', filename) print() else: - print('No parser added. The profile file was not created.\n') + print('No module added. The profile file was not created.\n') print() return else: @@ -139,9 +136,9 @@ def main(): parser = argparse.ArgumentParser(description='VLEAPP: Vehicle Logs, Events, and Protobuf Parser.') parser.add_argument('-t', choices=['fs', 'tar', 'zip', 'gz'], required=False, action="store", help=("Specify the input type. " - "'fs' for a folder containing extracted files with normal paths and names, " - "'tar', 'zip', or 'gz' for compressed packages containing files with normal names. " - )) + "'fs' for a folder containing extracted files with normal paths and names, " + "'tar', 'zip', or 'gz' for compressed packages containing files with normal names. " + )) parser.add_argument('-o', '--output_path', required=False, action="store", help='Path to base output folder (this must exist)') parser.add_argument('-i', '--input_path', required=False, action="store", help='Path to input file/folder') @@ -201,7 +198,7 @@ def main(): create_choice = input('Please enter your choice: ').lower() print() if create_choice == '1': - create_profile(selected_plugins, args.create_profile_casedata) + create_profile(available_plugins, args.create_profile_casedata) create_choice = '' elif create_choice == '2': create_casedata(args.create_profile_casedata) @@ -258,7 +255,6 @@ def main(): print(profile_load_error) return else: - print(f'Profile loaded: {profile_filename}') profile_plugins = set(profile.get("plugins", [])) selected_plugins = [selected_plugin for selected_plugin in available_plugins if selected_plugin.name in profile_plugins] @@ -280,13 +276,12 @@ def main(): if output_path[1] == ':': output_path = '\\\\?\\' + output_path.replace('/', '\\') out_params = OutputParameters(output_path) - print(f"Info: {len(available_plugins)} plugins loaded.") - crunch_artifacts(selected_plugins, extracttype, input_path, out_params, 1, wrap_text, loader, casedata, time_offset, profile_filename) + crunch_artifacts(selected_plugins, extracttype, input_path, out_params, wrap_text, loader, casedata, time_offset, profile_filename) def crunch_artifacts( - plugins: typing.Sequence[plugin_loader.PluginSpec], extracttype, input_path, out_params, ratio, wrap_text, + plugins: typing.Sequence[plugin_loader.PluginSpec], extracttype, input_path, out_params, wrap_text, loader: plugin_loader.PluginLoader, casedata, time_offset, profile_filename): start = process_time() start_wall = perf_counter() @@ -323,6 +318,7 @@ def crunch_artifacts( return False # Now ready to run + logfunc(f'Info: {len(loader)} modules loaded.') if profile_filename: logfunc(f'Loaded profile: {profile_filename}') logfunc(f'Artifact categories to parse: {len(plugins)}') @@ -333,7 +329,7 @@ def crunch_artifacts( log.write(f'Extraction/Path selected: {input_path}

') log.write(f'Timezone selected: {time_offset}

') - categories_searched = 0 + parsed_modules = 0 # Search for the files per the arguments for plugin in plugins: @@ -341,8 +337,10 @@ def crunch_artifacts( search_regexes = plugin.search else: search_regexes = [plugin.search] + parsed_modules += 1 + GuiWindow.SetProgressBar(parsed_modules, len(plugins)) files_found = [] - log.write(f'For {plugin.name} parser') + log.write(f'For {plugin.name} module') for artifact_search_regex in search_regexes: found = seeker.search(artifact_search_regex) if not found: @@ -356,6 +354,7 @@ def crunch_artifacts( log.write(f'') files_found.extend(found) if files_found: + logfunc() logfunc('{} [{}] artifact started'.format(plugin.name, plugin.module_name)) category_folder = os.path.join(out_params.report_folder_base, plugin.category) if not os.path.exists(category_folder): @@ -374,10 +373,7 @@ def crunch_artifacts( continue # nope logfunc('{} [{}] artifact completed'.format(plugin.name, plugin.module_name)) - logfunc('') - categories_searched += 1 - GuiWindow.SetProgressBar(categories_searched * ratio) log.close() logfunc('') diff --git a/vleapp.spec b/vleapp.spec index f5bcf8a..5596bd3 100755 --- a/vleapp.spec +++ b/vleapp.spec @@ -5,12 +5,7 @@ block_cipher = None a = Analysis(['vleapp.py'], pathex=['.\\scripts\\artifacts'], binaries=[], - datas=[('.\\scripts\\logo.jpg', '.\\scripts'), - ('.\\scripts\\dashboard.css', '.\\scripts'), - ('.\\scripts\\dark-mode.css', '.\\scripts'), - ('.\\scripts\\dark-mode-switch.js', '.\\scripts'), - ('.\\scripts\\MDB-Free_4.13.0', '.\\scripts\\MDB-Free_4.13.0'), - ('.\\scripts\\artifacts', '\\scripts\\artifacts')], + datas=[('.\\scripts', '.\\scripts')], hiddenimports=['simplekml'], hookspath=['.\\'], runtime_hooks=[], diff --git a/vleappGUI.py b/vleappGUI.py index d3867bb..7479496 100755 --- a/vleappGUI.py +++ b/vleappGUI.py @@ -1,345 +1,529 @@ -import json +import tkinter as tk +import plugin_loader import typing +import json import vleapp -import PySimpleGUI as sg import webbrowser -import plugin_loader -from scripts.ilapfuncs import * + +from tkinter import ttk, filedialog as tk_filedialog, messagebox as tk_msgbox from scripts.version_info import vleapp_version from scripts.search_files import * -MODULE_START_INDEX = 1000 - -def add_case_data(casedata): - case_data_font = ("Helvetica", 12) - case_data_layout = [ - [sg.Text('Add Case Data', font=("Helvetica", 18))], - [sg.Frame(layout=[ - [sg.Input(size=(80,1), key='Case Number', default_text=casedata.get('Case Number', ''))]], title='Case Number')], - [sg.Frame(layout=[ - [sg.Input(size=(80,1), key='Agency', default_text=casedata.get('Agency', ''))]], title='Agency')], - [sg.Frame(layout=[ - [sg.Input(size=(80,1), key='Examiner', default_text=casedata.get('Examiner', ''))]], title='Examiner')], - [sg.Text('')], - [sg.Button('Load Case Data File', font=normal_font, key='LOADCASEDATA'), - sg.Button('Save Case Data File', font=normal_font, key='SAVECASEDATA'), - sg.Text(' | ', font=("Helvetica", 14)), - sg.Button('Clear', font=normal_font, key='CLEAR'), - sg.Button('Close', font=normal_font)] - ] - - case_data_window = sg.Window('Add Case Data', case_data_layout, font=case_data_font) - - while True: - case_data_event, case_data_values = case_data_window.read() - - if case_data_event in (None, 'Close'): - case_data_window.close() - return case_data_values - - if case_data_event == 'CLEAR': - case_data_window['Case Number'].update('') - case_data_window['Agency'].update('') - case_data_window['Examiner'].update('') - continue - - if case_data_event == 'SAVECASEDATA': - destination_path = sg.popup_get_file( - "Save case data file", save_as=True, - file_types=(('LEAPP Case Data (*.lcasedata)', '*.lcasedata'),), - default_extension='.icasedata', no_window=True, keep_on_top=True) - - if destination_path: - with open(destination_path, "wt", encoding="utf-8") as case_data_out: - json.dump({"leapp": "case_data", "case_data_values": case_data_values}, case_data_out) - sg.Popup(f"Case Data saved: {destination_path}", title="Save Case Data") - case_data_window.close() - else: - continue - - return case_data_values - - if case_data_event == 'LOADCASEDATA': - destination_path = sg.popup_get_file( - "Load case data", save_as=False, - file_types=(('LEAPP Case Data (*.lcasedata)', '*.lcasedata'), ('All Files', '*')), - default_extension='.lcasedata', no_window=True) - - if destination_path and os.path.exists(destination_path): - case_data_load_error = None - with open(destination_path, "rt", encoding="utf-8") as case_data_in: - try: - case_data = json.load(case_data_in) - except: - case_data_load_error = "File was not a valid case data file: invalid format" - - if not case_data_load_error: - if isinstance(case_data, dict): - if case_data.get("leapp") != "case_data": - case_data_load_error = "File was not a valid case data file" - else: - casedata = case_data.get('case_data_values', {}) - for key, value in casedata.items(): - case_data_window[key].update(value) - else: - case_data_load_error = "File was not a valid case data file: invalid format" - - if case_data_load_error: - sg.popup(case_data_load_error) - continue + +def pickModules(): + '''Create a list of available modules: + - exclude ones that are required + - ones that take a long time to run are deselected by default''' + global mlist + global loader + + loader = plugin_loader.PluginLoader() + + for plugin in sorted(loader.plugins, key=lambda p: p.category.upper()): + # Modules that are required are not added to the list + # if plugin.module_name == '': + # continue + #items that take a long time to run are deselected by default. + enabled = not plugin.module_name == 'walStrings' + mlist[plugin] = tk.BooleanVar(value=enabled) + + +def get_selected_modules(): + '''Update the number and return the list of selected modules''' + selected_modules = [] + + for plugin, state in mlist.items(): + if state.get(): + selected_modules.append(plugin.name) + + selected_modules_label.config(text=f'Number of selected modules: {len(selected_modules)} / {len(mlist)}') + return selected_modules + + +def select_all(): + '''Select all modules in the list of available modules and execute get_selected_modules''' + for plugin in mlist: + main_window.nametowidget(f'f_modules.f_list.tbox.mcb_{plugin.name}').select() + + get_selected_modules() + + +def deselect_all(): + '''Unselect all modules in the list of available modules and execute get_selected_modules''' + for plugin in mlist: + main_window.nametowidget(f'f_modules.f_list.tbox.mcb_{plugin.name}').deselect() + + get_selected_modules() + + +def load_profile(): + '''Select modules from a profile file''' + global profile_filename + + destination_path = tk_filedialog.askopenfilename(parent=main_window, + title='Load a profile', + filetypes=(('VLEAPP Profile', '*.vlprofile'),)) + + if destination_path and os.path.exists(destination_path): + profile_load_error = None + with open(destination_path, 'rt', encoding='utf-8') as profile_in: + try: + profile = json.load(profile_in) + except: + profile_load_error = 'File was not a valid profile file: invalid format' + if not profile_load_error: + if isinstance(profile, dict): + if profile.get('leapp') != 'vleapp' or profile.get('format_version') != 1: + profile_load_error = 'File was not a valid profile file: incorrect LEAPP or version' else: - sg.popup(f"Loaded Case Data: {destination_path}", title="Load Case Data") - continue + deselect_all() + ticked = set(profile.get('plugins', [])) + for plugin in mlist: + if plugin.name in ticked: + main_window.nametowidget(f'f_modules.f_list.tbox.mcb_{plugin.name}').select() + get_selected_modules() else: - continue + profile_load_error = 'File was not a valid profile file: invalid format' + if profile_load_error: + tk_msgbox.showerror(title='Error', message=profile_load_error, parent=main_window) + else: + profile_filename = destination_path + tk_msgbox.showinfo( + title='Profile loaded', message=f'Loaded profile: {destination_path}', parent=main_window) + + +def save_profile(): + '''Save selected modules in a profile file''' + destination_path = tk_filedialog.asksaveasfilename(parent=main_window, + title='Save a profile', + filetypes=(('VLEAPP Profile', '*.vlprofile'),)) + + if destination_path: + selected_modules = get_selected_modules() + with open(destination_path, 'wt', encoding='utf-8') as profile_out: + json.dump({'leapp': 'vleapp', 'format_version': 1, 'plugins': selected_modules}, profile_out) + tk_msgbox.showinfo( + title='Save a profile', message=f'Profile saved: {destination_path}', parent=main_window) -def ValidateInput(values, window): - '''Returns tuple (success, extraction_type)''' - global module_end_index - i_path = values['INPUTPATH'] # input file/folder - o_path = values['OUTPUTPATH'] # output folder +def scroll(event): + '''Continue to scroll the list with mouse wheel when cursor hover a checkbutton''' + parent = main_window.nametowidget(event.widget.winfo_parent()) + parent.event_generate('', delta=event.delta, when='now') + + +def ValidateInput(): + '''Returns tuple (success, extraction_type)''' + i_path = input_entry.get() # input file/folder + o_path = output_entry.get() # output folder ext_type = '' + # check input if len(i_path) == 0: - sg.PopupError('No INPUT file or folder selected!', title="Error") + tk_msgbox.showerror(title='Error', message='No INPUT file or folder selected!', parent=main_window) return False, ext_type elif not os.path.exists(i_path): - sg.PopupError('INPUT file/folder does not exist!', title="Error") + tk_msgbox.showerror(title='Error', message='INPUT file/folder does not exist!', parent=main_window) return False, ext_type elif os.path.isdir(i_path): ext_type = 'fs' - else: # must be an existing file then - if not i_path.lower().endswith('.tar') and not i_path.lower().endswith('.zip') and not i_path.lower().endswith('.gz'): - sg.PopupError('Input file is not a supported archive! ', i_path, title="Error") - return False, ext_type - else: - ext_type = Path(i_path).suffix[1:].lower() - + else: + ext_type = Path(i_path).suffix[1:].lower() + # check output now if len(o_path) == 0 : # output folder - sg.PopupError('No OUTPUT folder selected!', title="Error") + tk_msgbox.showerror(title='Error', message='No OUTPUT folder selected!', parent=main_window) return False, ext_type - one_element_is_selected = False - for x in range(1000, module_end_index): - if window[x].Get(): - one_element_is_selected = True - break - if not one_element_is_selected: - sg.PopupError('No module selected for processing!', title="Error") + # check if at least one module is selected + if len(get_selected_modules()) == 0: + tk_msgbox.showerror(title='Error', message='No module selected for processing!', parent=main_window) return False, ext_type return True, ext_type -# initialize CheckBox control with module name -def CheckList(mtxt, lkey, mdstring, disable=False): - if mdstring == 'walStrings': #items in the if are modules that take a long time to run. Deselects them by default. - dstate = False - else: - dstate = True - return [sg.CBox(mtxt, default=dstate, key=lkey, metadata=mdstring, disabled=disable)] - -def pickModules(): - global module_end_index - global mlist - global loader - - loader = plugin_loader.PluginLoader() - module_end_index = MODULE_START_INDEX # arbitrary number to not interfere with other controls - for plugin in sorted(loader.plugins, key=lambda p: p.category.upper()): - disabled = False # Name of the module needed to be executed first i.e: plugin.module_name == 'usagestatsVersion' - mlist.append(CheckList(f'{plugin.category} [{plugin.name} - {plugin.module_name}.py]', module_end_index, plugin.name, disabled)) - module_end_index = module_end_index + 1 +def open_report(report_path): + '''Open report and Quit after processing completed''' + webbrowser.open_new_tab('file://' + report_path) + main_window.quit() + + +def process(casedata): + '''Execute selected modules and create reports''' + #check if selections made properly; if not we will return to input form without exiting app altogether + is_valid, extracttype = ValidateInput() + + if is_valid: + GuiWindow.window_handle = main_window + input_path = input_entry.get() + output_folder = output_entry.get() + + # File system extractions contain paths > 260 char, which causes problems + # This fixes the problem by prefixing \\?\ on each windows path. + if is_platform_windows(): + if input_path[1] == ':' and extracttype =='fs': input_path = '\\\\?\\' + input_path.replace('/', '\\') + if output_folder[1] == ':': output_folder = '\\\\?\\' + output_folder.replace('/', '\\') + + # re-create modules list based on user selection + selected_modules = get_selected_modules() + selected_modules = [loader[module] for module in selected_modules] + progress_bar.config(maximum=len(selected_modules)) + casedata = {key:value.get() for key, value in casedata.items()} + out_params = OutputParameters(output_folder) + wrap_text = True + time_offset = timezone_set.get() + if time_offset == '': + time_offset = 'UTC' -sg.theme('DarkPurple6') # Add a touch of color -# All the stuff inside your window. + logtext_frame.grid(row=1, column=0, rowspan=3, padx=14, pady=4, sticky='nswe') + bottom_frame.grid_remove() + progress_bar.grid(padx=16, pady=6, sticky='we') + + crunch_successful = vleapp.crunch_artifacts( + selected_modules, extracttype, input_path, out_params, wrap_text, loader, + casedata, time_offset, profile_filename) + + if crunch_successful: + report_path = os.path.join(out_params.report_folder_base, 'index.html') + if report_path.startswith('\\\\?\\'): # windows + report_path = report_path[4:] + if report_path.startswith('\\\\'): # UNC path + report_path = report_path[2:] + progress_bar.grid_remove() + open_report_button = ttk.Button(main_window, text='Open Report & Close', command=lambda: open_report(report_path)) + open_report_button.grid(ipadx=8) + else: + log_path = out_params.screen_output_file_path + if log_path.startswith('\\\\?\\'): # windows + log_path = log_path[4:] + tk_msgbox.showerror( + title='Error', + message=f'Processing failed :( \nSee log for error details..\nLog file located at {log_path}', + parent=main_window) + + +def select_input(button_type): + '''Select source and insert its path into input field''' + if button_type == 'file': + input_filename = tk_filedialog.askopenfilename(parent=main_window, + title='Select a file', + filetypes=(('tar file', '*.tar'), ('zip file', '*.zip'), ('gz file', '*.gz'))) + else: + input_filename = tk_filedialog.askdirectory(parent=main_window, title='Select a folder') + input_entry.delete(0, 'end') + input_entry.insert(0, input_filename) -normal_font = ("Helvetica", 12) -if is_platform_macos(): - log_window_height = 29 -elif is_platform_linux(): - log_window_height = 23 -else: - log_window_height = 20 +def select_output(): + '''Select target and insert its path into output field''' + output_filename = tk_filedialog.askdirectory(parent=main_window, title='Select a folder') + output_entry.delete(0, 'end') + output_entry.insert(0, output_filename) -loader: typing.Optional[plugin_loader.PluginLoader] = None -mlist = [] -# go through list of available modules and confirm they exist on the disk -pickModules() -GuiWindow.progress_bar_total = len(loader) -tzvalues = ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Juba', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Timbuktu', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek', 'America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Atka', 'America/Bahia', 'America/Bahia_Banderas', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Buenos_Aires', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Catamarca', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Ciudad_Juarez', 'America/Coral_Harbour', 'America/Cordoba', 'America/Costa_Rica', 'America/Creston', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Ensenada', 'America/Fort_Nelson', 'America/Fort_Wayne', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Jujuy', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/Kralendijk', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Louisville', 'America/Lower_Princes', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Mendoza', 'America/Menominee', 'America/Merida', 'America/Metlakatla', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Nuuk', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Punta_Arenas', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Rosario', 'America/Santa_Isabel', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Shiprock', 'America/Sitka', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Virgin', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'America/Yellowknife', 'Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok', 'Arctic/Longyearbyen', 'Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Ashkhabad', 'Asia/Atyrau', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Barnaul', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Calcutta', 'Asia/Chita', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Chungking', 'Asia/Colombo', 'Asia/Dacca', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Famagusta', 'Asia/Gaza', 'Asia/Harbin', 'Asia/Hebron', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Istanbul', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kashgar', 'Asia/Kathmandu', 'Asia/Katmandu', 'Asia/Khandyga', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macao', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qostanay', 'Asia/Qyzylorda', 'Asia/Rangoon', 'Asia/Riyadh', 'Asia/Saigon', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Srednekolymsk', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Tel_Aviv', 'Asia/Thimbu', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Tomsk', 'Asia/Ujung_Pandang', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator', 'Asia/Urumqi', 'Asia/Ust-Nera', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yangon', 'Asia/Yekaterinburg', 'Asia/Yerevan', 'Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faeroe', 'Atlantic/Faroe', 'Atlantic/Jan_Mayen', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley', 'Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/NSW', 'Australia/North', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna', 'Brazil/Acre', 'Brazil/DeNoronha', 'Brazil/East', 'Brazil/West', 'CET', 'CST6CDT', 'Canada/Atlantic', 'Canada/Central', 'Canada/Eastern', 'Canada/Mountain', 'Canada/Newfoundland', 'Canada/Pacific', 'Canada/Saskatchewan', 'Canada/Yukon', 'Chile/Continental', 'Chile/EasterIsland', 'Cuba', 'EET', 'EST', 'EST5EDT', 'Egypt', 'Eire', 'Etc/GMT', 'Etc/GMT+0', 'Etc/GMT+1', 'Etc/GMT+10', 'Etc/GMT+11', 'Etc/GMT+12', 'Etc/GMT+2', 'Etc/GMT+3', 'Etc/GMT+4', 'Etc/GMT+5', 'Etc/GMT+6', 'Etc/GMT+7', 'Etc/GMT+8', 'Etc/GMT+9', 'Etc/GMT-0', 'Etc/GMT-1', 'Etc/GMT-10', 'Etc/GMT-11', 'Etc/GMT-12', 'Etc/GMT-13', 'Etc/GMT-14', 'Etc/GMT-2', 'Etc/GMT-3', 'Etc/GMT-4', 'Etc/GMT-5', 'Etc/GMT-6', 'Etc/GMT-7', 'Etc/GMT-8', 'Etc/GMT-9', 'Etc/GMT0', 'Etc/Greenwich', 'Etc/UCT', 'Etc/UTC', 'Etc/Universal', 'Etc/Zulu', 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Astrakhan', 'Europe/Athens', 'Europe/Belfast', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Busingen', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Kirov', 'Europe/Kyiv', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Nicosia', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Saratov', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Tiraspol', 'Europe/Ulyanovsk', 'Europe/Uzhgorod', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zaporozhye', 'Europe/Zurich', 'GB', 'GB-Eire', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'Greenwich', 'HST', 'Hongkong', 'Iceland', 'Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion', 'Iran', 'Israel', 'Jamaica', 'Japan', 'Kwajalein', 'Libya', 'MET', 'MST', 'MST7MDT', 'Mexico/BajaNorte', 'Mexico/BajaSur', 'Mexico/General', 'NZ', 'NZ-CHAT', 'Navajo', 'PRC', 'PST8PDT', 'Pacific/Apia', 'Pacific/Auckland', 'Pacific/Bougainville', 'Pacific/Chatham', 'Pacific/Chuuk', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Johnston', 'Pacific/Kanton', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Pohnpei', 'Pacific/Ponape', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Samoa', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Truk', 'Pacific/Wake', 'Pacific/Wallis', 'Pacific/Yap', 'Poland', 'Portugal', 'ROC', 'ROK', 'Singapore', 'Turkey', 'UCT', 'US/Alaska', 'US/Aleutian', 'US/Arizona', 'US/Central', 'US/East-Indiana', 'US/Eastern', 'US/Hawaii', 'US/Indiana-Starke', 'US/Michigan', 'US/Mountain', 'US/Pacific', 'US/Samoa', 'UTC', 'Universal', 'W-SU', 'WET', 'Zulu'] +# GUI layout +## Case Data +def case_data(): + '''Add Case Data window''' + global casedata + + def clear(): + '''Remove the contents of all fields''' + case_number_entry.delete(0, 'end') + case_agency_entry.delete(0, 'end') + case_examiner_entry.delete(0, 'end') + -layout = [ [sg.Text('Vehicle Logs, Events, And Protobuf Parser', font=("Helvetica", 22))], - [sg.Text('https://github.com/abrignoni/VLEAPP', font=("Helvetica", 14))], - [sg.Frame(layout=[ - [sg.Input(size=(97,1), key='INPUTPATH'), - sg.FileBrowse(font=normal_font, button_text='Browse File', key='INPUTFILEBROWSE'), - sg.FolderBrowse(font=normal_font, button_text='Browse Folder', target=(sg.ThisRow, -2), key='INPUTFOLDERBROWSE') - ] - ], - title='Select the file (tar/zip/gz) or directory containing the data to be parsed:')], - [sg.Frame(layout=[ - [sg.Input(size=(112,1), key='OUTPUTPATH'), - sg.FolderBrowse(font=normal_font, button_text='Browse Folder') - ] - ], - title='Select Output Folder:')], - [sg.Text('Available Modules')], - [sg.Button('Select All', key='SELECT ALL'), sg.Button('Deselect All', key='DESELECT ALL'), - sg.Button('Load Profile', key='LOAD PROFILE'), sg.Button('Save Profile', key='SAVE PROFILE'), - # sg.FileBrowse( - # button_text='Load Profile', key='LOADPROFILE', enable_events=True, target='LOADPROFILE', - # file_types=(('VLEAPP Profile (*.vlprofile)', '*.vlprofile'), ('All Files', '*'))), - # sg.FileSaveAs( - # button_text='Save Profile', key='SAVEPROFILE', enable_events=True, target='SAVEPROFILE', - # file_types=(('VLEAPP Profile (*.vlprofile)', '*.vlprofile'), ('All Files', '*')), - # default_extension='.vlprofile') - sg.Text(' |', font=("Helvetica", 14)), - sg.Button('Add Case Data', key='ADD CASE DATA'), - sg.Text(' |', font=("Helvetica", 14)), - sg.Text('Timezone Offset (Not Implemented Yet):', font=("Helvetica", 14)), - sg.Combo(list(tzvalues), size=(20,15), key='timezone',readonly=True) - ], - [sg.Column(mlist, size=(300,310), scrollable=True), sg.Output(size=(85,log_window_height))], - [sg.ProgressBar(max_value=GuiWindow.progress_bar_total, orientation='h', size=(86, 7), key='PROGRESSBAR', bar_color=('DarkGreen', 'White'))], - [sg.Submit('Process', font=normal_font), sg.Button('Close', font=normal_font)] ] - -# Create the Window -window = sg.Window(f'VLEAPP version {vleapp_version}', layout) -GuiWindow.progress_bar_handle = window['PROGRESSBAR'] -profile_filename = None -casedata = {} - -# Event Loop to process "events" and get the "values" of the inputs -while True: - event, values = window.read() - if event in (None, 'Close'): # if user closes window or clicks cancel - break - - if event == "SELECT ALL": - # mark all modules - for x in range(MODULE_START_INDEX, module_end_index): - window[x].Update(True) - - if event == "DESELECT ALL": - # none modules - for x in range(MODULE_START_INDEX, module_end_index): - # window[x].Update(False if window[x].metadata != 'usagestatsVersion' else True) # usagestatsVersion.py is REQUIRED - window[x].Update(False) # No module is required nor need to be executed first at the moment - if event == "SAVE PROFILE": - destination_path = sg.popup_get_file( - "Save a profile", save_as=True, - file_types=(('VLEAPP Profile (*.vlprofile)', '*.vlprofile'),), - default_extension='.vlprofile', no_window=True) + def save_case(): + '''Save case data in a Case Data file''' + destination_path = tk_filedialog.asksaveasfilename(parent=case_window, + title='Save a case data file', + filetypes=(('LEAPP Case Data', '*.lcasedata'),)) if destination_path: - ticked = [] - for x in range(MODULE_START_INDEX, module_end_index): - if window[x].Get(): - key = window[x].metadata - ticked.append(key) - with open(destination_path, "wt", encoding="utf-8") as profile_out: - json.dump({"leapp": "vleapp", "format_version": 1, "plugins": ticked}, profile_out) - sg.Popup(f"Profile saved: {destination_path}", title="Save a profile") - - if event == "LOAD PROFILE": - destination_path = sg.popup_get_file( - "Load a profile", save_as=False, - file_types=(('VLEAPP Profile (*.vlprofile)', '*.vlprofile'), ('All Files', '*')), - default_extension='.vlprofile', no_window=True) + json_casedata = {key:value.get() for key, value in casedata.items()} + with open(destination_path, 'wt', encoding='utf-8') as case_data_out: + json.dump({'leapp': 'case_data', 'case_data_values': json_casedata}, case_data_out) + tk_msgbox.showinfo( + title='Save Case Data', message=f'Case Data saved: {destination_path}', parent=case_window) + + + def load_case(): + '''Import case data from a Case Data file''' + destination_path = tk_filedialog.askopenfilename(parent=case_window, + title='Load case data', + filetypes=(('LEAPP Case Data', '*.lcasedata'),)) if destination_path and os.path.exists(destination_path): - profile_load_error = None - with open(destination_path, "rt", encoding="utf-8") as profile_in: + case_data_load_error = None + with open(destination_path, 'rt', encoding='utf-8') as case_data_in: try: - profile = json.load(profile_in) + case_data = json.load(case_data_in) except: - profile_load_error = "File was not a valid profile file: invalid format" - - if not profile_load_error: - if isinstance(profile, dict): - if profile.get("leapp") != "vleapp" or profile.get("format_version") != 1: - profile_load_error = "File was not a valid profile file: incorrect LEAPP or version" + case_data_load_error = 'File was not a valid case data file: invalid format' + if not case_data_load_error: + if isinstance(case_data, dict): + if case_data.get('leapp') != 'case_data': + case_data_load_error = 'File was not a valid case data file' else: - ticked = set(profile.get("plugins", [])) - # ticked.add("usagestatsVersion") # always - for x in range(MODULE_START_INDEX, module_end_index): - if window[x].metadata in ticked: - window[x].update(True) - else: - window[x].update(False) + casedata = case_data.get('case_data_values', {}) + case_number_entry.delete(0, 'end') + case_number_entry.insert(0, casedata.get('Case Number', '')) + case_agency_entry.delete(0, 'end') + case_agency_entry.insert(0, casedata.get('Agency', '')) + case_examiner_entry.delete(0, 'end') + case_examiner_entry.insert(0, casedata.get('Examiner', '')) else: - profile_load_error = "File was not a valid profile file: invalid format" - - if profile_load_error: - sg.popup(profile_load_error) - else: - sg.popup(f"Loaded profile: {destination_path}", title="Load a profile") - profile_filename = destination_path - - if event == "ADD CASE DATA": - casedata = add_case_data(casedata) - - if event == 'Process': - #check is selections made properly; if not we will return to input form without exiting app altogether - is_valid, extracttype = ValidateInput(values, window) - if is_valid: - GuiWindow.window_handle = window - input_path = values['INPUTPATH'] - output_folder = values['OUTPUTPATH'] - - # File system extractions contain paths > 260 char, which causes problems - # This fixes the problem by prefixing \\?\ on each windows path. - if is_platform_windows(): - if input_path[1] == ':' and extracttype =='fs': input_path = '\\\\?\\' + input_path.replace('/', '\\') - if output_folder[1] == ':': output_folder = '\\\\?\\' + output_folder.replace('/', '\\') - - # re-create modules list based on user selection - # search_list = { 'lastBuild' : tosearch['lastBuild'] } # hardcode lastBuild as first item - #search_list = [loader['airdropEmails']] # hardcode as first item - search_list =[] - - s_items = 0 - for x in range(MODULE_START_INDEX, module_end_index): - if window[x].Get(): - key = window[x].metadata - if key in loader: - search_list.append(loader[key]) - s_items = s_items + 1 # for progress bar - - # no more selections allowed - window[x].Update(disabled=True) - - window['SELECT ALL'].update(disabled=True) - window['DESELECT ALL'].update(disabled=True) - - GuiWindow.window_handle = window - out_params = OutputParameters(output_folder) - wrap_text = True - time_offset = values['timezone'] - if time_offset == '': - time_offset = 'UTC' - - crunch_successful = vleapp.crunch_artifacts( - search_list, extracttype, input_path, out_params, len(loader)/s_items, wrap_text, loader, casedata, time_offset, profile_filename) - if crunch_successful: - report_path = os.path.join(out_params.report_folder_base, 'index.html') - - if report_path.startswith('\\\\?\\'): # windows - report_path = report_path[4:] - if report_path.startswith('\\\\'): # UNC path - report_path = report_path[2:] - locationmessage = 'Report name: ' + report_path - sg.Popup('Processing completed', locationmessage) - webbrowser.open_new_tab('file://' + report_path) + case_data_load_error = 'File was not a valid case data file: invalid format' + if case_data_load_error: + tk_msgbox.showerror(title='Error', message=case_data_load_error, parent=case_window) else: - log_path = out_params.screen_output_file_path - if log_path.startswith('\\\\?\\'): # windows - log_path = log_path[4:] - sg.Popup('Processing failed :( ', f'See log for error details..\nLog file located at {log_path}', title="Error") - break -window.close() + tk_msgbox.showinfo( + title='Load Case Data', message=f'Loaded Case Data: {destination_path}', parent=case_window) + + + ### Case Data Window creation + case_window = tk.Toplevel(main_window) + case_window_width = 560 + if is_platform_linux(): + case_window_height = 290 + else: + case_window_height = 270 + + #### Places Case Data window in the center of the screen + screen_width = main_window.winfo_screenwidth() + screen_height = main_window.winfo_screenheight() + margin_width = (screen_width - case_window_width) // 2 + margin_height = (screen_height - case_window_height) // 2 + + #### Case Data window properties + case_window.geometry(f'{case_window_width}x{case_window_height}+{margin_width}+{margin_height}') + case_window.resizable(False, False) + case_window.configure(bg=theme_bgcolor) + case_window.title('Add Case Data') + case_window.grid_columnconfigure(0, weight=1) + + #### Layout + case_title_label = ttk.Label(case_window, text='Add Case Data', font=('Helvetica 18')) + case_title_label.grid(row=0, column=0, padx=14, pady=7, sticky='w') + case_number_frame = ttk.LabelFrame(case_window, text=' Case Number ') + case_number_frame.grid(row=1, column=0, padx=14, pady=5, sticky='we') + case_number_entry = ttk.Entry(case_number_frame, textvariable=casedata['Case Number']) + case_number_entry.pack(padx=5, pady=4, fill='x') + case_number_entry.focus() + case_agency_frame = ttk.LabelFrame(case_window, text=' Agency ') + case_agency_frame.grid(row=2, column=0, padx=14, pady=5, sticky='we') + case_agency_entry = ttk.Entry(case_agency_frame, textvariable=casedata['Agency']) + case_agency_entry.pack(padx=5, pady=4, fill='x') + case_examiner_frame = ttk.LabelFrame(case_window, text=' Examiner ') + case_examiner_frame.grid(row=3, column=0, padx=14, pady=5, sticky='we') + case_examiner_entry = ttk.Entry(case_examiner_frame, textvariable=casedata['Examiner']) + case_examiner_entry.pack(padx=5, pady=4, fill='x') + modules_btn_frame = ttk.Frame(case_window) + modules_btn_frame.grid(row=4, column=0, padx=14, pady=16, sticky='we') + modules_btn_frame.grid_columnconfigure(2, weight=1) + load_case_button = ttk.Button(modules_btn_frame, text='Load Case Data File', command=load_case) + load_case_button.grid(row=0, column=0, padx=5) + save_case_button = ttk.Button(modules_btn_frame, text='Save Case Data File', command=save_case) + save_case_button.grid(row=0, column=1, padx=5) + ttk.Separator(modules_btn_frame,orient='vertical').grid(row=0, column=2, padx=20, sticky='ns') + clear_case_button = ttk.Button(modules_btn_frame, text='Clear', command=clear) + clear_case_button.grid(row=0, column=3, padx=5) + close_case_button = ttk.Button(modules_btn_frame, text='Close', command=case_window.destroy) + close_case_button.grid(row=0, column=4, padx=5) + + case_window.grab_set() + + +## Main window creation +main_window = tk.Tk() +window_width = 902 +window_height = 625 + +## Variables +icon = os.path.join(os.path.dirname(__file__), 'scripts', 'icon.png') +loader: typing.Optional[plugin_loader.PluginLoader] = None +mlist = {} +profile_filename = None +tzvalues = ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Juba', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Timbuktu', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek', 'America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Atka', 'America/Bahia', 'America/Bahia_Banderas', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Buenos_Aires', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Catamarca', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Ciudad_Juarez', 'America/Coral_Harbour', 'America/Cordoba', 'America/Costa_Rica', 'America/Creston', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Ensenada', 'America/Fort_Nelson', 'America/Fort_Wayne', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Jujuy', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/Kralendijk', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Louisville', 'America/Lower_Princes', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Mendoza', 'America/Menominee', 'America/Merida', 'America/Metlakatla', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Nuuk', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Punta_Arenas', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Rosario', 'America/Santa_Isabel', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Shiprock', 'America/Sitka', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Virgin', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'America/Yellowknife', 'Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok', 'Arctic/Longyearbyen', 'Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Ashkhabad', 'Asia/Atyrau', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Barnaul', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Calcutta', 'Asia/Chita', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Chungking', 'Asia/Colombo', 'Asia/Dacca', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Famagusta', 'Asia/Gaza', 'Asia/Harbin', 'Asia/Hebron', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Istanbul', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kashgar', 'Asia/Kathmandu', 'Asia/Katmandu', 'Asia/Khandyga', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macao', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qostanay', 'Asia/Qyzylorda', 'Asia/Rangoon', 'Asia/Riyadh', 'Asia/Saigon', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Srednekolymsk', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Tel_Aviv', 'Asia/Thimbu', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Tomsk', 'Asia/Ujung_Pandang', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator', 'Asia/Urumqi', 'Asia/Ust-Nera', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yangon', 'Asia/Yekaterinburg', 'Asia/Yerevan', 'Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faeroe', 'Atlantic/Faroe', 'Atlantic/Jan_Mayen', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley', 'Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/NSW', 'Australia/North', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna', 'Brazil/Acre', 'Brazil/DeNoronha', 'Brazil/East', 'Brazil/West', 'CET', 'CST6CDT', 'Canada/Atlantic', 'Canada/Central', 'Canada/Eastern', 'Canada/Mountain', 'Canada/Newfoundland', 'Canada/Pacific', 'Canada/Saskatchewan', 'Canada/Yukon', 'Chile/Continental', 'Chile/EasterIsland', 'Cuba', 'EET', 'EST', 'EST5EDT', 'Egypt', 'Eire', 'Etc/GMT', 'Etc/GMT+0', 'Etc/GMT+1', 'Etc/GMT+10', 'Etc/GMT+11', 'Etc/GMT+12', 'Etc/GMT+2', 'Etc/GMT+3', 'Etc/GMT+4', 'Etc/GMT+5', 'Etc/GMT+6', 'Etc/GMT+7', 'Etc/GMT+8', 'Etc/GMT+9', 'Etc/GMT-0', 'Etc/GMT-1', 'Etc/GMT-10', 'Etc/GMT-11', 'Etc/GMT-12', 'Etc/GMT-13', 'Etc/GMT-14', 'Etc/GMT-2', 'Etc/GMT-3', 'Etc/GMT-4', 'Etc/GMT-5', 'Etc/GMT-6', 'Etc/GMT-7', 'Etc/GMT-8', 'Etc/GMT-9', 'Etc/GMT0', 'Etc/Greenwich', 'Etc/UCT', 'Etc/UTC', 'Etc/Universal', 'Etc/Zulu', 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Astrakhan', 'Europe/Athens', 'Europe/Belfast', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Busingen', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Kirov', 'Europe/Kyiv', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Nicosia', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Saratov', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Tiraspol', 'Europe/Ulyanovsk', 'Europe/Uzhgorod', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zaporozhye', 'Europe/Zurich', 'GB', 'GB-Eire', 'GMT', 'GMT+0', 'GMT-0', 'GMT0', 'Greenwich', 'HST', 'Hongkong', 'Iceland', 'Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion', 'Iran', 'Israel', 'Jamaica', 'Japan', 'Kwajalein', 'Libya', 'MET', 'MST', 'MST7MDT', 'Mexico/BajaNorte', 'Mexico/BajaSur', 'Mexico/General', 'NZ', 'NZ-CHAT', 'Navajo', 'PRC', 'PST8PDT', 'Pacific/Apia', 'Pacific/Auckland', 'Pacific/Bougainville', 'Pacific/Chatham', 'Pacific/Chuuk', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Johnston', 'Pacific/Kanton', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Pohnpei', 'Pacific/Ponape', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Samoa', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Truk', 'Pacific/Wake', 'Pacific/Wallis', 'Pacific/Yap', 'Poland', 'Portugal', 'ROC', 'ROK', 'Singapore', 'Turkey', 'UCT', 'US/Alaska', 'US/Aleutian', 'US/Arizona', 'US/Central', 'US/East-Indiana', 'US/Eastern', 'US/Hawaii', 'US/Indiana-Starke', 'US/Michigan', 'US/Mountain', 'US/Pacific', 'US/Samoa', 'UTC', 'Universal', 'W-SU', 'WET', 'Zulu'] +casedata = {'Case Number': tk.StringVar(), + 'Agency': tk.StringVar(), + 'Examiner': tk.StringVar(), + } +timezone_set = tk.StringVar() +pickModules() + +## Theme properties +theme_bgcolor = '#070739' +theme_inputcolor = '#c327ab' +theme_fgcolor = '#e1e099' +theme_button = '#521477' + +if is_platform_macos(): + mlist_window_height = 24 + log_text_height = 37 +elif is_platform_linux(): + mlist_window_height = 16 + log_text_height = 27 +else: + mlist_window_height = 19 + log_text_height = 29 + +## Places main window in the center +screen_width = main_window.winfo_screenwidth() +screen_height = main_window.winfo_screenheight() +margin_width = (screen_width - window_width) // 2 +margin_height = (screen_height - window_height) // 2 + +## Main window properties +main_window.geometry(f'{window_width}x{window_height}+{margin_width}+{margin_height}') +main_window.title(f'VLEAPP version {vleapp_version}') +main_window.resizable(False, False) +main_window.configure(bg=theme_bgcolor) +logo_icon = tk.PhotoImage(file=icon) +main_window.iconphoto(True, logo_icon) +main_window.grid_columnconfigure(0, weight=1) + +## Widgets default style +style = ttk.Style() +style.theme_use('default') +style.configure('.', + background=theme_bgcolor, + foreground=theme_fgcolor) +style.configure('TButton') +style.map('TButton', + background=[('active', theme_fgcolor), ('!disabled', theme_button)], + foreground=[('active', theme_button), ('!disabled', theme_fgcolor)]) +style.configure('TEntry', fieldbackground=theme_inputcolor, highlightthickness=0) +style.configure( + 'TCombobox', selectforeground=theme_fgcolor, + selectbackground=theme_button, arrowcolor=theme_fgcolor) +style.map('TCombobox', + fieldbackground=[('active', theme_inputcolor), ('readonly', theme_inputcolor)], + ) +style.configure('TScrollbar', background=theme_button, arrowcolor='black', troughcolor=theme_inputcolor) +style.configure('TProgressbar', thickness=4, background='DarkGreen') + +## Main Window Layout +### Top part of the window +title_frame = ttk.Frame(main_window) +title_frame.grid(padx=14, pady=6, sticky='w') +title_label = ttk.Label( + title_frame, + text='Vehicle Logs, Events, And Protobuf Parser', + font=('Helvetica 22')) +title_label.pack(pady=4) +github_label = ttk.Label( + title_frame, + text='https://github.com/abrignoni/VLEAPP', + font=('Helvetica 14')) +github_label.pack(anchor='w') + +### Input output selection +input_frame = ttk.LabelFrame( + main_window, + text=' Select the file (tar/zip/gz) or directory containing the data to be parsed: ') +input_frame.grid(padx=14, pady=2, sticky='we') +input_frame.grid_columnconfigure(0, weight=1) +input_entry = ttk.Entry(input_frame) +input_entry.grid(row=0, column=0, padx=5, pady=4, sticky='we') +input_file_button = ttk.Button(input_frame, text='Browse File', command=lambda: select_input('file')) +input_file_button.grid(row=0, column=1, padx=5, pady=4) +input_folder_button = ttk.Button(input_frame, text='Browse Folder', command=lambda: select_input('folder')) +input_folder_button.grid(row=0, column=2, padx=5, pady=4) + +output_frame = ttk.LabelFrame(main_window, text=' Select Output Folder: ') +output_frame.grid(padx=14, pady=5, sticky='we') +output_frame.grid_columnconfigure(0, weight=1) +output_entry = ttk.Entry(output_frame) +output_entry.grid(row=0, column=0, padx=5, pady=4, sticky='we') +output_folder_button = ttk.Button(output_frame, text='Browse Folder', command=select_output) +output_folder_button.grid(row=0, column=1, padx=5, pady=4) + +### Modules +modules_frame = ttk.Frame(main_window, name='f_modules') +modules_frame.grid(padx=14, pady=4, sticky='we') +modules_frame.grid_columnconfigure(0, weight=1) + +#### Buttons & Timezone +button_frame = ttk.Frame(modules_frame) +button_frame.grid(row=0, column=0, pady=4, sticky='we') + +all_button = ttk.Button(button_frame, text='Select All', command=select_all) +all_button.grid(row=0, column=0, padx=5) +none_button = ttk.Button(button_frame, text='Deselect All', command=deselect_all) +none_button.grid(row=0, column=1, padx=5) +load_button = ttk.Button(button_frame, text='Load Profile', command=load_profile) +load_button.grid(row=0, column=2, padx=5) +save_button = ttk.Button(button_frame, text='Save Profile', command=save_profile) +save_button.grid(row=0, column=3, padx=5) +ttk.Separator(button_frame, orient='vertical').grid(row=0, column=4, padx=10, sticky='ns') +case_data_button = ttk.Button(button_frame, text='Case Data', command=case_data) +case_data_button.grid(row=0, column=5, padx=5) +ttk.Separator(button_frame, orient='vertical').grid(row=0, column=6, padx=10, sticky='ns') +if is_platform_macos(): + ttk.Label( + button_frame, text='Timezone Offset\n(Not Implemented): ' + ).grid(row=0, column=7) +else: + ttk.Label( + button_frame, text='Timezone Offset (Not Implemented): ' + ).grid(row=0, column=7) +timezone_offset = ttk.Combobox( + button_frame, textvariable=timezone_set, values=tzvalues, height=20, state='readonly') +timezone_offset.master.option_add( '*TCombobox*Listbox.background', theme_inputcolor) +timezone_offset.master.option_add( '*TCombobox*Listbox.foreground', theme_fgcolor) +timezone_offset.master.option_add( '*TCombobox*Listbox.selectBackground', theme_button) +timezone_offset.grid(row=0, column=8) + +#### List of modules +mlist_frame = ttk.LabelFrame(modules_frame, text=' Available Modules: ', name='f_list') +mlist_frame.grid(row=1, column=0, padx=4, pady=4, sticky='we') +mlist_frame.grid_columnconfigure(0, weight=1) +v = ttk.Scrollbar(mlist_frame, orient='vertical') +v.grid(row=0, column=1, sticky='ns') +mlist_text = tk.Text(mlist_frame, name='tbox', bg=theme_bgcolor, highlightthickness=0, + yscrollcommand=v.set, height=mlist_window_height) +mlist_text.grid(row=0, column=0, sticky='we') +v.config(command=mlist_text.yview) +for plugin, enabled in mlist.items(): + cb = tk.Checkbutton(mlist_text, name=f'mcb_{plugin.name}', + text=f'{plugin.category} [{plugin.name} - {plugin.module_name}.py]', + variable=enabled, onvalue=True, offvalue=False, command=get_selected_modules) + cb.config(background=theme_bgcolor, fg=theme_fgcolor, selectcolor=theme_inputcolor, + highlightthickness=0, activebackground=theme_bgcolor, activeforeground=theme_fgcolor) + mlist_text.window_create('insert', window=cb) + mlist_text.insert('end', '\n') +mlist_text.config(state='disabled') +main_window.bind_class('Checkbutton', '', scroll) +main_window.bind_class('Checkbutton', '', scroll) +main_window.bind_class('Checkbutton', '', scroll) + +### Process / Close +bottom_frame = ttk.Frame(main_window) +bottom_frame.grid(padx=16, pady=6, sticky='we') +bottom_frame.grid_columnconfigure(2, weight=1) +process_button = ttk.Button(bottom_frame, text='Process', command=lambda: process(casedata)) +process_button.grid(row=0, column=0, padx=5) +close_button = ttk.Button(bottom_frame, text='Close', command=main_window.quit) +close_button.grid(row=0, column=1, padx=5) +selected_modules_label = ttk.Label(bottom_frame, text='Number of selected modules: ') +selected_modules_label.grid(row=0, column=2, padx=5, sticky='e') +get_selected_modules() + +#### Logs +logtext_frame = ttk.Frame(main_window, name='logs_frame') +logtext_frame.grid_columnconfigure(0, weight=1) +vlog = ttk.Scrollbar(logtext_frame, orient='vertical') +vlog.grid(row=0, column=1, pady=10, sticky='ns') +log_text = tk.Text( + logtext_frame, name='log_text', bg=theme_inputcolor, fg=theme_fgcolor, + highlightthickness=1, yscrollcommand=vlog.set, height=log_text_height) +log_text.grid(row=0, column=0, padx=4, pady=10, sticky='we') +vlog.config(command=log_text.yview) + +### Progress bar +progress_bar = ttk.Progressbar(main_window, orient='horizontal') + +main_window.mainloop() diff --git a/vleappGUI.spec b/vleappGUI.spec index 7c0d2c1..9995ee9 100755 --- a/vleappGUI.spec +++ b/vleappGUI.spec @@ -5,13 +5,7 @@ block_cipher = None a = Analysis(['vleappGUI.py'], pathex=['.\\scripts\\artifacts'], binaries=[], - datas=[('.\\scripts\\logo.jpg', '.\\scripts'), - ('.\\scripts\\dashboard.css', '.\\scripts'), - ('.\\scripts\\dark-mode.css', '.\\scripts'), - ('.\\scripts\\dark-mode-switch.js', '.\\scripts'), - ('.\\scripts\\feather.min.js', '.\\scripts'), - ('.\\scripts\\MDB-Free_4.13.0', '.\\scripts\\MDB-Free_4.13.0'), - ('.\\scripts\\artifacts', '\\scripts\\artifacts')], + datas=[('.\\scripts', '.\\scripts')], hiddenimports=[], hookspath=['.\\'], runtime_hooks=[], @@ -33,5 +27,8 @@ exe = EXE(pyz, bootloader_ignore_signals=False, strip=False, upx=True, + console=True, + hide_console='hide-early', + disable_windowed_traceback=False, upx_exclude=[], - runtime_tmpdir=None ) + runtime_tmpdir=None ) \ No newline at end of file diff --git a/vleappGUI_macOS.spec b/vleappGUI_macOS.spec new file mode 100755 index 0000000..b79584b --- /dev/null +++ b/vleappGUI_macOS.spec @@ -0,0 +1,54 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['vleappGUI.py'], + pathex=['scripts/artifacts'], + binaries=[], + datas=[('scripts/', 'scripts')], + hiddenimports=[ + 'blackboxprotobuf', + 'xml.etree.ElementTree', + 'xmltodict', + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='vleappGUI', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='vleappGUI', +) +app = BUNDLE( + coll, + name='vleappGUI.app', + icon='../icon.icns', + bundle_identifier='4n6.brigs.VLEAPP', + version='2.1.0', +) diff --git a/vleapp_macOS.spec b/vleapp_macOS.spec new file mode 100644 index 0000000..c34880c --- /dev/null +++ b/vleapp_macOS.spec @@ -0,0 +1,40 @@ +# -*- mode: python ; coding: utf-8 -*- + +a = Analysis( + ['vleapp.py'], + pathex=['scripts/artifacts'], + binaries=[], + datas=[('scripts/', 'scripts')], + hiddenimports=[ + 'blackboxprotobuf', + 'xml.etree.ElementTree', + 'xmltodict', + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='vleapp', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +)