ok

Mini Shell

Direktori : /opt/imunify360/venv/lib/python3.11/site-packages/restore_infected/
Upload File :
Current File : //opt/imunify360/venv/lib/python3.11/site-packages/restore_infected/helpers.py

import datetime
import ftplib
import functools
import inspect
import os
import re
import shutil
import socket
import warnings
from itertools import chain

from dateutil import parser


def from_env(**param_to_env):
    def decorator(f):
        spec = inspect.getfullargspec(f)
        assert all(
            param in chain(spec.args, spec.kwonlyargs)
            for param in param_to_env
        )
        f.from_env = param_to_env
        return f

    return decorator


class DateTime(datetime.datetime):
    def __new__(cls, s):
        return parser.parse(s, ignoretz=True)

    fromtimestamp = datetime.datetime.fromtimestamp
    now = datetime.datetime.now
    utcfromtimestamp = datetime.datetime.utcfromtimestamp


def reconnect_ftp(func):
    @functools.wraps(func)
    def wrapper(ftp, *args, **kwargs):
        try:
            return func(ftp, *args, **kwargs)
        except ftplib.error_temp:
            ftp.connect()
            return func(ftp, *args, **kwargs)
    return wrapper


class FtpError(Exception):
    pass


def _handle_ftp_error(func):
    @functools.wraps(func)
    def wrapper(ftp, *args, **kwargs):
        try:
            return func(ftp, *args, **kwargs)
        except (ftplib.Error, socket.error) as e:
            raise FtpError(*e.args)
    return wrapper


@functools.lru_cache(maxsize=None)
class Ftp:
    ftp = None

    def __init__(self, host, login, password, use_ftps, passive_mode=True,
                 port=21, timeout=10, **_):
        self.host = host
        self.login = login
        self.password = password
        self.passive_mode = passive_mode
        self.use_ftps = use_ftps
        self.port = port
        self.timeout = timeout

    @property
    def _address(self):
        return self.login + '@' + self.host + ':' + str(self.port)

    def __str__(self):
        protocol = 'ftps' if self.use_ftps else 'ftp'
        return protocol + '://' + self._address

    @_handle_ftp_error
    def connect(self):
        if self.use_ftps:
            ftp = ftplib.FTP_TLS()
        else:
            ftp = ftplib.FTP()

        ftp.connect(self.host, self.port, self.timeout)
        ftp.login(self.login, self.password)

        if self.use_ftps:
            ftp.prot_p()

        ftp.set_pasv(self.passive_mode)

        self.ftp = ftp

    @_handle_ftp_error
    @reconnect_ftp
    def listdir(self, *args):
        return self.ftp.nlst(*args)

    @_handle_ftp_error
    @reconnect_ftp
    def mlistdir(self, *args, **kwargs):
        return self.ftp.mlsd(*args, **kwargs)

    @_handle_ftp_error
    @reconnect_ftp
    def retrieve(self, path, destination):
        """
        :raises FtpError:
        :raises IsNotDirError:
        :raises DirNotEmptyError:
        """
        if not os.path.isabs(destination):
            destination = os.path.abspath(destination)

        destination = os.path.join(destination, self._address)

        target_name = os.path.join(destination, path.lstrip(os.path.sep))
        target_dir = os.path.dirname(target_name)

        mkdir(target_dir)

        free_space = shutil.disk_usage(target_dir).free
        try:
            target_size = self.size(path)
        except FtpError:
            # Skip this step if SIZE command is not supported on the server
            pass
        else:
            if target_size > free_space:
                raise FtpError('Disk is full', free_space, target_size)

        with open(target_name, 'wb') as w:
            self.ftp.retrbinary('RETR ' + path, w.write)

        return target_name

    @_handle_ftp_error
    @reconnect_ftp
    def size(self, dir_path):
        """ Get the size of the file on the server """
        try:
            self.ftp.sendcmd('TYPE I')
            return self.ftp.size(dir_path)
        finally:
            # Fallback to the default ASCII mode
            self.ftp.sendcmd('TYPE A')


class DirNotEmptyError(FileExistsError):
    def __init__(self, message=None):
        message = message or 'destination directory exists and it is not empty'
        super().__init__(message)


class IsNotDirError(FileExistsError):
    def __init__(self, message=None):
        message = message or 'destination exists but it is not a directory'
        super().__init__(message)


class ActionError(Exception):
    def __init__(self, message=None, code=1):
        self.message = message
        self.code = code


def mkdir(path, check_empty=False):
    if os.path.isdir(path):
        if check_empty and os.listdir(path):
            raise DirNotEmptyError()
    elif os.path.exists(path):
        raise IsNotDirError()
    else:
        os.makedirs(path)


def read(fileobj, size=2 ** 20):
    for chunk in iter(lambda: fileobj.read(size), b''):
        yield chunk


optional_arg_match = re.compile(r'--(?P<key>\w+)(=(?P<value>\S+))?')


def parse_extra_args(arg_list):
    # FIXME: move this logic into a special ArgumentParser argument and
    # get rid of `extra_args` magic
    args = []
    kwargs = {}
    for arg in arg_list:
        match = optional_arg_match.match(arg)
        if match:
            kw = match.groupdict()
            value = kw['value']
            if value is None or value.lower() in ('1', 'true', 'yes', 'y'):
                value = True
            elif value.lower() in ('0', 'false', 'no', 'n'):
                value = False
            kwargs[kw['key']] = value
        else:
            args.append(arg)
    return args, kwargs


def _fill_out_positional_args(parsed_args, env_vars, positional_parameters):
    if len(parsed_args) < len(positional_parameters):
        provided_args_it = iter(parsed_args)
        result = []
        for param in positional_parameters:
            if param in env_vars:
                result.append(env_vars[param])
            else:
                try:
                    result.append(next(provided_args_it))
                except StopIteration:
                    continue
        result.extend(provided_args_it)
        return result

    return parsed_args


def _fill_out_keyword_args(parsed_kwargs, env_vars, keyword_parameters):
    return {
        **{
            arg: env_vars[arg] for arg in keyword_parameters if arg in env_vars
        },
        **parsed_kwargs,
    }


def fill_args_from_env(spec, parsed_args, parsed_kwargs, env_vars):
    defaults_len = len(spec.defaults) if spec.defaults else 0
    required = spec.args[: -defaults_len]
    optional = spec.args[-defaults_len:]
    return (
        _fill_out_positional_args(parsed_args, env_vars, required),
        _fill_out_keyword_args(parsed_kwargs, env_vars, optional),
    )


def validate_params(spec, args, kwargs):
    """
    Return tuple of lists with missing and unknown arguments of a func
    :param spec: func spec
    :param args: list of args to validate
    :param kwargs: list of args to validate
    :return: tuple of missing and unknown lists of arguments
    """
    defaults_len = -len(spec.defaults) if spec.defaults else None
    required = spec.args[:defaults_len]
    required_len = len(required)

    args_len = len(args)
    varargs = spec.varargs

    if not varargs and required_len != args_len or required_len > args_len:
        missing = required
    else:
        missing = []

    optional = spec.args[defaults_len:]
    unknown = list(set(kwargs) - set(optional))

    return missing, unknown


def _formatwarning(message, *_, **__):
    return 'Warning: %s%s' % (message, os.linesep)


warnings.formatwarning = _formatwarning


def warning(message, once=False):
    if once:
        warnings.warn(message)
    else:
        with warnings.catch_warnings():
            warnings.simplefilter('always')
            warnings.warn(message)

Zerion Mini Shell 1.0