\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7<F<2PFAFZUP_xxnnx\F5\AF\B9\91\C8\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\DB\00CUZZxix‚\EB\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\C0\00\00b\00d\00\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00\001A!Q\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00!1AQ\FF\DA\00\00\00?\00\F6\00\00\00\00\00\00\00\00\00\00\A0\00\00\C5\CF\F8\E7Ó\FCjD~\B9^\AD\%\B3]\D8cs-\BBs,-\A0\00"\80\00%\B83rÏ^K~F\A4ea\00E\00\C6\EE=u\B1\9B\D1\00@\00r\BE8\F9:\FE5#.M\00\E7\CB\C9q\CBq\D6\F2\BE\B7\C7>\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7	\8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh
\00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€
\00$\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95
59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>C///</title>
</head>
# -*- coding: utf-8 -*-

# Copyright Â© Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

import functools
import json
from pathlib import Path
from typing import Dict

from clwpos.cl_wpos_exceptions import WposError
from clwpos.constants import USER_WPOS_DIR
from clwpos.logsetup import setup_logging
from clwpos.optimization_features import ALL_OPTIMIZATION_FEATURES
from clwpos.utils import home_dir, is_run_under_user
from secureio import write_file_via_tempfile

logger = setup_logging(__name__)

PROGRESS_STATUS_FILE_NAME = '.progress-status'


def get_progress_file_path() -> Path:
    return Path(home_dir(), USER_WPOS_DIR, PROGRESS_STATUS_FILE_NAME)


class CommandProgress:
    """
    Class to represent user's AccelerateWP utility commands progress.
    """
    total_stages: int

    def __new__(cls, command):
        command_subclass_map = {
            subclass.command: subclass for subclass in cls.__subclasses__()
        }
        if command not in command_subclass_map:
            raise WposError(f'Internal Error: command "{command}" is not allowed, '
                            f'available commands: {list(command_subclass_map.keys())}')
        subclass = command_subclass_map[command]
        instance = super(CommandProgress, subclass).__new__(subclass)
        return instance

    def __init__(self, *args):
        if not is_run_under_user():
            raise WposError('Internal Error: trying to use CommandProgress class as root')
        self.progress_file = get_progress_file_path()
        self.in_progress = False
        self.completed_stages = 0

    def as_dict(self) -> Dict[str, int]:
        """
        Return number of total and completed stages as dict.
        """
        return {
            'total_stages': self.total_stages,
            'completed_stages': self.completed_stages,
        }

    def start(self) -> None:
        """
        Start tracking the progress of the command.
        """
        self.in_progress = True
        self.completed_stages = 0
        self.progress_file.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
        self._update_progress_file()

    def update(self) -> None:
        """
        Update progress status by incrementing
        the number of completed stages by one.
        """
        if not self.in_progress:
            return
        self.completed_stages += 1
        if self.completed_stages > self.total_stages:
            raise WposError(
                'Internal Error: the number of completed stages '
                'is greater than the total number of stages'
            )
        self._update_progress_file()

    def complete(self) -> None:
        """
        Complete progress, delete file with progress status.
        """
        if not self.in_progress:
            return
        self.in_progress = False
        self.completed_stages = self.total_stages
        if self.progress_file.exists():
            self.progress_file.unlink()


    def _update_progress_file(self) -> None:
        # TODO: improve me
        # clcaptain cannot write via tmp file, disable_quota() also does not work as this code is run only under user
        # (get/enable/disable) not that critical now, because feature installing will not work anyway for overquota user
        write_file_via_tempfile(
            json.dumps(self.as_dict()), self.progress_file, 0o644)

    @staticmethod
    def get_status() -> Dict[str, int]:
        """
        Return current progress status of the command execution,
        getting it from from special file.
        """
        progress_file = get_progress_file_path()
        defaults = {'total_stages': 1, 'completed_stages': 0}

        if not progress_file.exists():
            return defaults

        try:
            with open(progress_file, 'r') as f:
                status_from_file = json.loads(f.read())
            return {
                'total_stages': int(status_from_file['total_stages']),
                'completed_stages': int(status_from_file['completed_stages']),
            }
        except Exception as e:
            logger.error('Can\'t parse progress status json: %s', e)
            return defaults


class EnableCommandProgress(CommandProgress):
    """
    Class to represent user's AccelerateWP utility "enable" command progress.
    """
    command: str = 'enable'
    total_stages: int = 9


class DisableCommandProgress(CommandProgress):
    """
    Class to represent user's WPOS utility "disable" command progress.
    """
    command: str = 'disable'
    total_stages: int = 5


class GetCommandProgress(CommandProgress):
    """
    Class to represent user's WPOS utility "get" command progress.
    """
    command: str = 'get'
    total_stages: int = 10

    def recalculate_number_of_total_stages(self, user_info: dict):
        """
        Recalculate and change total number of progress stages depend