# -*- coding: utf-8 -*- import os import shutil import subprocess try: import send2trash except ImportError: pass from module.plugins.internal.Addon import Addon, Expose, threaded from module.plugins.internal.utils import encode, exists, fs_join class AntiVirus(Addon): __name__ = "AntiVirus" __type__ = "hook" __version__ = "0.15" __status__ = "testing" #@TODO: add trash option (use Send2Trash lib) __config__ = [("activated" , "bool" , "Activated" , False ), ("action" , "Antivirus default;Delete;Quarantine", "Manage infected files" , "Antivirus default"), ("quardir" , "folder" , "Quarantine folder" , "" ), ("deltotrash", "bool" , "Move to trash instead delete", True ), ("scanfailed", "bool" , "Scan failed downloads" , False ), ("avfile" , "file" , "Antivirus executable" , "" ), ("avargs" , "str" , "Executable arguments" , "" ), ("avtarget" , "file;folder" , "Scan target" , "file" ), ("ignore-err", "bool" , "Ignore scan errors" , False )] __description__ = """Scan downloaded files with antivirus program""" __license__ = "GPLv3" __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] @Expose @threaded def scan(self, pyfile, thread): avfile = encode(self.get_config('avfile')) avargs = encode(self.get_config('avargs').strip()) if not os.path.isfile(avfile): self.fail(_("Antivirus executable not found")) scanfolder = self.get_config('avtarget') is "folder" if scanfolder: dl_folder = self.pyload.config.get("general", "download_folder") package_folder = pyfile.package().folder if self.pyload.config.get("general", "folder_per_package") else "" target = fs_join(dl_folder, package_folder, pyfile.name) target_repr = "Folder: " + package_folder or dl_folder else: target = encode(pyfile.plugin.last_download) target_repr = "File: " + os.path.basename(pyfile.plugin.last_download) if not exists(target): return thread.addActive(pyfile) pyfile.setCustomStatus(_("virus scanning")) pyfile.setProgress(0) try: p = subprocess.Popen([avfile, avargs, target], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = map(str.strip, p.communicate()) if out: self.log_info(target_repr, out) if err: self.log_warning(target_repr, err) if not self.get_config('ignore-err'): self.log_debug("Delete/Quarantine task aborted due scan error") return if p.returncode: action = self.get_config('action') if scanfolder: if action is "Antivirus default": self.log_warning(_("Delete/Quarantine task skipped in folder scan mode")) return pyfile.error = _("Infected file") try: if action is "Delete": if not self.get_config('deltotrash'): os.remove(file) else: try: send2trash.send2trash(file) except NameError: self.log_warning(_("Send2Trash lib not found, moving to quarantine instead")) pyfile.setCustomStatus(_("file moving")) shutil.move(file, self.get_config('quardir')) except Exception, e: self.log_warning(_("Unable to move file to trash: %s, moving to quarantine instead") % e.message) pyfile.setCustomStatus(_("file moving")) shutil.move(file, self.get_config('quardir')) else: self.log_debug("Successfully moved file to trash") elif action is "Quarantine": pyfile.setCustomStatus(_("file moving")) shutil.move(file, self.get_config('quardir')) except (IOError, shutil.Error), e: self.log_error(target_repr, action + " action failed!", e) elif not err: self.log_debug(target_repr, "No infected file found") finally: pyfile.setProgress(100) thread.finishFile(pyfile) def download_finished(self, pyfile): return self.scan(pyfile) def download_failed(self, pyfile): #: Check if pyfile is still "failed", maybe might has been restarted in meantime if pyfile.status is 8 and self.get_config('scanfailed'): return self.scan(pyfile)