%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /var/www/knwn/wp-content/plugins/wpsynchro/includes/Transport/
Upload File :
Create Path :
Current File : /var/www/knwn/wp-content/plugins/wpsynchro/includes/Transport/RemoteTransport.php

<?php

namespace WPSynchro\Transport;

use WPSynchro\Installation;
use WPSynchro\Job;
use WPSynchro\Utilities\Configuration\PluginConfiguration;

/**
 * Class for handling transport of data between sites in WP Synchro
 * @since 1.3.0
 */
class RemoteTransport implements RemoteConnection
{
    public $url;
    public $args;
    public $job;
    public $installation;
    public $timer;
    public $transfer = null;
    public $send_data_as_json = false;
    public $max_requestsize = 0;
    public $blocking_request = true;
    // Specific token and encryption to use (for cases when we dont want to auto-get it from sync controller, like health check)
    public $token = null;
    public $encryption_key = null;
    // Configuration
    public $max_retries = 5;
    public $seconds_sleep_between_retries = 1;

    /**
     *  Set installation
     *  @since 1.6.0
     */
    public function setInstallation(Installation $installation = null)
    {
        if (is_object($installation)) {
            $this->installation = $installation;
        }
    }

    /**
     *  Set installation
     *  @since 1.6.0
     */
    public function setJob(Job $job = null)
    {
        if (is_object($job)) {
            $this->job = $job;
        }
    }

    /**
     *  Set no retries
     *  @since 1.6.0
     */
    public function setNoRetries()
    {
        $this->max_retries = 0;
    }

    /**
     *  Initialize request object
     *  @since 1.3.0
     */
    public function init()
    {
        // Get needed objects
        global $wpsynchro_container;
        $sync_controller = $wpsynchro_container->get("class.SynchronizeController");
        if (is_null($this->job)) {
            $this->job = $sync_controller->job;
        }
        if (is_null($this->installation)) {
            $this->installation = $sync_controller->installation;
        }

        // Get timer
        global $wpsynchro_container;
        $this->timer = $wpsynchro_container->get("class.SyncTimerList");

        // Get transfer object and setup it up
        global $wpsynchro_container;
        $this->transfer = $wpsynchro_container->get("class.Transfer");
        $this->transfer->setShouldEncrypt(true);
        $this->transfer->setShouldDeflate(true);

        // Setup WP remote post args
        $this->args = [
            'method' => 'POST',
            'redirection' => 2,
            'httpversion' => '1.0',
            'sslverify' => (isset($this->installation->verify_ssl) ? $this->installation->verify_ssl : true),
            'headers' => [
                'Content-Type' => $this->transfer->getContentType(),
            ],
        ];

        // Set basic authentication if needed
        if (isset($this->installation->connection_type) && $this->installation->connection_type === 'basicauth') {
            $this->setBasicAuthentication($this->installation->basic_auth_username, $this->installation->basic_auth_password);
        }
    }

    /**
     *  Set URL on request
     *  @since 1.3.0
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     *  Set max size on request
     *  @since 1.3.0
     */
    public function setMaxRequestSize($maxsize)
    {
        $this->max_requestsize = $maxsize;
    }

    /**
     *  Add data to request
     *  @since 1.3.0
     */
    public function setDataObject($object)
    {
        return $this->transfer->setDataObject($object);
    }

    /**
     *  Set token on request
     *  @since 1.6.0
     */
    public function setToken($token)
    {
        $this->token = $token;
    }

    /**
     *  Set encryption key on request
     *  @since 1.6.0
     */
    public function setEncryptionKey($key)
    {
        $this->encryption_key = $key;
    }

    /**
     *  Send data as JSON
     *  @since 1.3.0
     */
    public function setSendDataAsJSON()
    {
        $this->send_data_as_json = true;
        $this->args["headers"]["Content-Type"] = "application/json; charset=utf-8";
    }

    /**
     *  Set basic authentication on request
     *  @since 1.6.0
     */
    public function setBasicAuthentication($username, $password)
    {
        $this->args["headers"]["Authorization"] = "Basic " . base64_encode($username . ":" . $password);
    }

    /**
     *  Set request as non-blocking
     *  @since 1.6.0
     */
    public function setNonBlocking()
    {
        $this->args["blocking"] = false;
        $this->args["timeout"] = 0;
        $this->blocking_request = false;
    }

    /**
     *  Add file to request
     *  @since 1.3.0
     */
    public function addFiledata(\WPSynchro\Transport\TransferFile $file)
    {
        $current_request_size = $this->transfer->getRequestSize();
        $overhead_per_file = $this->transfer->getFileOverhead();

        // Check if there is more space
        if ($current_request_size + ($overhead_per_file * 2) > $this->max_requestsize) {
            return false;
        }

        global $wpsynchro_container;
        $logger = $wpsynchro_container->get("class.Logger");

        if (!file_exists($file->filename) || !is_readable($file->filename)) {
            $file->is_error = true;
            return true;
        }

        // Load file data into object, or part of it, if too big for remaining space
        $filesize = filesize($file->filename);

        if (($filesize + $current_request_size + $overhead_per_file) > $this->max_requestsize || $file->is_partial) {
            $logger->log("DEBUG", "No space for entire file, will chunk it: " . $file->filename);
            // Check if file is under mu-plugins, which causes troubles when being chunked
            if (strpos($file->filename, 'mu-plugins') !== false) {
                $logger->log("DEBUG", "File under mu-plugins should not be chunked, so skipping. File: " . $file->filename);
                $file->error_msg = "One of the mu-plugin files could not be contained in one request, so we have skipped it. A partial mu-plugin file would take down the site. You need to copy it manually or increase 'post_max_size' in PHP settings. The file is: " . $file->filename;
                $file->is_error = true;
                return true;
            }

            // Partial
            if (($current_request_size + $overhead_per_file) < $this->max_requestsize) {
                // Check if there is room for any more data
                $available_space_for_chunk = $this->max_requestsize - ($current_request_size + $overhead_per_file);
                if ($file->is_partial) {
                    // Already chunked, so continue from last position
                    $already_transferred_bytes = $file->partial_start;
                    $logger->log("DEBUG", "Already chunked, start position: " . $already_transferred_bytes . " and available: " . $available_space_for_chunk);
                    $file->data = file_get_contents($file->filename, false, null, $already_transferred_bytes, $available_space_for_chunk);
                } else {
                    // First read of chunked part, so start from 0
                    $logger->log("DEBUG", "First chunk, start position: 0 and available: " . $available_space_for_chunk);
                    if ($file->is_dir) {
                        // dir
                        $file->data = "";
                    } else {
                        // filename
                        $file->data = file_get_contents($file->filename, false, null, 0, $available_space_for_chunk);
                    }

                    $file->is_partial = true;
                    $file->partial_start = 0;
                }
            }
        } else {
            // Check if file
            if (!$file->is_dir) {
                // File can fit
                $logger->log("DEBUG", "File can be contained fully in request: " . $file->filename);
                $file->data = file_get_contents($file->filename);
            }
        }

        // Add file to transfer object
        $this->transfer->addFiledata($file);

        // Remove data again, as it is copied in transfer object and we dont want to send it in json also
        $file->data = "";

        return true;
    }

    /**
     *  Handle all POST requests to REST services
     *  @since 1.3.0
     */
    public function remotePOST()
    {
        $wpremoteresult = new RemoteTransportResult();

        if (isset($this->job->errors) && count($this->job->errors) > 0) {
            return $wpremoteresult;
        }

        // Adjust request
        $this->addTokenToRequest();
        if ($this->blocking_request === true) {
            $this->args["timeout"] = ceil($this->timer->getRemainingSyncTime() + PluginConfiguration::factory()->getRequestTimeoutMargin());
        }

        if ($this->send_data_as_json) {
            $this->args["body"] = json_encode($this->transfer->getDataObject());
        } else {
            $this->args["body"] = $this->transfer->getDataString();
        }

        // Handle expect header, which is a weird performance upgrade
        $this->args["headers"]['Expect'] = '';

        // Do request
        $result = $this->doRequest($wpremoteresult);
        if (!$result) {
            // Retry if there is time for that
            $this->handleRetries("POST", $wpremoteresult);
        }

        // Save errors to job
        if (isset($this->job->errors)) {
            $this->job->errors = array_merge($this->job->errors, $wpremoteresult->getErrors());
        }
        if (isset($this->job->warnings)) {
            $this->job->warnings = array_merge($this->job->warnings, $wpremoteresult->getWarnings());
        }

        $wpremoteresult->writeMessagesToLog();

        return $wpremoteresult;
    }

    /**
     *  Check which token, if any, to add to request
     *  @since 1.3.0
     */
    public function addTokenToRequest()
    {

        // Check if token and encryption was set manually
        if (!is_null($this->token) && !is_null($this->encryption_key)) {
            $this->url = add_query_arg('token', $this->token, $this->url);
            $this->transfer->setEncryptionKey($this->encryption_key);
            return;
        }

        // If to get it from job object, check that data is present
        if (!isset($this->job->from_token) || strlen($this->job->from_token) == 0) {
            return;
        }
        if (!isset($this->job->to_token) || strlen($this->job->to_token) == 0) {
            return;
        }

        // Check if it is local or remote and set appropriate encryptionkey on transfer object
        if (strpos($this->url, $this->job->from_rest_base_url) !== false) {
            $this->token = $this->job->from_token;
            $this->encryption_key = $this->job->from_accesskey;
        } else {
            $this->token = $this->job->to_token;
            $this->encryption_key = $this->job->to_accesskey;
        }
        $this->transfer->setEncryptionKey($this->encryption_key);

        if (strlen($this->token) > 0) {
            // If token is set, add it to url
            $this->url = add_query_arg('token', $this->token, $this->url);
        }
    }

    /**
     *  Handle retries of HTTP requests
     *  @since 1.3.0
     */
    public function handleRetries($type, &$wpremoteresult)
    {
        $min_time_to_retry = 3; // seconds

        $retries = 0;
        $wpremoteresult->debugs[] = sprintf(__("Entering retry with remaining time %f", "wpsynchro"), $this->timer->getRemainingSyncTime());
        // Unexpected response, so retry
        while ($retries < $this->max_retries) {

            // Check if it is possible within timeframe
            if (!$this->timer->shouldContinueWithLastrunTime($min_time_to_retry)) {
                $wpremoteresult->debugs[] = sprintf(__("Aborting retries because we dont have enough time - Tried %d times ", "wpsynchro"), $retries);
                break;
            }
            sleep($this->seconds_sleep_between_retries);

            // Try again
            if ($this->blocking_request === true) {
                $this->args["timeout"] = ceil($this->timer->getRemainingSyncTime());
            }

            // Do request
            $result = $this->doRequest($wpremoteresult);
            if ($result) {
                return;
            } else {
                $retries++;
                $wpremoteresult->debugs[] = sprintf(__("Got error connecting to service %s - Retry %d of %d ", "wpsynchro"), $this->url, $retries, $this->max_retries);
            }
        }
        $parsed_url = parse_url($this->url);
        $parsed_host = "unknown";
        if (isset($parsed_url["host"])) {
            $parsed_host = $parsed_url["host"];
        }
        $wpremoteresult->errors[] = sprintf(__("Could not connect to %s REST service (HTTP statuscode: %d)", "wpsynchro"), $parsed_host, $wpremoteresult->statuscode);
    }

    /**
     *  Do actual request and handle error scenarios
     */
    private function doRequest($wpremoteresult)
    {
        $response = wp_remote_post($this->url, $this->args);
        if ($this->blocking_request === false) {
            return true;
        }
        if (is_wp_error($response)) {
            $errormsg = $response->get_error_message();
            $parsedurl = parse_url($this->url);
            if (strpos($errormsg, "cURL error 60") > -1) {
                $this->job->errors[] = sprintf(__("SSL certificate is not valid or self-signed on host %s. To allow non-valid SSL certificates when running a synchronization, make sure it is set to allowed.", "wpsynchro"), $parsedurl['host']);
            } else {
                $this->job->errors[] = sprintf(__("REST error - Can not reach REST service on host %s. Error message: %s", "wpsynchro"), $parsedurl['host'], $errormsg);
            }
            return false;
        } else {
            $statuscode = wp_remote_retrieve_response_code($response);
            $wpremoteresult->parseResponse($response, $this->url, $this->args, $this->encryption_key);

            if ($statuscode === 200) {
                return true;
            } else {
                $wpremoteresult->debugs[] = "Call to REST service at url " . $this->url . " failed with HTTP error code: " . $statuscode . " - ";
                return false;
            }
        }
    }
}

Zerion Mini Shell 1.0