641 lines
27 KiB
Python
641 lines
27 KiB
Python
import os, gc, json, time, stat, threading
|
||
import ctypes, ctypes.wintypes
|
||
|
||
from PYAS_Tools import MEMORY_BASIC_INFORMATION
|
||
|
||
####################################################################################################
|
||
|
||
class ScannerMixin:
|
||
def init_engine_thread(self):
|
||
try:
|
||
self.start_daemon_thread(self.backup_mbr)
|
||
self.start_daemon_thread(self.relock_file)
|
||
self.start_daemon_thread(self.init_whitelist)
|
||
self.start_daemon_thread(self.popup_intercept_thread)
|
||
|
||
def log_callback(x):
|
||
self.write_log("INFO", "Load Engine", source=os.path.basename(x))
|
||
|
||
self.heuristic.load_path(self.path_heuristic, callback=log_callback)
|
||
self.properties.load_path(self.path_properties, callback=log_callback)
|
||
self.write_log("INFO", "System", detail="Engine Initialization Complete")
|
||
|
||
if "-scan" in self.args_pyas:
|
||
try:
|
||
idx = self.args_pyas.index("-scan")
|
||
self.trigger_context_scan(self.args_pyas[idx+1])
|
||
except Exception:
|
||
pass
|
||
|
||
with self.lock_config:
|
||
first_launch = self.pyas_config.get("first_launch", True)
|
||
driver_enabled = self.pyas_config.get("driver_switch", False)
|
||
context_enabled = self.pyas_config.get("context_switch", True)
|
||
autostart_enabled = self.pyas_config.get("autostart_switch", True)
|
||
|
||
self.register_context_menu(context_enabled)
|
||
self.manage_autostart(autostart_enabled)
|
||
|
||
if not first_launch:
|
||
with self.lock_config:
|
||
if self.pyas_config.get("process_switch"):
|
||
self.start_daemon_thread(self.protect_proc_thread)
|
||
if self.pyas_config.get("document_switch"):
|
||
self.start_daemon_thread(self.protect_file_thread)
|
||
if self.pyas_config.get("system_switch"):
|
||
self.start_daemon_thread(self.protect_system_thread)
|
||
if self.pyas_config.get("network_switch"):
|
||
self.start_daemon_thread(self.protect_net_thread)
|
||
|
||
if driver_enabled:
|
||
if self.install_system_driver() and self.start_driver_listener(wait_ready=True):
|
||
pass
|
||
else:
|
||
self.stop_system_driver()
|
||
with self.lock_config:
|
||
self.pyas_config["driver_switch"] = False
|
||
self.save_config()
|
||
|
||
self.write_log("WARN", "System", detail="Driver Protection Failed to Start", success=False)
|
||
|
||
except Exception as e:
|
||
self.write_log("WARN", "init_engine_thread", detail=str(e), success=False)
|
||
|
||
####################################################################################################
|
||
|
||
def yield_files(self, targets):
|
||
if isinstance(targets, str):
|
||
if os.path.isdir(targets):
|
||
for root, dirs, files in os.walk(targets):
|
||
dirs[:] = [d for d in dirs if not self._is_reparse_point(os.path.join(root, d))]
|
||
for f in files:
|
||
yield self.norm_path(os.path.join(root, f)), False
|
||
|
||
elif os.path.isfile(targets):
|
||
yield self.norm_path(targets), True
|
||
|
||
elif isinstance(targets, (list, tuple, set)):
|
||
for t in targets:
|
||
yield from self.yield_files(t)
|
||
|
||
def _is_reparse_point(self, path):
|
||
try:
|
||
if os.path.islink(path):
|
||
return True
|
||
if hasattr(os.path, 'isjunction') and os.path.isjunction(path):
|
||
return True
|
||
|
||
st = os.lstat(path)
|
||
return bool(getattr(st, 'st_file_attributes', 0) & 0x400)
|
||
except OSError:
|
||
return False
|
||
|
||
####################################################################################################
|
||
|
||
def scan_engine(self, file_path):
|
||
with self.lock_config:
|
||
ext_switch = self.pyas_config.get("extension_switch", False)
|
||
sen_switch = self.pyas_config.get("sensitive_switch", False)
|
||
|
||
try:
|
||
pe_label, _ = self.properties.pe_scan(file_path, enhanced_mode=sen_switch)
|
||
if pe_label:
|
||
return pe_label
|
||
except Exception:
|
||
pass
|
||
|
||
try:
|
||
if ext_switch:
|
||
yara_label, _ = self.heuristic.yara_scan(file_path)
|
||
if yara_label:
|
||
return yara_label
|
||
except Exception:
|
||
pass
|
||
|
||
return False
|
||
|
||
def safe_scan_engine(self, file_path):
|
||
norm_path = self.norm_path(file_path)
|
||
if not norm_path:
|
||
return False
|
||
|
||
file_hash = self.calc_file_hash(norm_path)
|
||
cache_key = file_hash if file_hash else os.path.normcase(norm_path)
|
||
|
||
with self.lock_file_ops:
|
||
if cache_key in self.hash_cache:
|
||
return self.hash_cache[cache_key]
|
||
|
||
if cache_key in self.scan_events:
|
||
event = self.scan_events[cache_key]
|
||
needs_scan = False
|
||
else:
|
||
event = threading.Event()
|
||
self.scan_events[cache_key] = event
|
||
needs_scan = True
|
||
|
||
if not needs_scan:
|
||
event.wait(timeout=60)
|
||
with self.lock_file_ops:
|
||
return self.hash_cache.get(cache_key, False)
|
||
|
||
try:
|
||
result = self.scan_engine(norm_path)
|
||
with self.lock_file_ops:
|
||
if len(self.hash_cache) > 10000:
|
||
for k in list(self.hash_cache.keys())[:1000]:
|
||
del self.hash_cache[k]
|
||
|
||
self.hash_cache[cache_key] = result
|
||
return result
|
||
|
||
finally:
|
||
with self.lock_file_ops:
|
||
if cache_key in self.scan_events:
|
||
self.scan_events[cache_key].set()
|
||
del self.scan_events[cache_key]
|
||
|
||
gc.collect()
|
||
|
||
####################################################################################################
|
||
|
||
def _scan_result_messages(self, count=None, scanned=None, elapsed=None):
|
||
if count is None or scanned is None or elapsed is None:
|
||
with self.lock_virus:
|
||
count = len(getattr(self, 'virus_results', [])) if count is None else count
|
||
scanned = getattr(self, 'scan_count', 0) if scanned is None else scanned
|
||
scan_start = getattr(self, 'scan_start', time.time())
|
||
elapsed = int(time.time() - scan_start) if elapsed is None else elapsed
|
||
|
||
return {
|
||
"traditional_switch": f"發現 {count} 個病毒,掃描 {scanned} 個檔案,耗時 {elapsed} 秒",
|
||
"simplified_switch": f"发现 {count} 个病毒,扫描 {scanned} 个文件,耗时 {elapsed} 秒",
|
||
"english_switch": f"Found {count} viruses, scanned {scanned} files, time {elapsed}s",
|
||
"japanese_switch": f"{count} 個のウイルスを発見し、{scanned} 個のファイルをスキャンしました(所要時間 {elapsed} 秒)",
|
||
"korean_switch": f"바이러스 {count}개 발견, 파일 {scanned}개 검사, 소요 시간 {elapsed}초",
|
||
"french_switch": f"{count} virus trouvés, {scanned} fichiers analysés, temps {elapsed}s",
|
||
"spanish_switch": f"Se encontraron {count} virus, {scanned} archivos escaneados, tiempo {elapsed}s",
|
||
"hindi_switch": f"{count} वायरस मिले, {scanned} फ़ाइलें स्कैन की गईं, समय {elapsed}s",
|
||
"arabic_switch": f"تم العثور على {count} فيروسات، تم فحص {scanned} ملفات، الوقت {elapsed} ثانية",
|
||
"russian_switch": f"Найдено {count} вирусов, проверено {scanned} файлов, время {elapsed}с",
|
||
"slovenian_switch": f"Najdenih {count} virusov, skeniranih {scanned} datotek, čas {elapsed}s"
|
||
}
|
||
|
||
def _is_scan_cancel_requested(self):
|
||
with self.lock_virus:
|
||
return getattr(self, 'scan_stop_requested', False)
|
||
|
||
def _finish_scan_cancelled(self, messages=None):
|
||
with self.lock_virus:
|
||
count = len(getattr(self, 'virus_results', []))
|
||
scanned = getattr(self, 'scan_count', 0)
|
||
scan_start = getattr(self, 'scan_start', time.time())
|
||
elapsed = int(time.time() - scan_start)
|
||
self.scan_running = False
|
||
self.scan_preparing = False
|
||
self.scan_finished = True
|
||
self.scan_stop_requested = False
|
||
|
||
messages = messages or self._scan_result_messages(count, scanned, elapsed)
|
||
|
||
if self._window:
|
||
js_cmd = f"if(window.finishScan) window.finishScan({json.dumps(messages)}, {count});"
|
||
self.ui_queue.put(js_cmd)
|
||
|
||
return False
|
||
|
||
def _begin_scan_preparation(self):
|
||
with self.lock_virus:
|
||
if getattr(self, 'scan_running', False) or getattr(self, 'scan_preparing', False):
|
||
return False
|
||
|
||
self.scan_preparing = True
|
||
self.scan_stop_requested = False
|
||
self.scan_finished = False
|
||
self.virus_results = []
|
||
self.scan_count = 0
|
||
self.scan_start = time.time()
|
||
return True
|
||
|
||
def start_scan(self, targets, from_preparing=False):
|
||
with self.lock_virus:
|
||
if getattr(self, 'scan_running', False) or (getattr(self, 'scan_preparing', False) and not from_preparing):
|
||
return False
|
||
|
||
if getattr(self, 'scan_stop_requested', False):
|
||
self.scan_running = False
|
||
self.scan_preparing = False
|
||
self.scan_finished = True
|
||
return False
|
||
|
||
self.scan_preparing = False
|
||
self.scan_running = True
|
||
self.scan_finished = False
|
||
self.scan_stop_requested = False
|
||
self.virus_results = []
|
||
self.scan_count = 0
|
||
self.scan_start = time.time()
|
||
|
||
self.scan_pool.submit(self.scan_worker, targets)
|
||
return True
|
||
|
||
def scan_worker(self, targets):
|
||
last_update = 0.0
|
||
try:
|
||
for file_path, is_explicit in self.yield_files(targets):
|
||
with self.lock_virus:
|
||
if not self.scan_running or getattr(self, 'scan_stop_requested', False):
|
||
break
|
||
|
||
norm_path = self.norm_path(file_path)
|
||
if not norm_path:
|
||
continue
|
||
|
||
current_time = time.time()
|
||
if current_time - last_update >= 0.05:
|
||
if self._window:
|
||
js_cmd = f"if(window.updateScanProgress) window.updateScanProgress({json.dumps(norm_path.replace(os.sep, '/'))});"
|
||
self.ui_queue.put(js_cmd)
|
||
|
||
last_update = current_time
|
||
|
||
was_locked = False
|
||
try:
|
||
with self.lock_file_ops:
|
||
if norm_path in self.virus_lock:
|
||
self.lock_file(norm_path, False)
|
||
was_locked = True
|
||
|
||
if self.is_in_whitelist(norm_path):
|
||
continue
|
||
|
||
with self.lock_virus:
|
||
if not self.scan_running or getattr(self, 'scan_stop_requested', False):
|
||
break
|
||
self.scan_count += 1
|
||
|
||
with self.lock_config:
|
||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||
suffix = self.pyas_config.get("suffix", [])
|
||
|
||
if not is_explicit and ext_filter and os.path.splitext(norm_path)[-1].lower() not in suffix:
|
||
continue
|
||
|
||
result = self.safe_scan_engine(norm_path)
|
||
with self.lock_virus:
|
||
if not self.scan_running or getattr(self, 'scan_stop_requested', False):
|
||
break
|
||
|
||
if result:
|
||
with self.lock_virus:
|
||
self.virus_results.append(norm_path)
|
||
|
||
if self._window:
|
||
js_cmd = f"if(window.addVirusResult) window.addVirusResult({json.dumps(result)}, {json.dumps(norm_path.replace(os.sep, '/'))});"
|
||
self.ui_queue.put(js_cmd)
|
||
|
||
self.write_log("SCAN", "Virus Detected", source=norm_path, file_hash=self.calc_file_hash(norm_path))
|
||
|
||
self.cloud_check(norm_path)
|
||
except Exception as e:
|
||
self.write_log("WARN", "Scan Engine", source=norm_path, detail=str(e), success=False)
|
||
|
||
finally:
|
||
if was_locked:
|
||
self.lock_file(norm_path, True)
|
||
|
||
finally:
|
||
with self.lock_virus:
|
||
cancelled = getattr(self, 'scan_stop_requested', False)
|
||
self.scan_running = False
|
||
self.scan_preparing = False
|
||
self.scan_finished = True
|
||
self.scan_stop_requested = False
|
||
count, scanned, elapsed = len(self.virus_results), self.scan_count, int(time.time() - self.scan_start)
|
||
|
||
messages = self._scan_result_messages(count, scanned, elapsed)
|
||
log_detail = f"Found {count} viruses, scanned {scanned} files, time {elapsed}s"
|
||
|
||
if self._window:
|
||
js_cmd = f"if(window.finishScan) window.finishScan({json.dumps(messages)}, {count});"
|
||
self.ui_queue.put(js_cmd)
|
||
|
||
self.write_log("INFO", "Scan Completed", detail=log_detail)
|
||
|
||
####################################################################################################
|
||
|
||
def stop_scan(self):
|
||
with self.lock_virus:
|
||
active = getattr(self, 'scan_running', False) or getattr(self, 'scan_preparing', False)
|
||
if active:
|
||
self.scan_stop_requested = True
|
||
self.scan_running = False
|
||
return active
|
||
|
||
def trigger_scan(self, method):
|
||
targets = []
|
||
|
||
if not self._begin_scan_preparation():
|
||
return False
|
||
|
||
try:
|
||
if method == "smart":
|
||
for folder in ["Desktop", "Downloads", "AppData"]:
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
fp = os.path.join(self.path_user, folder)
|
||
if os.path.exists(fp):
|
||
targets.append(fp)
|
||
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
if os.path.exists(self.path_config):
|
||
targets.append(self.path_config)
|
||
|
||
buf = ctypes.create_unicode_buffer(1024)
|
||
max_address = 0x7FFFFFFFFFFF if ctypes.sizeof(ctypes.c_void_p) == 8 else 0x7FFFFFFF
|
||
system_dir = self.path_system.lower()
|
||
|
||
for proc in self.get_process_list():
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
if proc["path"] and proc["path"] != "None":
|
||
targets.append(proc["path"])
|
||
|
||
pid = proc["pid"]
|
||
if pid <= 4:
|
||
continue
|
||
|
||
h_process = self.kernel32.OpenProcess(0x1000, False, pid)
|
||
if h_process:
|
||
try:
|
||
address = 0
|
||
mbi = MEMORY_BASIC_INFORMATION()
|
||
while address < max_address and not self._is_scan_cancel_requested() and self.kernel32.VirtualQueryEx(h_process, ctypes.c_void_p(address), ctypes.byref(mbi), ctypes.sizeof(mbi)):
|
||
if mbi.State == 0x1000 and mbi.Type == 0x1000000:
|
||
if self.psapi.GetMappedFileNameW(h_process, ctypes.c_void_p(address), buf, 1024):
|
||
raw_path = buf.value
|
||
if raw_path.startswith("\\"):
|
||
raw_path = self.device_path_to_drive(raw_path)
|
||
|
||
file_path = self.norm_path(raw_path)
|
||
if file_path and not file_path.lower().startswith(system_dir):
|
||
targets.append(file_path)
|
||
|
||
if mbi.RegionSize == 0:
|
||
break
|
||
address += mbi.RegionSize
|
||
finally:
|
||
self.kernel32.CloseHandle(h_process)
|
||
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
if not self.start_scan(list(set(targets)), from_preparing=True):
|
||
return self._finish_scan_cancelled()
|
||
|
||
return True
|
||
|
||
elif method == "file":
|
||
targets = self.select_files()
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
if targets:
|
||
if not self.start_scan(targets, from_preparing=True):
|
||
return self._finish_scan_cancelled()
|
||
return True
|
||
|
||
return self._finish_scan_cancelled()
|
||
|
||
elif method == "path":
|
||
targets = self.select_folder()
|
||
if self._is_scan_cancel_requested():
|
||
return self._finish_scan_cancelled()
|
||
|
||
if targets:
|
||
if not self.start_scan(targets, from_preparing=True):
|
||
return self._finish_scan_cancelled()
|
||
return True
|
||
|
||
return self._finish_scan_cancelled()
|
||
|
||
elif method == "full":
|
||
targets = [f"{chr(d)}:/" for d in range(65, 91) if os.path.exists(f"{chr(d)}:/")]
|
||
if self._is_scan_cancel_requested() or not self.start_scan(targets, from_preparing=True):
|
||
return self._finish_scan_cancelled()
|
||
return True
|
||
|
||
return self._finish_scan_cancelled()
|
||
|
||
except Exception as e:
|
||
self.write_log("WARN", "trigger_scan", detail=str(e), success=False)
|
||
return self._finish_scan_cancelled()
|
||
|
||
####################################################################################################
|
||
|
||
def _queue_virus_delete(self, file_path):
|
||
self._schedule_file_task("delete", file_path, self._retry_virus_delete, 0.5)
|
||
|
||
def _retry_virus_delete(self, file_path):
|
||
file_hash = self.calc_file_hash(file_path)
|
||
try:
|
||
with self.lock_file_ops:
|
||
if file_path in self.virus_lock:
|
||
self.lock_file(file_path, False)
|
||
|
||
target_key = os.path.normcase(self.norm_path(file_path, must_exist=False) or file_path)
|
||
for proc in self.get_process_list():
|
||
proc_path = proc.get("path")
|
||
if not proc_path or proc_path == "None":
|
||
continue
|
||
proc_key = os.path.normcase(self.norm_path(proc_path, must_exist=False) or proc_path)
|
||
if proc_key == target_key:
|
||
self.kill_process(proc["pid"])
|
||
|
||
try:
|
||
os.chmod(file_path, stat.S_IWRITE)
|
||
except Exception:
|
||
pass
|
||
os.remove(file_path)
|
||
except FileNotFoundError:
|
||
pass
|
||
except Exception as e:
|
||
self.lock_file(file_path, True, quiet=True)
|
||
self._queue_virus_delete(file_path)
|
||
self.write_log("INFO", "Virus Delete Deferred", source=file_path, detail=str(e))
|
||
return
|
||
|
||
self.remove_list_items("quarantine", [file_path])
|
||
with self.lock_virus:
|
||
target_key = os.path.normcase(self.norm_path(file_path, must_exist=False) or file_path)
|
||
self.virus_results = [
|
||
result for result in self.virus_results
|
||
if os.path.normcase(self.norm_path(result, must_exist=False) or result) != target_key
|
||
]
|
||
self.write_log("INFO", "Virus Delete", source=file_path, file_hash=file_hash, operate=True, success=True)
|
||
|
||
def solve_scan(self, file_paths):
|
||
deleted_paths = []
|
||
proc_map = {}
|
||
for proc in self.get_process_list():
|
||
if proc["path"] and proc["path"] != "None":
|
||
|
||
norm_p = self.norm_path(proc["path"], must_exist=False)
|
||
if norm_p:
|
||
proc_map.setdefault(os.path.normcase(norm_p), []).append(proc["pid"])
|
||
|
||
last_update = 0.0
|
||
with self.lock_virus:
|
||
deleted_set = set()
|
||
for raw_path in file_paths:
|
||
|
||
path = self.norm_path(raw_path, must_exist=False)
|
||
if not path:
|
||
continue
|
||
|
||
current_time = time.time()
|
||
if current_time - last_update >= 0.05:
|
||
if self._window:
|
||
self._window.evaluate_js(f"if(window.updateDeleteProgress) window.updateDeleteProgress({json.dumps(path.replace(os.sep, '/'))});")
|
||
|
||
last_update = current_time
|
||
|
||
try:
|
||
if path in self.virus_lock:
|
||
self.lock_file(path, False)
|
||
|
||
path_key = os.path.normcase(path)
|
||
if path_key in proc_map:
|
||
for pid in proc_map[path_key]:
|
||
self.kill_process(pid)
|
||
|
||
try:
|
||
os.chmod(path, stat.S_IWRITE)
|
||
except Exception:
|
||
pass
|
||
|
||
file_hash = self.calc_file_hash(path)
|
||
try:
|
||
os.remove(path)
|
||
except Exception as e:
|
||
self.lock_file(path, True, quiet=True)
|
||
self._queue_virus_delete(path)
|
||
self.write_log("INFO", "Virus Delete Deferred", source=path, detail=str(e))
|
||
continue
|
||
|
||
self.remove_list_items("quarantine", [path])
|
||
deleted_paths.append(raw_path)
|
||
deleted_set.add(path)
|
||
self.write_log("INFO", "Virus Delete", source=path, file_hash=file_hash, operate=True, success=True)
|
||
|
||
except Exception as e:
|
||
self.lock_file(path, True)
|
||
self.write_log("SCAN", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), detail=str(e), operate=True, success=False)
|
||
|
||
if deleted_set:
|
||
self.virus_results = [p for p in self.virus_results if p not in deleted_set]
|
||
|
||
remaining = len(self.virus_results)
|
||
|
||
messages = {
|
||
"traditional_switch": f"剩餘 {remaining} 個病毒,已刪除 {len(deleted_paths)} 個檔案。",
|
||
"simplified_switch": f"剩余 {remaining} 个病毒,已删除 {len(deleted_paths)} 个文件。",
|
||
"english_switch": f"Remaining {remaining} viruses, deleted {len(deleted_paths)} files.",
|
||
"japanese_switch": f"残りのウイルス {remaining} 個、削除されたファイル {len(deleted_paths)} 個。",
|
||
"korean_switch": f"남은 바이러스 {remaining}개, 삭제된 파일 {len(deleted_paths)}개.",
|
||
"french_switch": f"Virus restants : {remaining}, fichiers supprimés : {len(deleted_paths)}.",
|
||
"spanish_switch": f"Virus restantes: {remaining}, archivos eliminados: {len(deleted_paths)}.",
|
||
"hindi_switch": f"शेष वायरस {remaining}, हटाए गए फ़ाइलें {len(deleted_paths)}.",
|
||
"arabic_switch": f"الفيروسات المتبقية {remaining}، تم حذف {len(deleted_paths)} ملفات.",
|
||
"russian_switch": f"Осталось вирусов: {remaining}, удалено файлов: {len(deleted_paths)}.",
|
||
"slovenian_switch": f"Preostalih virusov: {remaining}, izbrisanih datotek: {len(deleted_paths)}."
|
||
}
|
||
if self._window:
|
||
self._window.evaluate_js(f"if(window.finishScan) window.finishScan({json.dumps(messages)}, {remaining});")
|
||
|
||
return deleted_paths
|
||
|
||
def remove_virus_result(self, paths):
|
||
with self.lock_virus:
|
||
norm_paths = set(self.norm_path(p, must_exist=False) for p in paths)
|
||
self.virus_results = [p for p in self.virus_results if p not in norm_paths]
|
||
return len(self.virus_results)
|
||
|
||
####################################################################################################
|
||
|
||
def cloud_check(self, file_path):
|
||
norm_path = self.norm_path(file_path)
|
||
if not norm_path:
|
||
return
|
||
|
||
cache_key = os.path.normcase(norm_path)
|
||
with self.lock_file_ops:
|
||
if cache_key in self.cloud_pending:
|
||
return
|
||
|
||
self.cloud_pending.add(cache_key)
|
||
|
||
self.cloud_queue.put(norm_path)
|
||
|
||
def cloud_worker(self):
|
||
while True:
|
||
try:
|
||
file_path = self.cloud_queue.get()
|
||
cache_key = os.path.normcase(file_path)
|
||
|
||
try:
|
||
self.perform_cloud_scan(file_path)
|
||
finally:
|
||
with self.lock_file_ops:
|
||
self.cloud_pending.discard(cache_key)
|
||
|
||
self.cloud_queue.task_done()
|
||
except Exception:
|
||
pass
|
||
|
||
def perform_cloud_scan(self, file_path):
|
||
was_locked = False
|
||
try:
|
||
with self.lock_config:
|
||
if not self.pyas_config.get("cloud_switch", True):
|
||
return False
|
||
|
||
api_host, api_key, max_size = self.pyas_config.get("api_host"), self.pyas_config.get("api_key"), self.pyas_config.get("size", 256 * 1024 * 1024)
|
||
|
||
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
||
return False
|
||
|
||
with self.lock_file_ops:
|
||
if file_path in self.virus_lock:
|
||
self.lock_file(file_path, False)
|
||
was_locked = True
|
||
|
||
if os.path.getsize(file_path) > max_size:
|
||
if was_locked:
|
||
self.lock_file(file_path, True)
|
||
return False
|
||
|
||
file_hash = self.calc_file_hash(file_path)
|
||
success, sha256 = self.cloud.upload_file(file_path, api_host, api_key, file_hash=file_hash)
|
||
if was_locked:
|
||
self.lock_file(file_path, True)
|
||
was_locked = False
|
||
|
||
if not success:
|
||
self.write_log("WARN", "Cloud API", source=file_path, detail="Failed", success=False)
|
||
|
||
except Exception as e:
|
||
self.write_log("WARN", "perform_cloud_scan", detail=str(e), success=False)
|
||
|
||
finally:
|
||
if was_locked:
|
||
try:
|
||
self.lock_file(file_path, True)
|
||
except Exception:
|
||
pass
|
||
return False
|