diff --git a/requirements.txt b/requirements.txt index 019c477..af8a305 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ appdirs==1.4.4 send2trash==1.8.2 zstandard==0.21.0 ordered-set==4.1.0 +psutil=5.9.6 diff --git a/src/core.py b/src/core.py index 35cc85d..6146139 100644 --- a/src/core.py +++ b/src/core.py @@ -1,8 +1,36 @@ +#!/usr/bin/python3 + +#################################################################################### +# +# Copyright (c) 2023 Piotr Jochymek +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +#################################################################################### + from os import scandir from os import stat from os import sep from os import getpgid -from os import killpg + from os.path import join as path_join from os.path import abspath from os.path import normpath @@ -13,6 +41,8 @@ from re import search from sys import getsizeof +from hashlib import sha1 + from collections import defaultdict import re @@ -29,7 +59,10 @@ import difflib -from subprocess import STDOUT, check_output, Popen, TimeoutExpired, PIPE +from executor import Executor + +from subprocess import STDOUT, TimeoutExpired, PIPE, check_output +#, Popen import pathlib @@ -102,7 +135,7 @@ def test_regexp(expr): #print(i, temp_tuple) ####################################################################### -data_format_version='1.0001' +data_format_version='1.0002' class LibrerCoreData : label = "" @@ -151,12 +184,57 @@ def __init__(self,label,path,log): self.abort_action = False self.files_search_progress = 0 + self.crc_progress_info=0 + def file_name(self): return f'{self.db.rid}.dat' def abort(self): self.abort_action = True + CRC_BUFFER_SIZE=4*1024*1024 + def calc_crc(self,fullpath,size): + buf = bytearray(self.CRC_BUFFER_SIZE) + view = memoryview(buf) + + self.crc_progress_info=0 + + try: + file_handle=open(fullpath,'rb') + file_handle_readinto=file_handle.readinto + except Exception as e: + self.log.error(e) + return None + else: + hasher = sha1() + hasher_update=hasher.update + + #faster for smaller files + if size128: + result = gzip.compress(bytes(output,"ISO-8859-1")) #"utf-8" + #result = gzip.compress(output) #"utf-8" + is_compressed = True + else: + result = output + #.decode("ISO-8859-1") + is_compressed = False + new_list_ref_elem = [cd_ok,is_compressed,result] - cd_ok = False + else: is_compressed = False - - e_str = str(et) - e_size = getsizeof(e_str) - list_ref.append( (cd_ok,is_compressed,e_str) ) + new_list_ref_elem = [cd_ok,is_compressed,output] self_db.files_cde_errors_quant +=1 - self_db.files_cde_size += e_size - except Exception as e: - print('error on ',cde_run_list) - self.log.error('Custom Data Extraction subprocess error:%s\n%s',cde_run_list,e ) + if crc: + new_list_ref_elem.append(crc_val) - cd_ok = False - is_compressed = False + list_ref.append( tuple(new_list_ref_elem) ) - e_str = str(e) - e_size = getsizeof(e_str) - list_ref.append( (cd_ok,is_compressed,e_str) ) - print(e_str) + self_db.files_cde_quant += 1 + self_db.files_cde_size += size + self_db.files_cde_size_extracted += getsizeof(output) - self_db.files_cde_errors_quant +=1 + #try: + #shell = False + #output = check_output(cde_run_list, stderr=STDOUT, timeout=timeout,shell=shell,start_new_session=True) + #encoding="ISO-8859-1" + #text=True, - self_db.files_cde_size += e_size - else: - output, error = p.communicate() + #process = Popen(cde_run_list, start_new_session=True, stdout=PIPE, stderr=STDOUT) + + #if timeout: + # process.wait(timeout=timeout) + #else: + # process.wait() + + #except TimeoutExpired as et: + #print('timeout on ',cde_run_list) + + #try: + # process.terminate() + #except Exception as term_e: + # self.log.error('Custom Data Extraction subprocess timeout termination:%s\n%s',cde_run_list,term_e ) + # e_str = str(et) + '\n' + str(term_e) + #else: + + #e_str = str(et) + + #killpg(getpgid(process.pid), SIGTERM) + #self.log.error('Custom Data Extraction subprocess timeout:%s\n%s',cde_run_list,et ) + + #cd_ok = False + #is_compressed = False + + #e_size = getsizeof(e_str) + #new_list_ref_elem = [cd_ok,is_compressed,e_str] + #list_ref.append( (cd_ok,is_compressed,e_str) ) + #self_db.files_cde_errors_quant +=1 + #self_db.files_cde_size += e_size + + #except Exception as e: + #print('error on ',cde_run_list) + # self.log.error('Custom Data Extraction subprocess error:%s\n%s',cde_run_list,e ) + + # cd_ok = False + # is_compressed = False + + # e_str = str(e) + # e_size = getsizeof(e_str) + + # new_list_ref_elem = [cd_ok,is_compressed,e_str] + #list_ref.append( (cd_ok,is_compressed,e_str) ) + #print(e_str) + + # self_db.files_cde_errors_quant +=1 + + # self_db.files_cde_size += e_size + #else: + #returncode = process.returncode + #print('returncode:',returncode) + + #output, error = process.communicate() #print(output,type(output)) - cd_ok = True + #cd_ok = True - output_len = len(output) + #output_len = len(output) - if output_len==0: - result = None - is_compressed = False - elif output_len>128: + #if output_len==0: + # result = None + # is_compressed = False + #elif output_len>128: #result = gzip.compress(bytes(output,"ISO-8859-1")) #"utf-8" - result = gzip.compress(output) #"utf-8" - is_compressed = True - else: - result = output - is_compressed = False + # result = gzip.compress(output) #"utf-8" + # is_compressed = True + #else: + # result = output.decode("ISO-8859-1") + # is_compressed = False + + #new_list_ref_elem = [cd_ok,is_compressed,result] - list_ref.append( (cd_ok,is_compressed,result) ) + #if crc: + # new_list_ref_elem.append(crc_val) + + #list_ref.append( tuple(new_list_ref_elem) ) + + #self_db.files_cde_quant += 1 + #self_db.files_cde_size += size + #self_db.files_cde_size_extracted += getsizeof(output) - self_db.files_cde_quant += 1 - self_db.files_cde_size += size - self_db.files_cde_size_extracted += getsizeof(output) self.info_line_current = '' del self.custom_data_pool + exe.end() + self.set_data() self.scan_data={} @@ -653,6 +805,11 @@ def load(self,db_dir,file_name): with lzma.open(full_file_path, "rb") as gzip_file: self.db = pickle.load(gzip_file) + global data_format_version + if self.db.data_format_version != data_format_version: + self.log.error(f'incompatible data format version error: {self.db.data_format_version} vs {data_format_version}') + return True + except Exception as e: print('loading error:%s' % e ) return True diff --git a/src/executor.py b/src/executor.py new file mode 100644 index 0000000..58f333f --- /dev/null +++ b/src/executor.py @@ -0,0 +1,119 @@ +#!/usr/bin/python3 + +#################################################################################### +# +# Copyright (c) 2023 Piotr Jochymek +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +#################################################################################### + +import psutil + +from subprocess import Popen, STDOUT, TimeoutExpired, PIPE, check_output, CalledProcessError +from threading import Thread +from time import time +from time import sleep + +from signal import SIGSTOP,SIGINT,SIGTERM +class Executor : + def __init__(self): + self.command_list_to_execute = None + self.output='' + self.keep_running = True + + self.run_thread=Thread(target=self.run_in_thread,daemon=True) + self.run_thread.start() + + def run(self,command_list_to_execute,timeout=None): + #print('run',command_list_to_execute,timeout) + + self.output = '' + self.pid = None + + self.running = True + self.res_ok = True + self.killed = False + start = time() + + self.command_list_to_execute = command_list_to_execute + error_message = '' + + while self.running: + if timeout: + if time()-start>timeout: + self.kill() + if not self.killed: + error_message += '\nKilled after timeout.' + self.killed = True + + sleep(0.001) + + return self.res_ok and not self.killed,(self.output if self.output else '') + error_message + + def kill(self): + pid = self.pid + proc = psutil.Process(pid) + + #proc.send_signal(SIGSTOP) + #proc.send_signal(SIGINT) + + for child in proc.children(): + self.kill(child.pid) + + try: + proc.send_signal(SIGTERM) + print('SIGTERM send to',pid) + + except Exception as e: + print(e) + + def run_in_thread(self): + while self.keep_running: + if self.command_list_to_execute: + self.output = '' + output_list = [] + + try: + self.process = Popen(self.command_list_to_execute, start_new_session=True, stdout=PIPE, stderr=STDOUT) + self.pid = self.process.pid + + while True: + output=self.process.stdout.readline().decode("ISO-8859-1") + output_list.append(output) + if not output and self.process.poll() is not None: + break + + except Exception as e: + self.res_ok = False + output_list.append(str(e)) + + self.output = ''.join(output_list) + self.command_list_to_execute=None + self.running = False + else: + sleep(0.001) + + def end(self): + self.keep_running=False + self.run_thread.join() + + diff --git a/src/librer.py b/src/librer.py index 7213bf0..a5be007 100644 --- a/src/librer.py +++ b/src/librer.py @@ -334,6 +334,14 @@ def restore_status_line_wrapp(self,*args,**kwargs): return res return restore_status_line_wrapp + def widget_tooltip_cget(self,widget,tooltip): + widget.bind("", lambda event : self.motion_on_widget_cget(event,tooltip)) + widget.bind("", lambda event : self.widget_leave()) + + def widget_tooltip(self,widget,tooltip): + widget.bind("", lambda event : self.motion_on_widget(event,tooltip)) + widget.bind("", lambda event : self.widget_leave()) + ####################################################################### action_abort=False @@ -518,22 +526,25 @@ def __init__(self,cwd): self.status_record.pack(fill='x',expand=1,side='left') self.status_record_configure = lambda x : self.status_record.configure(image = self.ico_record, text = x,compound='left') - self.status_record.bind("", lambda event : self.motion_on_widget_cget(event,'Selected record (user label)')) - self.status_record.bind("", lambda event : self.widget_leave()) + self.widget_tooltip_cget(self.status_record,'Selected record (user label):') + #self.status_record.bind("", lambda event : self.motion_on_widget_cget(event,'Selected record (user label)')) + #self.status_record.bind("", lambda event : self.widget_leave()) self.status_record_path=Label(status_frame,text='--',width=20,borderwidth=2,bg=self.bg_color,relief='groove',anchor='w') self.status_record_path.pack(fill='x',expand=1,side='left') self.status_record_path_configure = lambda x : self.status_record_path.configure(text = x,compound='left') - self.status_record_path.bind("", lambda event : self.motion_on_widget_cget(event,'Scanpath of selected record: ')) - self.status_record_path.bind("", lambda event : self.widget_leave()) + self.widget_tooltip_cget(self.status_record_path,'Scanpath of selected record') + #self.status_record_path.bind("", lambda event : self.motion_on_widget_cget(event,'Scanpath of selected record: ')) + #self.status_record_path.bind("", lambda event : self.widget_leave()) self.status_record_subpath=Label(status_frame,text='--',width=60,borderwidth=2,bg=self.bg_color,relief='groove',anchor='w') self.status_record_subpath.pack(fill='x',expand=1,side='left') self.status_record_subpath_configure = lambda x : self.status_record_subpath.configure(text = x,compound='left') - self.status_record_subpath.bind("", lambda event : self.motion_on_widget_cget(event,'subpath of selected item: ')) - self.status_record_subpath.bind("", lambda event : self.widget_leave()) + self.widget_tooltip_cget(self.status_record_subpath,'subpath of selected item') + #self.status_record_subpath.bind("", lambda event : self.motion_on_widget_cget(event,'subpath of selected item: ')) + #self.status_record_subpath.bind("", lambda event : self.widget_leave()) self.status_info = Label(status_frame,text='Initializing...',relief='sunken',borderwidth=1,bg=self.bg_color,anchor='w') @@ -701,8 +712,9 @@ def post_close(on_main_window_dialog=True): self.add_path_button = Button(temp_frame,width=18,image = self_ico['open'], command=self.set_path_to_scan,underline=0) self.add_path_button.grid(row=0, column=4, sticky='news',padx=4,pady=4) - self.add_path_button.bind("", lambda event : self.motion_on_widget(event,"Set path to scan.")) - self.add_path_button.bind("", lambda event : self.widget_leave()) + self.widget_tooltip(self.add_path_button,"Set path to scan.") + #self.add_path_button.bind("", lambda event : self.motion_on_widget(event,"Set path to scan.")) + #self.add_path_button.bind("", lambda event : self.widget_leave()) temp_frame.grid_columnconfigure(3, weight=1) @@ -711,8 +723,9 @@ def post_close(on_main_window_dialog=True): single_device_button.grid(row=1, column=0, sticky='news',padx=4,pady=4,columnspan=4) self.single_device.set(self.cfg_get_bool(CFG_KEY_SINGLE_DEVICE)) - single_device_button.bind("", lambda event : self.motion_on_widget(event,"Don't cross device boundaries (mount points, bindings etc.)")) - single_device_button.bind("", lambda event : self.widget_leave()) + self.widget_tooltip(single_device_button,"Don't cross device boundaries (mount points, bindings etc.)") + #single_device_button.bind("", lambda event : self.motion_on_widget(event,"Don't cross device boundaries (mount points, bindings etc.)")) + #single_device_button.bind("", lambda event : self.widget_leave()) ############## self.exclude_regexp_scan=BooleanVar() @@ -729,14 +742,18 @@ def post_close(on_main_window_dialog=True): self.add_exclude_button_dir = Button(buttons_fr2,width=18,image = self_ico['open'],command=self.exclude_mask_add_dir) self.add_exclude_button_dir.pack(side='left',pady=4,padx=4) - self.add_exclude_button_dir.bind("", lambda event : self.motion_on_widget(event,"Add path as exclude expression ...")) - self.add_exclude_button_dir.bind("", lambda event : self.widget_leave()) + + self.widget_tooltip(self.add_exclude_button_dir,"Add path as exclude expression ...") + #self.add_exclude_button_dir.bind("", lambda event : self.motion_on_widget(event,"Add path as exclude expression ...")) + #self.add_exclude_button_dir.bind("", lambda event : self.widget_leave()) self.add_exclude_button = Button(buttons_fr2,width=18,image= self_ico['expression'],command=self.exclude_mask_add_dialog,underline=4) tooltip_string = 'Add expression ...\nduring the scan, the entire path is checked \nagainst the specified expression,\ne.g.' + ('*windows* etc. (without regular expression)\nor .*windows.*, etc. (with regular expression)' if windows else '*.git* etc. (without regular expression)\nor .*\\.git.* etc. (with regular expression)') - self.add_exclude_button.bind("", lambda event : self.motion_on_widget(event,tooltip_string)) - self.add_exclude_button.bind("", lambda event : self.widget_leave()) + + self.widget_tooltip(self.add_exclude_button,tooltip_string) + #self.add_exclude_button.bind("", lambda event : self.motion_on_widget(event,tooltip_string)) + #self.add_exclude_button.bind("", lambda event : self.widget_leave()) self.add_exclude_button.pack(side='left',pady=4,padx=4) @@ -749,8 +766,9 @@ def post_close(on_main_window_dialog=True): skip_button = Checkbutton(self.scan_dialog.area_main,text='log skipped files',variable=self.log_skipped_var) skip_button.grid(row=4,column=0,sticky='news',padx=8,pady=3,columnspan=3) - skip_button.bind("", lambda event : self.motion_on_widget(event,"log every skipped file (softlinks, hardlinks, excluded, no permissions etc.)")) - skip_button.bind("", lambda event : self.widget_leave()) + self.widget_tooltip(skip_button,"log every skipped file (softlinks, hardlinks, excluded, no permissions etc.)") + #skip_button.bind("", lambda event : self.motion_on_widget(event,"log every skipped file (softlinks, hardlinks, excluded, no permissions etc.)")) + #skip_button.bind("", lambda event : self.widget_leave()) self.scan_button = Button(self.scan_dialog.area_buttons,width=12,text="Scan",image=self_ico['scan'],compound='left',command=self.scan_wrapper,underline=0) self.scan_button.pack(side='right',padx=4,pady=4) @@ -768,14 +786,32 @@ def post_close(on_main_window_dialog=True): sf_par3.pack(fill='both',expand=True,side='top') self.cde_frame = cde_frame = sf_par3.frame() - Label(cde_frame,text='Use',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=0,sticky='news') - Label(cde_frame,text='File Mask',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=1,sticky='news') - Label(cde_frame,text='Min Size',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=2,sticky='news') - Label(cde_frame,text='Max Size',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=3,sticky='news') - Label(cde_frame,text='Executable',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=4,sticky='news') - Label(cde_frame,text='',bg=self.bg_color,anchor='w').grid(row=0, column=5,sticky='news') - Label(cde_frame,text='Timeout',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=6,sticky='news') - #Label(cde_frame,text='Delete',bg=self.bg_color,anchor='w',relief='groove',bd=2).grid(row=0, column=6,sticky='news') + (lab_use := Label(cde_frame,text='Use',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=0,sticky='news') + (lab_mask := Label(cde_frame,text='File Mask',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=1,sticky='news') + (lab_min := Label(cde_frame,text='Min Size',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=2,sticky='news') + (lab_max := Label(cde_frame,text='Max Size',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=3,sticky='news') + (lab_exec := Label(cde_frame,text='Executable',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=4,sticky='news') + (lab_open := Label(cde_frame,text='',bg=self.bg_color,anchor='w')).grid(row=0, column=5,sticky='news') + (lab_timeout := Label(cde_frame,text='Timeout',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=6,sticky='news') + (lab_crc := Label(cde_frame,text='CRC',bg=self.bg_color,anchor='w',relief='groove',bd=2)).grid(row=0, column=7,sticky='news') + + use_tooltip = "Mark to use CD Extractor" + mask_tooltip = "glob expresions separated by comma ','\ne.g. '*.7z, *.zip, *.gz'" + min_tooltip = "Minimum size of file to aplly CD expresion or crc\nmay be empty e.g. 0" + max_tooltip = "Maximum size of file aplly CD expresion or crc\nmay be empty e.g. '100MB'" + exec_tooltip = "executable or batch script that will be run\nwith file for extraction\nmay have parameters\nWill be executed with scanned file full path\ne.g. '7z l', 'cat', '~/my_extraction.sh', 'c:\\my_extraction.bat'" + open_tooltip = "Open dialog for executable" + timeout_tooltip = "Time limit in seconds for single CD extraction.\nAfter timeout executed process will be terminated" + crc_tooltip = "Calculate CRC (SHA1) for ALL\nfiles matching glob and size cryteria\nIt may take a long time." + + self.widget_tooltip(lab_use,use_tooltip) + self.widget_tooltip(lab_mask,mask_tooltip) + self.widget_tooltip(lab_min,min_tooltip) + self.widget_tooltip(lab_max,max_tooltip) + self.widget_tooltip(lab_exec,exec_tooltip) + self.widget_tooltip(lab_open,open_tooltip) + self.widget_tooltip(lab_timeout,timeout_tooltip) + self.widget_tooltip(lab_crc,crc_tooltip) self.CDE_ENTRIES_MAX = 16 self.CDE_use_var_list = [] @@ -784,6 +820,7 @@ def post_close(on_main_window_dialog=True): self.CDE_size_max_var_list=[] self.CDE_executable_var_list=[] self.CDE_timeout_var_list=[] + self.CDE_crc_var_list=[] for e in range(self.CDE_ENTRIES_MAX): self.CDE_use_var_list.append(BooleanVar()) @@ -792,10 +829,11 @@ def post_close(on_main_window_dialog=True): self.CDE_size_max_var_list.append(StringVar()) self.CDE_executable_var_list.append(StringVar()) self.CDE_timeout_var_list.append(StringVar()) + self.CDE_crc_var_list.append(BooleanVar()) row = e+1 - use_button = Checkbutton(cde_frame,variable=self.CDE_use_var_list[e]) - use_button.grid(row=row,column=0,sticky='news') + use_checkbutton = Checkbutton(cde_frame,variable=self.CDE_use_var_list[e]) + use_checkbutton.grid(row=row,column=0,sticky='news') mask_entry = Entry(cde_frame,textvariable=self.CDE_mask_var_list[e]) mask_entry.grid(row=row, column=1,sticky='news') @@ -809,12 +847,24 @@ def post_close(on_main_window_dialog=True): executable_entry = Entry(cde_frame,textvariable=self.CDE_executable_var_list[e]) executable_entry.grid(row=row, column=4,sticky='news') - del_button = Button(cde_frame,image=self.ico_folder,command = lambda x=e : self.cde_entry_open(x) ) - del_button.grid(row=row,column=5,sticky='news') + open_button = Button(cde_frame,image=self.ico_folder,command = lambda x=e : self.cde_entry_open(x) ) + open_button.grid(row=row,column=5,sticky='news') timeout_entry = Entry(cde_frame,textvariable=self.CDE_timeout_var_list[e]) timeout_entry.grid(row=row, column=6,sticky='news') + crc_entry = Checkbutton(cde_frame,variable=self.CDE_crc_var_list[e]) + crc_entry.grid(row=row, column=7,sticky='news') + + self.widget_tooltip(use_checkbutton,use_tooltip) + self.widget_tooltip(mask_entry,mask_tooltip) + self.widget_tooltip(size_min_entry,min_tooltip) + self.widget_tooltip(size_max_entry,max_tooltip) + self.widget_tooltip(executable_entry,exec_tooltip) + self.widget_tooltip(open_button,open_tooltip) + self.widget_tooltip(timeout_entry,timeout_tooltip) + self.widget_tooltip(crc_entry,crc_tooltip) + #self.add_path_button = Button(cde_frame,width=18,image = self_ico['open'], command=self.custom_data_wrapper_dialog,underline=0) #self.add_path_button.grid(row=1, column=2, sticky='news',padx=4,pady=4) @@ -831,22 +881,25 @@ def post_close(on_main_window_dialog=True): self.progress_dialog_on_scan = dialogs.ProgressDialog(self.scan_dialog.widget,self_ico_librer,self.bg_color,pre_show=pre_show,post_close=post_close) self.progress_dialog_on_scan.command_on_close = self.progress_dialog_abort - self.progress_dialog_on_scan.abort_button.bind("", lambda event : self.widget_leave()) - self.progress_dialog_on_scan.abort_button.bind("", lambda event : self.motion_on_widget(event) ) + self.widget_tooltip(self.progress_dialog_on_scan.abort_button,'') + #self.progress_dialog_on_scan.abort_button.bind("", lambda event : self.widget_leave()) + #self.progress_dialog_on_scan.abort_button.bind("", lambda event : self.motion_on_widget(event) ) self.progress_dialog_on_load = dialogs.ProgressDialog(self_main,self_ico_librer,self.bg_color,pre_show=pre_show,post_close=post_close) self.progress_dialog_on_load.command_on_close = self.progress_dialog_load_abort - self.progress_dialog_on_load.abort_button.bind("", lambda event : self.widget_leave()) - self.progress_dialog_on_load.abort_button.bind("", lambda event : self.motion_on_widget(event) ) + self.widget_tooltip(self.progress_dialog_on_load.abort_button,'') + #self.progress_dialog_on_load.abort_button.bind("", lambda event : self.widget_leave()) + #self.progress_dialog_on_load.abort_button.bind("", lambda event : self.motion_on_widget(event) ) self.find_dialog=dialogs.GenericDialog(self_main,self_ico_librer,self.bg_color,'Search database',pre_show=pre_show,post_close=post_close) self.progress_dialog_on_find = dialogs.ProgressDialog(self.find_dialog.widget,self_ico_librer,self.bg_color,pre_show=pre_show,post_close=post_close) self.progress_dialog_on_find.command_on_close = self.progress_dialog_find_abort - self.progress_dialog_on_find.abort_button.bind("", lambda event : self.widget_leave()) - self.progress_dialog_on_find.abort_button.bind("", lambda event : self.motion_on_widget(event) ) + self.widget_tooltip(self.progress_dialog_on_find.abort_button,'') + #self.progress_dialog_on_find.abort_button.bind("", lambda event : self.widget_leave()) + #self.progress_dialog_on_find.abort_button.bind("", lambda event : self.motion_on_widget(event) ) self.mark_dialog_on_groups = dialogs.CheckboxEntryDialogQuestion(self_tree,self_ico_librer,self.bg_color,pre_show=pre_show,post_close=post_close) @@ -1148,7 +1201,6 @@ def help_cascade_post(): ####################################################################### self_tree.bind("", self.motion_on_tree) - self_tree.bind("", lambda event : self.widget_leave()) ####################################################################### @@ -2432,6 +2484,7 @@ def scan(self): smax = self.CDE_size_max_var_list[e].get() exe = self.CDE_executable_var_list[e].get() timeout = self.CDE_timeout_var_list[e].get() + crc = self.CDE_crc_var_list[e].get() smin_int = core_str_to_bytes(smin) smax_int = core_str_to_bytes(smax) @@ -2439,7 +2492,7 @@ def scan(self): try: timeout_int = int(timeout) except: - timeout_int = 0 + timeout_int = None line_list = [ '1' if self.CDE_use_var_list[e].get() else '0', @@ -2447,20 +2500,22 @@ def scan(self): smin, smax, exe, - timeout ] + timeout, + '1' if self.CDE_crc_var_list[e].get() else '0' ] cde_sklejka_list.append(':'.join(line_list)) if self.CDE_use_var_list[e].get(): any_cde_enabled=True cde_list.append( ( - mask.split(','), + [elem.strip() for elem in mask.split(',')], True if smin_int>=0 else False, smin_int, True if smax_int>=0 else False, smax_int, exe.split(), - timeout_int ) ) + timeout_int, + crc ) ) self.cfg.set(CFG_KEY_CDE_SETTINGS,'|'.join(cde_sklejka_list)) @@ -2631,13 +2686,14 @@ def scan_dialog_show(self,do_scan=False): e=0 for e_section in self.cfg.get(CFG_KEY_CDE_SETTINGS).split('|'): try: - v1,v2,v3,v4,v5,v6 = e_section.split(':') + v1,v2,v3,v4,v5,v6,v7 = e_section.split(':') self.CDE_use_var_list[e].set(True if v1=='1' else False) self.CDE_mask_var_list[e].set(v2) self.CDE_size_min_var_list[e].set(v3) self.CDE_size_max_var_list[e].set(v4) self.CDE_executable_var_list[e].set(v5) self.CDE_timeout_var_list[e].set(v6) + self.CDE_crc_var_list[e].set(True if v7=='1' else False) e+=1 except: print(e_section) @@ -2670,8 +2726,9 @@ def exclude_mask_update(self) : remove_expression_button=Button(frame,image=self.ico['delete'],command=lambda entrypar=entry: self.exclude_mask_remove(entrypar),width=3) remove_expression_button.pack(side='right',padx=2,pady=1,fill='y') - remove_expression_button.bind("", lambda event : self.motion_on_widget(event,'Remove expression from list.')) - remove_expression_button.bind("", lambda event : self.widget_leave()) + self.widget_tooltip(remove_expression_button,'Remove expression from list.') + #remove_expression_button.bind("", lambda event : self.motion_on_widget(event,'Remove expression from list.')) + #remove_expression_button.bind("", lambda event : self.widget_leave()) row+=1