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,
+)