wordpress/php-fpm/wordpress_files/plugins/wp-cerber/nexus/cerber-nexus-slave.php
2020-05-22 01:40:23 +00:00

602 lines
17 KiB
PHP

<?php
/*
Copyright (C) 2015-20 CERBER TECH INC., https://cerber.tech
Copyright (C) 2015-20 CERBER TECH INC., https://wpcerber.com
Licenced under the GNU GPL.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
*========================================================================*
| |
| ATTENTION! Do not change or edit this file! |
| |
*========================================================================*
*/
if ( ! defined( 'WPINC' ) ) { exit; }
require_once( dirname( cerber_plugin_file() ) . '/cerber-maintenance.php' );
add_action( 'plugins_loaded', function () {
if ( ! $key = cerber_get_get( 'cerber_magic_key', '[\d\w\-_]+' ) ) {
return;
}
if ( ! $data = cerber_get_set( '_the_key_' . $key ) ) {
cerber_404_page();
}
@ini_set( 'display_errors', 0 );
cerber_delete_set( '_the_key_' . $key );
cerber_load_admin_code();
crb_admin_download_file( $data['query']['type'], $data['query'] );
cerber_404_page();
}, 0 );
class CRB_Master {
public $page;
public $tab;
public $base;
public $is_post;
public $seal;
public $type;
public $get_params = array(); // Unsanitized, as is
public $payload;
public $action;
public $screen;
public $at_site;
public $locale;
public $error;
final function __construct() {
global $crb_assets_url, $crb_ajax_loader;
$fields = nexus_get_fields();
if ( ! $payload = cerber_get_post( $fields[1] ) ) {
$this->error = new WP_Error( 'nexus_format_error', 'Invalid request: master request malformed' );
return;
}
$request = json_decode( stripslashes( $payload ), true );
if ( JSON_ERROR_NONE != json_last_error() ) {
$this->error = new WP_Error( 'json_error', 'Unable to parse JSON: ' . json_last_error_msg() );
return;
}
array_walk_recursive( $request, function ( &$e ) {
$e = str_replace( array( '<br/>' ), "\n", $e ); // restore new lines after json_decode
} );
$this->seal = $request['seal'];
$this->base = rtrim( $request['base'], '/' ) . '/';
$this->get_params = $request['params'];
$this->payload = $request['payload'];
$this->type = $request['payload']['type'];
$this->page = preg_replace( '/[^\w\-]/i', '', crb_array_get( $request, 'page' ) );
$this->tab = preg_replace( '/[^\w\-]/i', '', crb_array_get( $request, 'tab' ) );
$this->at_site = crb_array_get( $request, 'at_site' );
$this->screen = crb_array_get( $request, 'screen' );
$this->is_post = ( ! empty( $request['is_post'] ) ) ? true : false;
if ( ! $this->locale = crb_array_get( $request, 'master_locale' ) ) {
if ( ! $this->locale = get_site_option( 'WPLANG' ) ) {
$this->locale = 'en_US';
}
}
$crb_assets_url = $request['assets'];
$crb_ajax_loader = $crb_assets_url . 'ajax-loader.gif';
if ( $this->type == 'ajax' ) {
if ( ! $this->action = crb_array_get( $request['params'], 'action' ) ) {
$this->action = $this->get_post_fields( 'action' );
}
$this->action = preg_replace( '/[^\w\-]/i', '', (string) $this->action );
}
}
/**
* @param integer|string $key
* @param mixed $default
* @param $pattern string REGEX pattern for value validation, UTF is not supported
*
* @return mixed
*/
final function get_post_fields( $key = null, $default = false, $pattern = '' ) {
if ( ! empty( $this->payload['data']['post'] ) ) {
if ( $key ) {
return crb_array_get( $this->payload['data']['post'], $key, $default, $pattern );
}
return $this->payload['data']['post'];
}
if ( $key ) {
return false;
}
return array();
}
}
/**
* @return CRB_Master
*/
function nexus_request_data() {
static $crb_master;
if ( ! is_object( $crb_master ) ) {
$crb_master = new CRB_Master();
}
return $crb_master;
}
function nexus_slave_process() {
if ( ! nexus_is_valid_request() ) {
return;
}
@ini_set( 'display_errors', 0 );
@ignore_user_abort( true );
crb_raise_limits();
cerber_update_set( 'processing_master_request', 1, 0, false, time() + 120 );
nexus_diag_log( 'Parsing request...' );
$crb_master = nexus_request_data();
if ( is_wp_error( $crb_master->error ) ) {
nexus_diag_log( 'ERROR: ' . $crb_master->error->get_error_message() );
exit;
}
nexus_diag_log( 'Request is parsed, generating response...' );
add_filter( 'plugin_locale', function () {
return nexus_request_data()->locale;
}, 9999 );
$use_eng = false;
if ( nexus_request_data()->locale == 'en_US' ) {
$use_eng = true;
// We do not load any translation files
add_filter( 'override_load_textdomain', function ( $val, $domain, $mofile ) {
return true;
}, 9999, 3 );
}
if ( ! $use_eng ) {
$r = load_plugin_textdomain( 'wp-cerber', false, 'wp-cerber/languages' );
/*if ( ! $r ) {
nexus_diag_log( 'Unable to load plugin localization files ' . (string) nexus_request_data()->locale );
}*/
}
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
require_once( ABSPATH . 'wp-admin/includes/template.php' );
require_once( ABSPATH . WPINC . '/pluggable.php' );
require_once( ABSPATH . WPINC . '/vars.php' );
cerber_load_admin_code();
$response = nexus_prepare_responce();
if ( is_wp_error( $response ) ) {
$m = __( 'ERROR:', 'wp-cerber' ) . ' ' . $response->get_error_message();
nexus_diag_log( $m );
$error = array( $response->get_error_code(), $response->get_error_message() );
$response = array( 'error' => $error );
}
nexus_diag_log( 'Now sending response to the master...' );
$result = nexus_net_send_responce( $response );
if ( is_wp_error( $result ) ) {
nexus_diag_log( __( 'ERROR:', 'wp-cerber' ) . ' ' . $result->get_error_message() );
}
cerber_delete_set( 'processing_master_request' );
nexus_diag_log( '=== SLAVE HAS FINISHED ===' );
exit;
}
/**
* Avoid simultaneous requests from the master(s)
*
* @return bool
*
*/
function nexus_is_processing() {
return ( cerber_get_set( 'processing_master_request' ) ) ? true : false;
}
/*
function nexus_render_admin_page_1(){
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
require_once( ABSPATH . '/wp-admin/includes/template.php' );
require_once( ABSPATH . '/wp-includes/pluggable.php' );
cerber_load_admin_code();
$page_id = 'cerber-recaptcha';
//$tab = 'captcha';
if ( empty( $tab ) ) {
$tab = $page_id;
}
cerber_wp_settings_setup( cerber_get_setting_id( $tab ) );
// Render inner html
$page = cerber_get_admin_page_config( $page_id );
call_user_func( $page['callback'], '' );
exit;
}*/
function nexus_render_admin_page( $page, $tab ) {
$id = ( empty( $tab ) ) ? $page : $tab;
cerber_wp_settings_setup( cerber_get_setting_id( $id ) );
cerber_admin_init(); // TODO: remove, old way
ob_start();
cerber_render_admin_page( $page, $tab ); // Render whole html
return ob_get_clean();
}
function nexus_parse_request() {
$fields = nexus_get_fields();
if ( ! $payload = cerber_get_post( $fields[1] ) ) {
return new WP_Error( 'nexus_format_error', 'Invalid request: master request malformed' );
}
$request = json_decode( stripslashes( $payload ), true );
if ( JSON_ERROR_NONE != json_last_error() ) {
return new WP_Error( 'json_error', 'Unable to parse JSON: ' . json_last_error_msg() );
}
return $request;
}
function nexus_prepare_responce() {
$master = nexus_request_data();
nexus_diag_log( 'Type: ' . $master->type );
if ( ! nexus_is_granted() ) {
return new WP_Error( 'not_allowed', 'Operation is not allowed in this context' );
}
$result = '';
switch ( $master->type ) {
case 'get_page':
CRB_Addons::load_active();
return array(
'html' => nexus_render_admin_page( $master->page, $master->tab ),
//'o' => get_option( 'gmt_offset' ),
//'z' => get_option( 'timezone_string' ),
);
break;
case 'submit':
CRB_Addons::load_active();
if ( $master->get_post_fields( 'option_page' ) ) { // True WP setting page
return nexus_process_wp_settings_form( $master->get_post_fields() );
}
else {
return cerber_admin_request( $master->is_post );
}
// A new way: processing + follow up rendering in a single request
//return nexus_render_admin_page( $master->page, $master->tab );
break;
case 'manage':
return cerber_admin_request( $master->is_post );
break;
case 'hello':
//case 'checkup':
return array( 'numbers' => nexus_get_numbers() );
break;
case 'ping':
return array( 'pong' );
break;
case 'sw_upgrade':
$result = nexus_sw_upgrade();
$result ['numbers'] = nexus_get_numbers();
if ( ! $result['updates'] ) {
//nexus_diag_log( 'No updates are available' );
}
break;
case 'ajax':
if ( ! crb_admin_allowed_ajax( $master->action ) ) {
return new WP_Error( 'unknown_ajax', 'The action ' . $master->action . ' is not supported' );
}
global $nexus_doing_ajax;
$nexus_doing_ajax = true;
ob_start();
do_action( 'wp_ajax_' . $master->action );
$nexus_doing_ajax = false;
nexus_diag_log( 'AJAX ' . $master->action . ' completed' );
return ob_get_clean();
break;
default:
return new WP_Error( 'unknown_request', 'This type of request is not supported' );
break;
}
return $result;
}
function nexus_sw_upgrade() {
$ret = array( 'updates' => 0, 'completed' => 1, 'results' => array() );
if ( nexus_is_processing() ) {
$ret['completed'] = 0;
$ret['wait'] = cerber_get_remote_ip();
return $ret;
}
$list = crb_array_get( nexus_request_data()->payload, 'list' );
switch ( nexus_request_data()->payload['sw_type'] ) {
case 'plugins':
if ( empty( $list ) ) {
// Upgrade all
$to_update = array();
$active = get_option( 'active_plugins' );
if ( ! $plugins = get_site_transient( 'update_plugins' ) ) {
wp_update_plugins();
$plugins = get_site_transient( 'update_plugins' );
}
if ( isset( $plugins->response ) ) {
$to_update = array_intersect( $active, array_keys( $plugins->response ) );
}
nexus_diag_log( 'Total active plugins to update: ' . count( $to_update ) );
if ( $done = cerber_get_set( 'plugins_done' ) ) { // Upgraded in the current bulk operation
$to_do = array_diff( $to_update, $done );
}
else {
$done = array();
$to_do = $to_update;
}
if ( ! empty( $to_do ) ) {
$run_now = array_shift( $to_do );
$done[] = $run_now;
$list = array( $run_now );
}
if ( ! empty( $to_do ) ) {
$ret['completed'] = 0; // Something left
cerber_update_set( 'plugins_done', $done, 0, true, time() + 300 * count( $to_do ) );
}
else {
cerber_delete_set( 'plugins_done' );
}
}
if ( ! empty( $list ) && is_array( $list ) ) {
foreach ( $list as $obj ) {
$ret['results'][ $obj ] = cerber_update_plugin( $obj );
}
}
break;
}
nexus_diag_log( cerber_flat_results( $ret['results'] ) );
return $ret;
}
/**
* Process forms generated by WP Settings API
*
* @param $form array WP Settings form fields
*
* @return string|WP_Error
*/
function nexus_process_wp_settings_form( $form ) {
if ( ! $page = crb_array_get( $form, 'option_page' ) ) {
return new WP_Error( 'unknown_option', 'Unable to identify settings page' );
}
if ( ! wp_verify_nonce( crb_array_get( $form, '_wpnonce' ), $page . '-options' ) ) {
return new WP_Error( 'nonce_failed', 'Nonce verification failed' );
}
$wp_option = 'cerber-' . cerber_get_wp_option_id( $page );
if ( ! isset( $form[ $wp_option ] ) ) {
return new WP_Error( 'no_value', 'Setting fields are not set' );
}
$new_values = crb_array_get( $form, $wp_option );
nexus_diag_log( 'Updating ' . $wp_option . ' option' );
cerber_update_site_option( $wp_option, $new_values );
cerber_admin_message( __( 'Settings updated', 'wp-cerber' ) );
return '';
}
/**
* @param string|array $payload
*
* @return bool|WP_Error
*/
function nexus_net_send_responce( $payload ) {
$ret = true;
$role = nexus_get_role_data();
if ( is_array( $payload ) ) {
$p = json_encode( $payload, JSON_UNESCAPED_UNICODE ); // 8.0.5
}
elseif ( is_scalar( $payload ) ) {
$p = (string) $payload;
}
else {
$p = '';
$ret = new WP_Error( 'wrong_type', 'Unsupported slave data type' );
}
$processing = microtime( true ) - cerber_request_time();
$hash = hash( 'sha512', $role['slave']['nx_echo'] . sha1( $p ) );
$response = json_encode( array(
'payload' => $payload,
'extra' => array(
'versions' => array( CERBER_VER, cerber_get_wp_version(), PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, PHP_OS, lab_lab( 2 ) )
),
'echo' => $hash,
'p_time' => $processing,
'scheme' => 2 // 8.0.5
), JSON_UNESCAPED_UNICODE );
if ( JSON_ERROR_NONE != json_last_error() ) {
$response = 'Unable to encode payload. JSON error.';
$ret = new WP_Error( 'json_error', 'Unable to encode JSON: ' . json_last_error_msg() );
}
echo $response; // To master
return $ret;
}
function nexus_is_granted( $type = null ) {
$acs = crb_get_settings( 'slave_access' );
$lab = lab_lab();
if ( $acs >= 8 ) {
return false;
}
if ( $acs == 2 ) {
if ( $lab ) {
return true;
}
}
// RO mode
if ( ! $type ) {
$type = nexus_request_data()->type;
}
if ( in_array( $type, array( 'get_page', 'hello', 'ping', 'sw_upgrade' ) ) ) {
return true;
}
if ( $type == 'submit' ) {
if ( crb_get_post_fields( 'cerber_license' ) ) {
return true;
}
return false;
}
if ( $type == 'ajax' ) {
$action = nexus_request_data()->action;
if ( ! crb_admin_allowed_ajax( $action ) ) {
return false;
}
if ( in_array( $action, array( 'cerber_scan_control', 'cerber_view_file' ) ) ) {
return true;
}
if ( $action == 'cerber_ajax' ) {
$fields = crb_get_request_fields();
if ( empty( $fields['acl_delete'] ) ) {
return true;
}
}
}
return false;
}
function nexus_get_numbers() {
$numbers = array();
$active_plugins = get_option( 'active_plugins' );
// see wp_get_update_data();
$updates = array( 'plugins' => 0, 'themes' => 0, 'wp' => 0, 'translations' => 0 );
$pl_updates = get_site_transient( 'update_plugins' );
if ( ! $pl_updates || ( $pl_updates->last_checked < ( time() - 7200 ) ) ) {
delete_site_transient( 'update_plugins' );
wp_update_plugins();
$pl_updates = get_site_transient( 'update_plugins' );
}
if ( ! empty( $pl_updates->response ) ) {
$updates['plugins'] = count( array_intersect( $active_plugins, array_keys( $pl_updates->response ) ) );
}
include_once( ABSPATH . 'wp-admin/includes/update.php' );
if ( function_exists( 'get_core_updates' ) ) {
$wp = get_core_updates( array( 'dismissed' => false ) );
if ( ! empty( $wp ) ) {
$updates['wp'] = 1;
}
}
$scan = array();
if ( ( $last_scan = cerber_get_scan() ) && $last_scan['finished'] ) {
$scan['finished'] = $last_scan['finished'];
$scan['numbers'] = $last_scan['numbers'];
}
$numbers['updates'] = $updates;
$numbers['scan'] = $scan;
// New
$list = array();
if ( ! empty( $pl_updates->response ) ) {
$list = array_map( function ( $e ) {
//$ret = (array) $e;
$ret = array_map( function ( $e ) {
return ( is_object( $e ) ) ? (array) $e : $e;
}, (array) $e );
return $ret;
}, $pl_updates->response );
}
$numbers['pl_updates'] = $list;
$numbers['active'] = $active_plugins;
$numbers['plugins'] = get_plugins();
$numbers['themes'] = crb_get_themes();
$numbers['gmt'] = get_option( 'gmt_offset' );
$numbers['tz'] = get_option( 'timezone_string' );
return $numbers;
}
// We have to use our own "user id"
add_filter( 'nonce_user_logged_out', function ( $uid, $action ) {
if ( ! nexus_is_valid_request() ) {
return $uid;
}
return PHP_INT_MAX;
}, 10, 2 );