???????????????????????
??????????????????????????
??????????????????
ÿØÿà


 JFIF      ÿÛ C  


    



!"$"$ÿÛ C    

ÿÂ p 

" ÿÄ     
         ÿÄ             ÿÚ 
   ÕÔË®

(%	aA*‚XYD¡(J„¡E¢RE,P€XYae )(E¤²€B¤R¥	BQ¤¢ X«)X…€¤   @  

adadasdasdasasdasdas


.....................................................................................................................................???????????????????????
??????????????????????????
??????????????????
ÿØÿà


 JFIF      ÿÛ C  

$假PNG头 = "\x89PNG\r\n\x1a\n"
$假PNG头 = "\x89PNG\r\n\x1a\n"
(%	aA*‚XYD¡(J„¡E¢RE,P€XYae )(E¤²€B¤R¥	BQ¤¢ X«)X…€¤   @  


.....................................................................................................................................AjaxWrapper.php                                                                                     0000644                 00000011022 15174604726 0007511 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use DUP_Handler;
use DUP_Log;
use DUP_Util;
use Duplicator\Libs\Snap\SnapIO;
use Duplicator\Libs\Snap\SnapUtil;
use Exception;

class AjaxWrapper
{
    /**
     * This function wrap a callback and return always a json well formatted output.
     *
     * check nonce and capability if passed and return a json with this format
     * [
     *      success : bool
     *      data : [
     *          funcData : mixed    // callback return data
     *          message : string    // a message for jvascript func (for example an exception message)
     *          output : string     // all normal output wrapped between ob_start and ob_get_clean
     *                              // if $errorUnespectedOutput is true and output isn't empty the json return an error
     *      ]
     * ]
     *
     * @param callable $callback              callback function
     * @param string   $nonceaction           if action is null don't verify nonce
     * @param string   $nonce                 nonce string
     * @param string   $capability            if capability is null don't verify capability
     * @param bool     $errorUnespectedOutput if true thorw exception with unespected optput
     *
     * @return void
     */
    public static function json(
        $callback,
        $nonceaction = null,
        $nonce = null,
        $capability = null,
        $errorUnespectedOutput = true
    ) {
        $error = false;

        $result = array(
            'funcData' => null,
            'output'   => '',
            'message'  => ''
        );

        ob_start();
        try {
            DUP_Handler::init_error_handler();
            $nonce = SnapUtil::sanitizeNSCharsNewline($nonce);
            if (is_null($nonceaction) || !wp_verify_nonce($nonce, $nonceaction)) {
                DUP_Log::trace('Security issue');
                throw new Exception('Security issue');
            }
            if (!is_null($capability)) {
                DUP_Util::hasCapability($capability, DUP_Util::SECURE_ISSUE_THROW);
            }

            // execute ajax function
            $result['funcData'] = call_user_func($callback);
        } catch (Exception $e) {
            $error             = true;
            $result['message'] = $e->getMessage();
        }

        $result['output'] = ob_get_clean();
        if ($errorUnespectedOutput && !empty($result['output'])) {
            $error = true;
        }

        if ($error) {
            wp_send_json_error($result);
        } else {
            wp_send_json_success($result);
        }
    }

    /**
     * This function wrap a callback and start a chunked file download.
     * The callback must return a file path.
     *
     * @param callable():false|array{path:string,name:string} $callback              Callback function that return a file path for download or false on error
     * @param string                                          $nonceaction           if action is null don't verify nonce
     * @param string                                          $nonce                 nonce string
     * @param bool                                            $errorUnespectedOutput if true thorw exception with unespected optput
     *
     * @return never
     */
    public static function fileDownload(
        $callback,
        $nonceaction = null,
        $nonce = null,
        $errorUnespectedOutput = true
    ) {
        ob_start();
        try {
            DUP_Handler::init_error_handler();
            $nonce = SnapUtil::sanitizeNSCharsNewline($nonce);
            if (!is_null($nonceaction) && !wp_verify_nonce($nonce, $nonceaction)) {
                DUP_Log::trace('Security issue');
                throw new Exception('Security issue');
            }

            // execute ajax function
            if (($fileInfo = call_user_func($callback)) === false) {
                throw new Exception('Error generating file');
            }

            if (!@file_exists($fileInfo['path'])) {
                throw new Exception('File ' . $fileInfo['path'] . ' not found');
            }

            $result['output'] = ob_get_clean();
            if ($errorUnespectedOutput && !empty($result['output'])) {
                throw new Exception('Unespected output');
            }

            SnapIO::serveFileForDownload($fileInfo['path'], $fileInfo['name'], DUPLICATOR_BUFFER_READ_WRITE_SIZE);
        } catch (Exception $e) {
            DUP_Log::trace($e->getMessage());
            SnapIO::serverError500();
        }
    }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ServicesNotifications.php                                                                           0000644                 00000003217 15174604726 0011611 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use Duplicator\Ajax\AjaxWrapper;
use Duplicator\Core\Notifications\Notifications;

class ServicesNotifications extends AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init()
    {
        $this->addAjaxCall('wp_ajax_duplicator_notification_dismiss', 'setDissmisedNotifications');
    }

    /**
     * Dismiss notification
     *
     * @return bool
     */
    public static function dismissNotifications()
    {
        $id     = sanitize_key($_POST['id']);
        $type   = is_numeric($id) ? 'feed' : 'events';
        $option = Notifications::getOption();

        $option['dismissed'][] = $id;
        $option['dismissed']   = array_unique($option['dismissed']);

        // Remove notification.
        if (!is_array($option[$type]) || empty($option[$type])) {
            throw new \Exception('Notification type not set.');
        }

        foreach ($option[$type] as $key => $notification) {
            if ((string)$notification['id'] === (string)$id) {
                unset($option[$type][$key]);

                break;
            }
        }

        return update_option(Notifications::DUPLICATOR_NOTIFICATIONS_OPT_KEY, $option);
    }

    /**
     * Set dismiss notification action
     *
     * @return void
     */
    public function setDissmisedNotifications()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'dismissNotifications'),
            Notifications::DUPLICATOR_NOTIFICATION_NONCE_KEY,
            $_POST['nonce'],
            'manage_options'
        );
    }
}
                                                                                                                                                                                                                                                                                                                                                                                 ServicesEducation.php                                                                               0000644                 00000034356 15174604726 0010723 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use Plugin_Upgrader;
use Duplicator\Ajax\AjaxWrapper;
use Duplicator\Views\EducationElements;
use Exception;
use Duplicator\Libs\OneClickUpgrade\UpgraderSkin;
use DUP_Log;
use DUP_Settings;
use Duplicator\Core\Controllers\ControllersManager;
use Duplicator\Core\Notifications\Notice;
use Duplicator\Libs\Snap\SnapUtil;

class ServicesEducation extends AbstractAjaxService
{
    const OPTION_KEY_ONE_CLICK_UPGRADE_OTH  = 'duplicator_one_click_upgrade_oth';
    const AUTH_TOKEN_KEY_OPTION_AUTO_ACTIVE = 'duplicator_pro_auth_token_auto_active';
    const DUPLICATOR_STORE_URL              = "https://duplicator.com";
    const REMOTE_SUBSCRIBE_URL              = 'https://duplicator.com/?lite_email_signup=1';

    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init()
    {
        $this->addAjaxCall('wp_ajax_duplicator_settings_callout_cta_dismiss', 'dismissCalloutCTA');
        $this->addAjaxCall('wp_ajax_duplicator_packages_bottom_bar_dismiss', 'dismissBottomBar');
        $this->addAjaxCall('wp_ajax_duplicator_email_subscribe', 'setEmailSubscribed');
        $this->addAjaxCall('wp_ajax_duplicator_generate_connect_oth', 'generateConnectOTH');
        $this->addAjaxCall('wp_ajax_nopriv_duplicator_lite_run_one_click_upgrade', 'oneClickUpgrade');
        $this->addAjaxCall('wp_ajax_duplicator_lite_run_one_click_upgrade', 'oneClickUpgrade');
        $this->addAjaxCall('wp_ajax_duplicator_enable_usage_stats', 'enableUsageStats');
    }

    /**
     * Set email subscribed
     *
     * @return bool
     */
    public static function setEmailSubscribedCallback()
    {
        if (EducationElements::userIsSubscribed()) {
            return true;
        }

        $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE);
        if (is_null($email)) {
            throw new \Exception('Invalid email');
        }

        $response = wp_remote_post(self::REMOTE_SUBSCRIBE_URL, array(
            'method'      => 'POST',
            'timeout'     => 45,
            'body'        => array('email' => $email)
        ));

        if (is_wp_error($response) || 200 !== wp_remote_retrieve_response_code($response)) {
            $error_msg = $response->get_error_code() . ': ' . $response->get_error_message();
            SnapUtil::errorLog($error_msg);
            throw new \Exception($error_msg);
        }

        return (update_user_meta(get_current_user_id(), EducationElements::DUP_EMAIL_SUBSCRIBED_OPT_KEY, true) !== false);
    }

    /**
     * Set recovery action
     *
     * @return void
     */
    public function setEmailSubscribed()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'setEmailSubscribedCallback'),
            'duplicator_email_subscribe',
            $_POST['nonce'],
            'export'
        );
    }

    /**
     * Set dismiss callout CTA callback
     *
     * @return bool
     */
    public static function dismissCalloutCTACallback()
    {
        return (update_user_meta(get_current_user_id(), EducationElements::DUP_SETTINGS_FOOTER_CALLOUT_DISMISSED, true) !== false);
    }

    /**
     * Dismiss callout CTA
     *
     * @return void
     */
    public function dismissCalloutCTA()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'dismissCalloutCTACallback'),
            'duplicator_settings_callout_cta_dismiss',
            $_POST['nonce'],
            'export'
        );
    }

    /**
     * Dismiss bottom bar callback
     *
     * @return bool
     */
    public static function dismissBottomBarCallback()
    {
        return (update_user_meta(get_current_user_id(), EducationElements::DUP_PACKAGES_BOTTOM_BAR_DISMISSED, true) !== false);
    }

    /**
     * Dismiss bottom bar
     *
     * @return void
     */
    public function dismissBottomBar()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'dismissBottomBarCallback'),
            'duplicator_packages_bottom_bar_dismiss',
            $_POST['nonce'],
            'export'
        );
    }


    /**
     * Generate OTH for connect flow
     *
     * @return void
     */
    public function generateConnectOTH()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'generateConnectOTHCallback'),
            'duplicator_generate_connect_oth',
            SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce'),
            'export'
        );
    }

    /**
     * Generate OTH for connect flow callback
     *
     * @return array
     * @throws Exception
     */
    public static function generateConnectOTHCallback()
    {
        $oth        = wp_generate_password(30, false, false);
        $hashed_oth = self::hashOth($oth);

        // Save HASHED OTH with TTL for security
        $oth_data = array(
            'token' => $hashed_oth,  // Store hashed OTH for decryption
            'created_at' => time(),
            'expires_at' => time() + (10 * MINUTE_IN_SECONDS) // 10 minute expiration
        );

        delete_option(self::OPTION_KEY_ONE_CLICK_UPGRADE_OTH);
        $ok = update_option(self::OPTION_KEY_ONE_CLICK_UPGRADE_OTH, $oth_data);

        if (!$ok) {
            throw new Exception("Problem saving security token.");
        }

        return array(
            'success' => true,
            'oth' => $hashed_oth,
            'php_version' => phpversion(),
            'wp_version' => get_bloginfo('version'),
            'redirect_url' => admin_url('admin-ajax.php?action=duplicator_lite_run_one_click_upgrade')
        );
    }


    /**
     * Returh hashed OTH
     *
     * @param string $oth OTH
     *
     * @return string Hashed OTH
     */
    protected static function hashOth($oth)
    {
        return  hash_hmac('sha512', $oth, wp_salt());
    }

    /**
     * Decrypt data using OTH-based key.
     *
     * @param string $encryptedData Base64 encoded encrypted data
     * @param string $oth           The OTH token
     *
     * @return string|false Decrypted data or false on failure
     */
    protected static function decryptData($encryptedData, $oth)
    {
        try {
            $encryption_key = substr(hash('sha256', $oth), 0, 32); // 32-byte key from OTH
            $iv             = substr($oth, 0, 16); // 16-byte IV from OTH

            $encrypted = base64_decode($encryptedData);
            return openssl_decrypt($encrypted, 'AES-256-CBC', $encryption_key, 0, $iv);
        } catch (Exception $e) {
            DUP_Log::trace("ERROR: Decryption failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Decrypt and parse encrypted package from service.
     *
     * @param string $encryptedPackage Base64 encoded encrypted package
     * @param string $oth              The OTH token
     *
     * @return array|false Parsed package data or false on failure
     */
    protected static function decryptPackage($encryptedPackage, $oth)
    {
        $decrypted = self::decryptData($encryptedPackage, $oth);

        if ($decrypted === false) {
            return false;
        }

        $package = json_decode($decrypted, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            DUP_Log::trace("ERROR: Invalid JSON in decrypted package");
            return false;
        }

        return $package;
    }


    /**
     * Enable usage stats
     *
     * @return void
     */
    public function enableUsageStats()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'enableUsageStatsCallback'),
            'duplicator_enable_usage_stats',
            SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce'),
            'manage_options'
        );
    }

    /**
     * Enable usage stats callback
     *
     * @return void
     */
    public static function enableUsageStatsCallback()
    {
        $result = true;
        if (DUP_Settings::Get('usage_tracking') !== true) {
            DUP_Settings::setUsageTracking(true);
            $result = DUP_Settings::Save();
        }

        return $result && self::setEmailSubscribedCallback();
    }


    /**
     * Accepts encrypted package from remote endpoint, after validating the OTH.
     *
     * @return void
     */
    public function oneClickUpgrade()
    {
        try {
            // Get encrypted package from service
            $encryptedPackage = sanitize_text_field($_REQUEST["package"] ?? '');

            if (empty($encryptedPackage)) {
                DUP_Log::trace("ERROR: No encrypted package received from service.");
                throw new Exception("No encrypted package received from service");
            }

            // Get OTH data for validation
            $oth_data = get_option(self::OPTION_KEY_ONE_CLICK_UPGRADE_OTH);

            if (empty($oth_data) || !is_array($oth_data)) {
                DUP_Log::trace("ERROR: Invalid OTH data structure.");
                throw new Exception("Invalid security token");
            }

            // Check TTL expiration
            if (time() > $oth_data['expires_at']) {
                DUP_Log::trace("ERROR: OTH token expired.");
                delete_option(self::OPTION_KEY_ONE_CLICK_UPGRADE_OTH);
                throw new Exception("Security token expired");
            }

            // Decrypt package using OTH
            $package = self::decryptPackage($encryptedPackage, $oth_data['token']);

            if ($package === false) {
                DUP_Log::trace("ERROR: Failed to decrypt package from service.");
                throw new Exception("Invalid encrypted data");
            }

            // Extract data from decrypted package
            $download_url = $package['download_url'] ?? '';
            $auth_token   = $package['auth_token'] ?? '';

            if (empty($download_url)) {
                DUP_Log::trace("ERROR: No download URL in decrypted package.");
                throw new Exception("No download URL provided");
            }

            // Delete OTH so it cannot be replayed (single-use)
            delete_option(self::OPTION_KEY_ONE_CLICK_UPGRADE_OTH);

            // Save authentication token for Pro to use
            if (!empty($auth_token)) {
                delete_option(self::AUTH_TOKEN_KEY_OPTION_AUTO_ACTIVE);
                update_option(self::AUTH_TOKEN_KEY_OPTION_AUTO_ACTIVE, $auth_token);
                DUP_Log::trace("Authentication token saved for Pro activation.");
            }

            // Validate download URL format
            if (!filter_var($download_url, FILTER_VALIDATE_URL)) {
                DUP_Log::trace("ERROR: Invalid download URL format: " . $download_url);
                throw new Exception("Invalid download URL format");
            }

            // Install Pro if not already installed
            if (!is_dir(WP_PLUGIN_DIR . "/duplicator-pro")) {
                DUP_Log::trace("Installing Pro using service-provided URL: " . $download_url);

                // Request filesystem credentials
                $url   = esc_url_raw(add_query_arg(array('page' => 'duplicator-settings'), admin_url('admin.php')));
                $creds = request_filesystem_credentials($url, '', false, false, null);

                if (false === $creds || ! \WP_Filesystem($creds)) {
                    wp_send_json_error(array('message' => 'File system permissions error. Please check permissions and try again.'));
                }

                // Install the plugin
                require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
                remove_action('upgrader_process_complete', array('Language_Pack_Upgrader', 'async_upgrade'), 20);

                $installer = new Plugin_Upgrader(new UpgraderSkin());
                $result    = $installer->install($download_url);

                if (is_wp_error($result)) {
                    DUP_Log::trace("ERROR: Plugin installation failed: " . $result->get_error_message());
                    throw new Exception('Plugin installation failed: ' . $result->get_error_message());
                }

                wp_cache_flush();
                $plugin_basename = $installer->plugin_info();

                if ($plugin_basename) {
                    $upgradeDir = dirname($plugin_basename);
                    if ($upgradeDir != "duplicator-pro" && !rename(WP_PLUGIN_DIR . "/" . $upgradeDir, WP_PLUGIN_DIR . "/duplicator-pro")) {
                        throw new Exception('Failed renaming plugin directory');
                    }
                } else {
                    throw new Exception('Installation of upgrade version failed');
                }
            }

            $newFolder = WP_PLUGIN_DIR . "/duplicator-pro";
            if (!is_dir($newFolder)) {
                DUP_Log::trace("ERROR: Duplicator Pro folder not found after installation");
                throw new Exception('Pro plugin installation failed - folder not created');
            }

            // Deactivate Lite FIRST (critical for avoiding conflicts)
            deactivate_plugins(DUPLICATOR_PLUGIN_PATH . "/duplicator.php");

            // Create activation URL for Pro
            $plugin          = "duplicator-pro/duplicator-pro.php";
            $pluginsAdminUrl = is_multisite() ? network_admin_url('plugins.php') : admin_url('plugins.php');
            $activateProUrl  = esc_url_raw(
                add_query_arg(
                    array(
                        'action' => 'activate',
                        'plugin' => $plugin,
                        '_wpnonce' => wp_create_nonce("activate-plugin_$plugin")
                    ),
                    $pluginsAdminUrl
                )
            );

            // Redirect to WordPress activation URL
            DUP_Log::trace("Pro installation successful. Redirecting to activation URL: " . $activateProUrl);
            wp_safe_redirect($activateProUrl);
            exit;
        } catch (Exception $e) {
            DUP_Log::trace("ERROR in oneClickUpgrade: " . $e->getMessage());

            // Add error notice and redirect to settings page
            Notice::error(
                sprintf(__('Upgrade installation failed: %s. Please try again or install manually.', 'duplicator'), $e->getMessage()),
                'one_click_upgrade_failed'
            );

            $settingsUrl = ControllersManager::getMenuLink(
                ControllersManager::SETTINGS_SUBMENU_SLUG,
                'general'
            );

            wp_safe_redirect($settingsUrl);
            exit;
        }
    }
}
                                                                                                                                                                                                                                                                                  AbstractAjaxService.php                                                                             0000644                 00000001062 15174604726 0011160 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

abstract class AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    abstract public function init();

    /**
     * Add ajax action
     *
     * @param string $tag        ajax tag name
     * @param string $methodName method name
     *
     * @return bool Always returns true
     */
    protected function addAjaxCall($tag, $methodName)
    {
        return add_action($tag, array($this, $methodName));
    }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ServicesDashboard.php                                                                               0000644                 00000003504 15174604726 0010666 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use DUP_Package;
use Duplicator\Ajax\AjaxWrapper;
use Duplicator\Views\DashboardWidget;

class ServicesDashboard extends AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init()
    {
        $this->addAjaxCall('wp_ajax_duplicator_dashboad_widget_info', 'dashboardWidgetInfo');
        $this->addAjaxCall('wp_ajax_duplicator_dismiss_recommended_plugin', 'dismissRecommendedPlugin');
    }

    /**
     * Set recovery callback
     *
     * @return array<string, mixed>
     */
    public static function dashboardWidgetInfoCallback()
    {
        $result = array(
            'isRunning' => DUP_Package::isPackageRunning(),
            'lastBackupInfo' => DashboardWidget::getLastBackupString()
        );
        return $result;
    }

    /**
     * Set recovery action
     *
     * @return void
     */
    public function dashboardWidgetInfo()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'dashboardWidgetInfoCallback'),
            'duplicator_dashboad_widget_info',
            $_POST['nonce'],
            'export'
        );
    }

    /**
     * Set dismiss recommended callback
     *
     * @return bool
     */
    public static function dismissRecommendedPluginCallback()
    {
        return (update_user_meta(get_current_user_id(), DashboardWidget::RECOMMENDED_PLUGIN_DISMISSED_OPT_KEY, true) !== false);
    }

    /**
     * Set recovery action
     *
     * @return void
     */
    public function dismissRecommendedPlugin()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'dismissRecommendedPluginCallback'),
            'duplicator_dashboad_widget_dismiss_recommended',
            $_POST['nonce'],
            'export'
        );
    }
}
                                                                                                                                                                                            ServicesExtraPlugins.php                                                                            0000644                 00000002302 15174604726 0011417 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use Duplicator\Ajax\AjaxWrapper;
use Duplicator\Utils\ExtraPlugins\ExtraPluginsMng;

class ServicesExtraPlugins extends AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init()
    {
        $this->addAjaxCall('wp_ajax_duplicator_install_extra_plugin', 'extraPluginInstall');
    }

    /**
     * Install and activate or just activate plugin
     *
     * @return string
     */
    public static function extraPluginInstallCallback()
    {
        $slug    = filter_input(INPUT_POST, 'plugin', FILTER_SANITIZE_STRING);
        $message = '';

        if (!ExtraPluginsMng::getInstance()->install($slug, $message)) {
            throw new \Exception($message);
        }

        return $message;
    }

    /**
     *  Addon plugin install action callback
     *
     * @return void
     */
    public function extraPluginInstall()
    {
        AjaxWrapper::json(
            array(__CLASS__, 'extraPluginInstallCallback'),
            'duplicator_install_extra_plugin',
            $_POST['nonce'],
            'install_plugins'
        );
    }
}
                                                                                                                                                                                                                                                                                                                              ServicesTools.php                                                                                   0000644                 00000002731 15174604726 0010100 0                                                                                                    ustar 00                                                                                                                                                                                                                                                       <?php

/**
 * @package   Duplicator
 * @copyright (c) 2022, Snap Creek LLC
 */

namespace Duplicator\Ajax;

use Duplicator\Ajax\AjaxWrapper;
use Duplicator\Libs\Snap\SnapURL;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Utils\Support\SupportToolkit;

class ServicesTools extends AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init()
    {
        $this->addAjaxCall('wp_ajax_duplicator_download_support_toolkit', 'downloadSupportToolkit');
    }

    /**
     * Function to download diagnostic data
     *
     * @return never
     */
    public function downloadSupportToolkit()
    {
        AjaxWrapper::fileDownload(
            [
                __CLASS__,
                'downloadSupportToolkitCallback',
            ],
            'duplicator_download_support_toolkit',
            SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce')
        );
    }

    /**
     * Function to create diagnostic data
     *
     * @return false|array{path:string,name:string}
     */
    public static function downloadSupportToolkitCallback()
    {
        $domain = SnapURL::wwwRemove(SnapURL::parseUrl(network_home_url(), PHP_URL_HOST));
        $result = [
            'path' => SupportToolkit::getToolkit(),
            'name' => SupportToolkit::SUPPORT_TOOLKIT_PREFIX .
                substr(sanitize_file_name($domain), 0, 12) . '_' .
                date('YmdHis') . '.zip',
        ];

        return $result;
    }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       