Last active
March 9, 2023 23:07
-
-
Save mcascardi/eda74bab1b22c5de08dbddf9a3b735f5 to your computer and use it in GitHub Desktop.
Customized version of cf_deploy_client class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class cf_deploy_client extends cfd_common { | |
protected $use_compression = CF_DEPLOY_USE_COMPRESSION; | |
public function __construct() { | |
$this->options = maybe_unserialize(get_option(CF_DEPLOY_SETTINGS, array())); | |
} | |
/** | |
* Generate a sekret key for request validation | |
* | |
* - The request args are first sorted by key to ensure that they're always in the same order | |
* for key generation. | |
* - The query vars are converted to an http request string | |
* - The user, method and query string are concatenated | |
* - The concatenated string is then hashed with sha1 | |
* | |
* The sending server generates a hash by using the receiving server's auth key | |
* The receiving server should be able to replicate the result by using its own auth key | |
* | |
* @param string $auth_key - the receiving server's authentication key | |
* @param string $method | |
* @param array $args | |
* @param string $user | |
* @return string | |
*/ | |
public function sekret($auth_key, $method, $args, $user) { | |
ksort($args); | |
$query_string = $user.$method.http_build_query($args); | |
return hash_hmac('sha1', $query_string, $auth_key); | |
} | |
// Encoding & compression | |
protected function encode($data) { | |
if ($this->use_compression) { | |
$data = $this->compress($data); | |
} | |
$data = base64_encode($data); | |
return $data; | |
} | |
protected function decode($data) { | |
$data = base64_decode($data); | |
if ($this->use_compression) { | |
$data = $this->uncompress($data); | |
} | |
return $data; | |
} | |
protected function compress($data) { | |
return gzcompress($data); | |
} | |
protected function uncompress($data) { | |
return gzuncompress($data); | |
} | |
// send | |
/** | |
* Build data for XML-RPC Request & hand off to query function for actual send | |
* Builds validation hash of $params['args'] for quick integrity check on remote end | |
* | |
* @param array $params | |
* @return object cfd_message | |
*/ | |
function send($params) { | |
if (empty($params['server']) || empty($params['auth_key'])) { | |
$errors = array(); | |
if (empty($params['server'])) { | |
$errors[] = '<p>'.__('<b>No remote server defined.</b> A remote server address should be defined in the <a href="' . admin_url("admin.php?page=cf-ramp-settings") . '">RAMP Settings</a> to continue.', 'cf-deploy').'</p>'; | |
} | |
if (empty($params['auth_key'])) { | |
$errors[] = '<p>'.__('<b>No remote server auth key defined.</b> A remote server auth key should be defined in the <a href="' . admin_url("admin.php?page=cf-ramp-settings") . '">RAMP Settings</a> to continue.', 'cf-deploy').'</p>'; | |
} | |
return new cfd_message(array( | |
'success' => false, | |
'type' => 'config-error', | |
'message' => '<div class="cfd-message error">'.implode('', $errors).'</div>' | |
)); | |
} | |
$current_user = wp_get_current_user(); | |
$send_args = array( | |
'cfd.receive', // listener | |
'1', // blog_id, leave for future expansion | |
$current_user->user_login, // username for capabilities | |
'omgtheykilledkenny!', // just like it says... | |
$params['method'], // remote method to call | |
$this->encode(serialize($params['args'])), // args | |
$this->sekret($params['auth_key'], $params['method'], $params['args'], $current_user->user_login), // sekret | |
md5(serialize($params['args'])) // cheap way to help validate complete transmission of data | |
); | |
cfd_tmp_dbg('cf-data-sent.txt', array_slice($send_args, 1), 'print'); | |
try { | |
$response = $this->query($params['server'], $send_args); | |
} | |
catch(Exception $e) { | |
$ret = array( | |
'success' => false, | |
'response' => 'Error ('.$e->getCode().'): '.$e->getMessage() | |
); | |
} | |
$response['response'] = unserialize($this->decode($response['response'])); | |
if (is_array($response['response']) && !empty($response['response']['is_cfd_message'])) { | |
$ret = new cfd_message($response['response']); | |
} | |
elseif ($response['response'] instanceof IXR_Error) { | |
$ret = new cfd_message(array( | |
'success' => false, | |
'type' => 'ixr-communication-error', | |
'message' => '<div class="cfd-message error"><p>'.$response['response']->message.'</p></div>' | |
)); | |
} | |
else { | |
// try a json decode on the response, if so, process response as message | |
$_ret = false; | |
if (is_scalar($response['response'])) { | |
$_ret = json_decode($response['response'], true); | |
} | |
elseif ($response['response'] instanceof cfd_message) { | |
return $response['response']; | |
} | |
if (!empty($_ret)) { | |
if (is_array($_ret)) { | |
$ret = new cfd_message($_ret); | |
} | |
} | |
else { | |
$ret = new cfd_message(array( | |
'success' => $response['success'], | |
'type' => $params['method'].'-response', | |
'message' => $response['response'] | |
)); | |
} | |
} | |
return $ret; | |
} | |
/** | |
* Perform the remove XML-RPC request | |
* | |
* @param string $server | |
* @param array $params | |
* @return array | |
*/ | |
public function query($server, $params) { | |
include_once(ABSPATH.WPINC.'/class-IXR.php'); | |
include_once(ABSPATH.WPINC.'/class-wp-http-ixr-client.php'); | |
$rpc = @new WP_HTTP_IXR_Client(trailingslashit($server).'xmlrpc.php'); | |
$this->add_ssl_verify_bypass(); | |
$status = call_user_func_array(array($rpc, 'query'), $params); | |
$this->remove_ssl_verify_bypass(); | |
if( !$status ) { | |
$success = false; | |
$response = $this->encode(serialize('<div class="error"><p><strong>Error ('.$rpc->getErrorCode().'):</strong> '.$rpc->getErrorMessage().' - on host: '.$server.'</p></div>')); | |
} | |
else { | |
$success = true; | |
$response = $rpc->getResponse(); | |
} | |
return compact('success', 'response'); | |
} | |
// receive | |
/** | |
* Receive an IXR message | |
* | |
* @param array $args | |
* @return mixed | |
*/ | |
public function receive($args) { | |
$args[4] = unserialize($this->decode($args[4])); | |
try { | |
$params = $this->parse_request($args); | |
if ($args[3] == 'import_batch_open') { | |
cfd_tmp_dbg('import.txt', '', 'print'); | |
} | |
elseif($args[3] == 'import_batch' || $args[3] == 'import_batch_extra') { | |
cfd_tmp_dbg('import.txt', $params, 'print', true); | |
} | |
if (!($params instanceof IXR_Error)) { | |
extract($params); | |
$ret = $this->$method($args); | |
} | |
else { | |
$ret = $params; | |
} | |
} | |
catch(cfd_xmlrpc_exception $e) { | |
$ret = $e->getIXRError(); | |
} | |
catch(Exception $e) { | |
$ret = new IXR_Error($e->getCode(), $e->getMessage()); | |
} | |
cfd_tmp_dbg('receive-result.txt', $ret, 'print'); | |
return $this->encode(serialize($ret)); | |
} | |
/** | |
* Monster validation routine prior to accepting any data | |
* | |
* @param array $args | |
* @return mixed | |
*/ | |
public function parse_request($params) { | |
cfd_tmp_dbg('cf-data-received.txt', $params, 'print'); | |
list($blog_id, $username, $auth_key, $method, $args, $sekret, $val_hash) = $params; | |
# sekret key validation | |
if($sekret != $this->sekret($this->options['auth_key'], $method, $args, $username)) { | |
return new IXR_Error('401', __('Unauthorized: key match failure', 'cf-deploy')); | |
} | |
# key validation | |
if (!method_exists($this, $method)) { | |
return new IXR_Error('400', sprintf(__('Invalid Request: method `%s` does not exist', 'cf-deploy'), $method)); | |
} | |
elseif (empty($this->options['auth_key'])) { | |
return new IXR_Error(401, __('Unauthorized: auth key not configured', 'cf-deploy')); | |
} | |
# hash validation, cheap way to ensure that the data safely made it from there to here intact | |
if (md5(serialize($args)) != $val_hash) { | |
cfd_tmp_dbg('cf-data-received.txt', PHP_EOL.PHP_EOL.'---- calculated local hash: '.md5(serialize($args)), '', true); | |
return new IXR_Error('400', __('Validation hash mismatch', 'cf-deploy')); | |
} | |
# user validation | |
if (!$user = get_user_by('login', $username)) { | |
cfd_tmp_dbg('cf-data-received.txt', PHP_EOL.PHP_EOL.'---- local get_user_by(login): '.PHP_EOL.PHP_EOL.print_r($user, true), '', true); | |
return new IXR_Error(401, __('Invalid Username', 'cf-deploy')); | |
} | |
$user = new WP_User($user->ID); | |
if (!$user->has_cap(apply_filters('cf-deploy-user-permissions', CF_DEPLOY_CAPABILITIES))) { | |
cfd_tmp_dbg('/tmp/cf-data-received.txt', PHP_EOL.PHP_EOL.'---- local user object: '.PHP_EOL.PHP_EOL.print_r($user, true), '', true); | |
return new IXR_Error(401, __('Unauthorized: not allowed', 'cf-deploy')); | |
} | |
// user is ok, set as current user | |
wp_set_current_user($user->ID, $user->user_login); | |
return compact('args', 'method', 'blog_id'); | |
} | |
// std test func | |
/** | |
* Test communications back to calling server | |
* Will fail if | |
* - can't load test file from remote server (url passed in args) | |
* - can't modify the memory limit on this server | |
* | |
* @see cf_deploy_admin::ajax_test_comms_settings() for calling method | |
* @param string $args | |
* @return void | |
*/ | |
protected function ixr_comms_test($args) { | |
$message = new cfd_message(array( | |
'success' => true, | |
'message' => '', | |
'type' => 'comms-test-message' | |
)); | |
// test memory limit modifications | |
$old_memory_limit = ini_get('memory_limit'); | |
@ini_set('memory_limit', '512M'); | |
if(ini_get('memory_limit') != '512M') { | |
$message->success = false; | |
$message->message .= '<p>'.sprintf(__('Could not modify ram limit on the server "%s". RAMP requires the ability to raise the ram limit during batch operations.', 'cf-deploy'), get_bloginfo('url')).'<p>'; | |
} | |
else { | |
ini_set('memory_limit', $old_memory_limit); | |
} | |
// test communications with calling server (for image pull & the like) | |
$this->add_ssl_verify_bypass(); | |
$f = @wp_remote_get( | |
esc_url($args['calling_from']), | |
array( | |
'reject_unsafe_urls' => false, | |
)); | |
$this->remove_ssl_verify_bypass(); | |
if (is_wp_error($f)) { | |
$message->success = false; | |
$message->message .= '<p>'.sprintf(__('Remote server could not reach this server (%s). Status: %s', 'cf-deploy'), esc_url($args['calling_from']), $f->get_error_message()).'</p>'; | |
} | |
elseif ($f['response']['code'] >= 400) { | |
$message->success = false; | |
$message->message .= '<p>'.sprintf(__('Remote server could not reach this server (%s). Status: %s - %s', 'cf-deploy'), esc_url($args['calling_from']), $f['response']['code'], $f['response']['message']).'</p>'; | |
} | |
elseif ($f['body'] !== $args['testdata']) { | |
$message->success = false; | |
$message->message .= '<p>'.sprintf(__('Remote server could not reach this server (%s). Status: <br/><b>Expecting:</b> %s<br/><b>Got:</b> %s', 'cf-deploy'), esc_url($args['calling_from']), esc_html($args['testdata']), esc_html($f['body'])).'</p>'; | |
} | |
return $message; | |
} | |
/** | |
* Testing, testing, is this thing on? | |
* | |
* @param array $args | |
* @return string | |
*/ | |
protected function hello($args) { | |
return 'hello to you too, '.(!empty($args['name']) ? $args['name'] : 'bub').'!'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment