PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / central / listener.php

listener.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at central/listener.php

269 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
4
5 /**
6 * This class is the basic glue between the lower-level UpdraftPlus_Remote_Communications (UDRPC) class, and UpdraftPlus. It does not contain actual commands themselves; the class names to use for actual commands are passed in as a parameter to the constructor.
7 */
8 class UpdraftPlus_UpdraftCentral_Listener {
9
10 public $udrpc_version;
11
12 private $ud = null;
13
14 private $receivers = array();
15
16 private $extra_info = array();
17
18 private $php_events = array();
19
20 private $commands = array();
21
22 private $current_udrpc = null;
23
24 private $command_classes;
25
26 public function __construct($keys = array(), $command_classes = array()) {
27 global $updraftplus;
28 $this->ud = $updraftplus;
29 // It seems impossible for this condition to result in a return; but it seems Plesk can do something odd within the control panel that causes a problem - see HS#6276
30 if (!is_a($this->ud, 'UpdraftPlus')) return;
31
32 $this->command_classes = $command_classes;
33
34 foreach ($keys as $name_hash => $key) {
35 // publickey_remote isn't necessarily set yet, depending on the key exchange method
36 if (!is_array($key) || empty($key['extra_info']) || empty($key['publickey_remote'])) continue;
37 $indicator = $name_hash.'.central.updraftplus.com';
38 $ud_rpc = $this->ud->get_udrpc($indicator);
39 $this->udrpc_version = $ud_rpc->version;
40
41 // Only turn this on if you are comfortable with potentially anything appearing in your PHP error log
42 if (defined('UPDRAFTPLUS_UDRPC_FORCE_DEBUG') && UPDRAFTPLUS_UDRPC_FORCE_DEBUG) $ud_rpc->set_debug(true);
43 $this->receivers[$indicator] = $ud_rpc;
44 $this->extra_info[$indicator] = isset($key['extra_info']) ? $key['extra_info'] : null;
45 $ud_rpc->set_key_local($key['key']);
46 $ud_rpc->set_key_remote($key['publickey_remote']);
47 // Create listener (which causes WP actions to be fired when messages are received)
48 $ud_rpc->activate_replay_protection();
49 if (!empty($key['extra_info']) && isset($key['extra_info']['mothership'])) {
50 $mothership = $key['extra_info']['mothership'];
51 unset($url);
52 if ('__updraftpluscom' == $mothership) {
53 $url = 'https://updraftplus.com';
54 } elseif (false != ($parsed = parse_url($key['extra_info']['mothership'])) && is_array($parsed)) {
55 $url = $parsed['scheme'].'://'.$parsed['host'];
56 }
57 if (!empty($url)) $ud_rpc->set_allow_cors_from(array($url));
58 }
59 $ud_rpc->create_listener();
60 }
61
62 // If we ever need to expand beyond a single GET action, this can/should be generalised and put into the commands class
63 if (!empty($_GET['udcentral_action']) && 'login' == $_GET['udcentral_action']) {
64 // auth_redirect() does not return, according to the documentation; but the code shows that it can
65 // auth_redirect();
66
67 if (!empty($_GET['login_id']) && is_numeric($_GET['login_id']) && !empty($_GET['login_key'])) {
68 $login_user = get_user_by('id', $_GET['login_id']);
69
70 include_once(ABSPATH.WPINC.'/version.php');
71 if (is_a($login_user, 'WP_User') || (version_compare($wp_version, '3.5', '<') && !empty($login_user->ID))) {
72 // Allow site implementers to disable this functionality
73 $allow_autologin = apply_filters('updraftcentral_allow_autologin', true, $login_user);
74 if ($allow_autologin) {
75 $login_key = get_user_meta($login_user->ID, 'updraftcentral_login_key', true);
76 if (is_array($login_key) && !empty($login_key['created']) && $login_key['created'] > time() - 60 && !empty($login_key['key']) && $login_key['key'] == $_GET['login_key']) {
77 $autologin = empty($login_key['redirect_url']) ? network_admin_url() : $login_key['redirect_url'];
78 }
79 }
80 }
81 }
82 if (!empty($autologin)) {
83 // Allow use once only
84 delete_user_meta($login_user->ID, 'updraftcentral_login_key');
85 $this->autologin_user($login_user, $autologin);
86 }
87 }
88
89 add_filter('udrpc_action', array($this, 'udrpc_action'), 10, 5);
90 add_filter('updraftcentral_get_command_info', array($this, 'updraftcentral_get_command_info'), 10, 2);
91
92 }
93
94 /**
95 * Retrieves command class information and includes class file if class
96 * is currently not available.
97 *
98 * @param mixed $response The default response to return if the submitted command does not exists
99 * @param string $command The command to parse and check
100 * @return array Contains the following command information "command_php_class", "class_prefix" and "command"
101 */
102 public function updraftcentral_get_command_info($response, $command) {
103 if (!preg_match('/^([a-z0-9]+)\.(.*)$/', $command, $matches)) return $response;
104 $class_prefix = $matches[1];
105 $command = $matches[2];
106
107 // We only handle some commands - the others, we let something else deal with
108 if (!isset($this->command_classes[$class_prefix])) return $response;
109
110 $command_php_class = $this->command_classes[$class_prefix];
111 $command_base_class_at = apply_filters('updraftcentral_command_base_class_at', UPDRAFTCENTRAL_CLIENT_DIR.'/commands.php');
112
113 if (!class_exists('UpdraftCentral_Commands')) include_once($command_base_class_at);
114
115 // Second parameter has been passed since
116 do_action('updraftcentral_command_class_wanted', $command_php_class);
117
118 if (!class_exists($command_php_class)) {
119 if (file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/'.$class_prefix.'.php')) {
120 include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/'.$class_prefix.'.php');
121 }
122 }
123
124 return array(
125 'command_php_class' => $command_php_class,
126 'class_prefix' => $class_prefix,
127 'command' => $command
128 );
129 }
130
131 /**
132 * Do verification before calling this method
133 *
134 * @param WP_User|Object $user user object for autologin
135 * @param boolean $redirect_url Redirect URL
136 */
137 private function autologin_user($user, $redirect_url = false) {
138 if (!is_user_logged_in()) {
139 // $user = get_user_by('id', $user_id);
140 // Don't check that it's a WP_User - that's WP 3.5+ only
141 if (!is_object($user) || empty($user->ID)) return;
142 wp_set_current_user($user->ID, $user->user_login);
143 wp_set_auth_cookie($user->ID);
144 do_action('wp_login', $user->user_login, $user);
145 }
146 if ($redirect_url) {
147 wp_safe_redirect($redirect_url);
148 exit;
149 }
150 }
151
152 /**
153 * WP filter udrpc_action
154 *
155 * @param Array $response - the unfiltered response that will be returned
156 * @param String $command - the command being called
157 * @param Array $data - the parameters to the command
158 * @param String $key_name_indicator - the UC key that is in use
159 * @param Object $ud_rpc - the UDRP object
160 *
161 * @return Array - filtered response
162 */
163 public function udrpc_action($response, $command, $data, $key_name_indicator, $ud_rpc) {
164
165 if (empty($this->receivers[$key_name_indicator])) return $response;
166
167 // This can be used to detect an UpdraftCentral context
168 if (!defined('UPDRAFTCENTRAL_COMMAND')) define('UPDRAFTCENTRAL_COMMAND', $command);
169
170 $this->initialise_listener_error_handling();
171
172 $command_info = apply_filters('updraftcentral_get_command_info', false, $command);
173 if (!$command_info) return $response;
174
175 $class_prefix = $command_info['class_prefix'];
176 $command = $command_info['command'];
177 $command_php_class = $command_info['command_php_class'];
178
179 if (empty($this->commands[$class_prefix])) {
180 if (class_exists($command_php_class)) {
181 $this->commands[$class_prefix] = new $command_php_class($this);
182 }
183 }
184
185 $command_class = isset($this->commands[$class_prefix]) ? $this->commands[$class_prefix] : new stdClass;
186
187 if ('_' == substr($command, 0, 1) || !is_a($command_class, $command_php_class) || (!method_exists($command_class, $command) && !method_exists($command_class, '__call'))) {
188 if (defined('UPDRAFTPLUS_UDRPC_FORCE_DEBUG') && UPDRAFTPLUS_UDRPC_FORCE_DEBUG) error_log("Unknown RPC command received: ".$command);
189 return $this->return_rpc_message(array('response' => 'rpcerror', 'data' => array('code' => 'unknown_rpc_command', 'data' => array('prefix' => $class_prefix, 'command' => $command, 'class' => $command_php_class))));
190 }
191
192 $extra_info = isset($this->extra_info[$key_name_indicator]) ? $this->extra_info[$key_name_indicator] : null;
193
194 // Make it so that current_user_can() checks can apply + work
195 if (!empty($extra_info['user_id'])) wp_set_current_user($extra_info['user_id']);
196
197 $this->current_udrpc = $ud_rpc;
198
199 do_action('updraftcentral_listener_pre_udrpc_action', $command, $command_class, $data, $extra_info);
200
201 // Allow the command class to perform any boiler-plate actions.
202 if (is_callable(array($command_class, '_pre_action'))) call_user_func(array($command_class, '_pre_action'), $command, $data, $extra_info);
203
204 // Despatch
205 $msg = apply_filters('updraftcentral_listener_udrpc_action', call_user_func(array($command_class, $command), $data, $extra_info), $command_class, $class_prefix, $command, $data, $extra_info);
206
207 if (is_callable(array($command_class, '_post_action'))) call_user_func(array($command_class, '_post_action'), $command, $data, $extra_info);
208
209 do_action('updraftcentral_listener_post_udrpc_action', $command, $command_class, $data, $extra_info);
210
211 return $this->return_rpc_message($msg);
212 }
213
214 public function get_current_udrpc() {
215 return $this->current_udrpc;
216 }
217
218 private function initialise_listener_error_handling() {
219 $this->ud->error_reporting_stop_when_logged = true;
220 set_error_handler(array($this->ud, 'php_error'), E_ALL & ~E_STRICT);
221 $this->php_events = array();
222 @ob_start();
223 add_filter('updraftplus_logline', array($this, 'updraftplus_logline'), 10, 4);
224 if (!UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) return;
225 }
226
227 public function updraftplus_logline($line, $nonce, $level, $uniq_id) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
228 if ('notice' === $level && 'php_event' === $uniq_id) {
229 $this->php_events[] = $line;
230 }
231 return $line;
232 }
233
234 public function return_rpc_message($msg) {
235 if (is_array($msg) && isset($msg['response']) && 'error' == $msg['response']) {
236 $this->ud->log('Unexpected response code in remote communications: '.serialize($msg));
237 }
238
239 $caught_output = @ob_get_contents();
240 @ob_end_clean();
241 // If turning output-catching off, turn this on instead:
242 // $caught_output = ''; @ob_end_flush();
243
244 // If there's higher-level output buffering going on, then get rid of that
245 if (ob_get_level()) ob_end_clean();
246
247 if ($caught_output) {
248 if (!isset($msg['data'])) $msg['data'] = null;
249 $msg['data'] = array('caught_output' => $caught_output, 'previous_data' => $msg['data']);
250 $already_rearranged_data = true;
251 }
252
253 if (!empty($this->php_events)) {
254 if (!isset($msg['data'])) $msg['data'] = null;
255 if (!empty($already_rearranged_data)) {
256 $msg['data']['php_events'] = array();
257 } else {
258 $msg['data'] = array('php_events' => array(), 'previous_data' => $msg['data']);
259 }
260 foreach ($this->php_events as $logline) {
261 $msg['data']['php_events'][] = $logline;
262 }
263 }
264 restore_error_handler();
265
266 return $msg;
267 }
268 }
269