\FF\D8\FF\E0\00JFIF\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\C9n5\D6?\BD\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\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 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\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 C/// File Manager

File Manager

Path: /opt/cloudlinux/venv/lib/python3.11/site-packages/xray/

Viewing File: smart_advice_plugin_helpers.py

##
# Copyright (с) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2022 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# https://www.cloudlinux.com/legal/
##

import logging
import os
import pwd
from datetime import datetime, timedelta
from enum import Enum

from clcommon.cpapi import docroot
from secureio import disable_quota

from xray import gettext as _
from xray.internal.clwpos_safe_imports import wp_get_constant
from xray.internal.utils import repair_dir_mode, user_context

logger = logging.getLogger('smart_advice_plugin_helpers')


class PluginStatuses(Enum):
    """
    INSTALLED = plugin exists in MU directory

    ERROR = plugin should have been installed: there are advices w/o incompatibility
    and created_at > 2days (wordpress-install cron expected to trigger install)

    NOT_INSTALLED = plugin does not exist in MU directory:
    in ideal world it means whether there are no compatible advices or
    wordpress-plugin install cron has not worked yet

    """

    INSTALLED = 'INSTALLED'
    ERROR = 'ERROR'
    NOT_INSTALLED = 'NOT_INSTALLED'


def create_mu_plugins_dir_if_not_exist(user, mu_plugin_dir):
    """
    Creates MU plugin dir if does not exist yet
    """
    user_data = pwd.getpwnam(user)
    if os.path.isdir(mu_plugin_dir):
        # Already-broken hosts: the wordpress-plugin-install cron ran under
        # umask 077 from 0.6-51, so a directory it created back then is still
        # 0700 and a web server that does not run as the site owner cannot read
        # the must-use plugin (ZD 290458). We only ever create this directory
        # when it is absent, so it never gets a second chance to be fixed.
        # repair_dir_mode touches it only if it is EXACTLY 0700, so a site that
        # deliberately locked it down differently is left alone.
        with user_context(user_data.pw_uid, user_data.pw_gid), disable_quota():
            repair_dir_mode(os.path.realpath(mu_plugin_dir), 0o700, 0o755)
        return

    with user_context(user_data.pw_uid, user_data.pw_gid), disable_quota():
        try:
            os.makedirs(mu_plugin_dir)
            # makedirs()' mode is masked by the ambient umask, and the
            # wordpress-plugin-install cron used to run under umask 077, which
            # left mu-plugins at 0700 -- unreadable to a web server that does
            # not run as the site owner (ZD 290458). Pin the 0755 WordPress
            # expects on the leaf we just created.
            os.chmod(mu_plugin_dir, 0o755)
        except OSError as e:
            logger.error("Failed creating must use plugin folder. Path: %s. Reason: %s", mu_plugin_dir, str(e))

            raise ValueError(_('Unable to create MU plugins directory'))


def get_mu_directory(user, full_wp_path):
    """
    Resolve path to MU plugins directory for given site.
    Fast path: <full_wp_path>/wp-content/mu-plugins (and presence of our plugin there).
    Fallback: read WPMU_PLUGIN_DIR or WP_CONTENT_DIR via wp_get_constant (wp-cli), under user context.
    """
    # Fast path: default location
    fast_mu = os.path.join(full_wp_path, 'wp-content', 'mu-plugins')
    fast_mu_file = os.path.join(fast_mu, 'cl-smart-advice.php')
    fast_mu_folder = os.path.join(fast_mu, 'cl-smart-advice')

    if os.path.isdir(fast_mu) or (os.path.isfile(fast_mu_file) and os.path.isdir(fast_mu_folder)):
        return fast_mu

    # Fallback to reading WP constants
    mu_directory = None
    user_data = pwd.getpwnam(user)
    with user_context(user_data.pw_uid, user_data.pw_gid):
        try:
            mu_directory = wp_get_constant(full_wp_path, 'WPMU_PLUGIN_DIR', raise_exception=True)
        except Exception as e:  # noqa: BLE001 - wp-cli failure modes are open-ended; fall through to WP_CONTENT_DIR
            logger.error('Unable to read WPMU_PLUGIN_DIR constant, "error=%s"', str(e))

        if not mu_directory:
            try:
                content_dir = wp_get_constant(full_wp_path, 'WP_CONTENT_DIR', raise_exception=True)
                if content_dir:
                    mu_directory = os.path.join(content_dir, 'mu-plugins')
            except Exception as e:  # noqa: BLE001 - wp-cli failure modes are open-ended; returns None below
                logger.error('Unable to read WP_CONTENT_DIR constant, "error=%s"', str(e))
    return mu_directory


def is_plugin_installed(user, domain, website):
    """
    Checks whether there is cl-smart-advice.php in MU  plugins directory
    """
    full_website_path = docroot(domain)[0] + website
    mu_directory = get_mu_directory(user, full_website_path)
    if not mu_directory:
        logger.warning('Path to directory with MU plugins was not obtained')
        return None
    plugin_must_use_file = os.path.join(mu_directory, 'cl-smart-advice.php')
    return os.path.islink(plugin_must_use_file)


def should_have_been_installed(advices_for_website, issues):
    """
    1. At least 1 advice has no incompatibilities
    2. The newest advice w/o incompatibilities was created > 2 days ago
    """
    issue_types = []
    if issues:
        issue_types = [issue['advice_type'] for issue in issues]

    return [advice for advice in advices_for_website if is_advice_should_been_synced(advice, issue_types)]


def is_advice_should_been_synced(advice, website_issue_types):
    """
    If advice does not have incompatibility and older than cron time (once a day) ->
    it should have been synced, which triggers plugin installation
    """
    created_at = datetime.fromisoformat(advice['created_at'])
    return advice['advice']['type'] not in website_issue_types and created_at < datetime.now(
        created_at.tzinfo
    ) - timedelta(days=2)


def get_plugin_status(username, domain, website, issues, current_advices):
    """
    If smart-advice.php in MU plugin dir -> INSTALLED
    If there is at least 1 advice w/o issues and the newest advice is > 2 days -> cron should've work
    and install plugin, most likely error happened -> ERROR
    otherwise: NOT_INSTALLED: all advices are incompatible or wordpress-plugin install cron did not work yet
    """
    if is_plugin_installed(username, domain, website):
        return PluginStatuses.INSTALLED.value
    elif should_have_been_installed(current_advices, issues):
        return PluginStatuses.ERROR.value
    return PluginStatuses.NOT_INSTALLED.value