PluginProbe
UpdraftCentral Dashboard / 0.3.4
UpdraftCentral Dashboard v0.3.4
0.8.33 0.7.2 0.7.3 0.7.4 0.8.0 0.8.1 0.8.10 0.8.11 0.8.12 0.8.13 0.8.14 0.8.15 0.8.16 0.8.17 0.8.18 0.8.19 0.8.2 0.8.20 0.8.21 0.8.22 0.8.23 0.8.24 0.8.25 0.8.26 0.8.27 All 51 releases
updraftcentral / classes / user.php

user.php in UpdraftCentral Dashboard 0.3.4, at classes/user.php

769 lines 31.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('UD_CENTRAL_DIR')) die('Security check');
4
5 if (!class_exists('UpdraftCentral_User')):
6 class UpdraftCentral_User {
7
8 private $rc;
9 public $user_id = null;
10 public $sites = null;
11 public $sites_meta = null;
12 private $php_events;
13
14 private $licence_manager = null;
15
16 public function __construct($user_id) {
17
18 $this->user_id = (int)$user_id;
19 if (!is_user_logged_in()) throw new Exception('The current visitor is not logged in');
20
21 global $wpdb;
22 $this->rc = UpdraftCentral();
23 $this->sites_table = $wpdb->prefix.$this->rc->table_prefix.'sites';
24 $this->sitemeta_table = $wpdb->prefix.$this->rc->table_prefix.'sitemeta';
25
26 add_filter('updraftcentral_dashboard_ajaxaction_newsite', array($this, 'dashboard_ajaxaction_newsite'), 10, 2);
27 add_filter('updraftcentral_dashboard_ajaxaction_edit_site_configuration', array($this, 'dashboard_ajaxaction_edit_site_configuration'), 10, 2);
28 add_filter('updraftcentral_dashboard_ajaxaction_edit_site_connection_method', array($this, 'dashboard_ajaxaction_edit_site_connection_method'), 10, 2);
29 add_filter('updraftcentral_dashboard_ajaxaction_delete_site', array($this, 'dashboard_ajaxaction_delete_site'), 10, 2);
30 add_filter('updraftcentral_dashboard_ajaxaction_sites_html', array($this, 'dashboard_ajaxaction_sites_html'));
31 add_filter('updraftcentral_dashboard_ajaxaction_site_rpc', array($this, 'dashboard_ajaxaction_site_rpc'), 10, 2);
32
33 add_filter('updraftcentral_load_user_sites', array($this, 'load_user_sites_filter'));
34
35 // Load licence manager - this needs loading before the sites themselves are
36 if (!class_exists('UpdraftCentral_Licence_Manager')) require_once(UD_CENTRAL_DIR.'/classes/licence-manager.php');
37
38 // Allow developers to implement their own licence management
39 $licence_manager_class = apply_filters('updraftcentral_licence_manager_class', 'UpdraftCentral_Licence_Manager');
40
41 $this->licence_manager = new $licence_manager_class($this, $this->rc);
42
43 $this->load_user_sites();
44
45 }
46
47 public function get_licence_manager() {
48 return $this->licence_manager;
49 }
50
51 // TODO: 1) Catch PHP events on the mothership, pass them on and let them be console.logged 2) Pass on caught output from the remote side, and get it console.logged
52 public function dashboard_ajaxaction_site_rpc($response, $post_data) {
53
54 $is_preencrypted = !empty($_POST['site_rpc_preencrypted']);
55
56 // Check the sent data
57 if ($is_preencrypted) {
58
59 if (!isset($post_data['wrapped_message']) || !is_array($post_data['wrapped_message']) || empty($post_data['site_id']) || !is_numeric($post_data['site_id'])) {
60 $response['responsetype'] = 'error';
61 $response['code'] = 'missing_data';
62 $response['message'] = __('Missing information', 'updraftcentral');
63 return $response;
64 }
65
66 } else {
67
68 if (!isset($post_data['data']) || !is_array($post_data['data']) || empty($post_data['data']['command']) || empty($post_data['site_id']) || !is_numeric($post_data['site_id'])) {
69 $response['responsetype'] = 'error';
70 $response['code'] = 'missing_data';
71 $response['message'] = __('Missing information', 'updraftcentral');
72 return $response;
73 }
74
75 }
76
77 $site_id = (int)$post_data['site_id'];
78
79 // This is also a security check - whether the specified site belongs to the current user
80 if (empty($this->sites[$site_id])) {
81 $response['responsetype'] = 'error';
82 $response['code'] = 'nonexistent_site';
83 $response['message'] = sprintf(__('This site (%d / %d) was not found', 'updraftcentral'), $this->user_id, $site_id);
84 return $response;
85 }
86
87 $site = $this->sites[$site_id];
88
89 if (empty($site->key_local_private)) {
90 $response['responsetype'] = 'error';
91 $response['code'] = 'nonexistent_site_key';
92 $response['message'] = sprintf(__('The key for this site (%d / %d) was not found', 'updraftcentral'), $this->user_id, $site_id);
93 return $response;
94 }
95
96 if (!empty($site->unlicensed)) {
97 $response['responsetype'] = 'error';
98 $response['code'] = 'site_unlicensed';
99 $response['message'] = apply_filters('updraftcentral_site_unlicensed_message', __('You have more sites in your dashboard than licences. As a result, you cannot perform actions on this site.', 'updraftcentral').' '.__('You will need to obtain more licences, or remove some sites.', 'updraftcentral'));
100 return $response;
101 }
102
103 if ($this->rc->url_looks_internal($site->url) && !$this->rc->url_looks_internal(site_url()) && !apply_filters('updraftcentral_allow_contacting_internal_url_from_server', true, $url)) {
104 $url_scheme = strtolower(parse_url($site->url, PHP_URL_SCHEME));
105 $response['responsetype'] = 'error';
106 $response['code'] = 'cannot_contact_localdev';
107 $response['message'] = __('You cannot contact a website hosted on a site-local network (e.g. localhost) from this dashboard - it cannot be reached.', 'updraftcentral');
108 return $response;
109 }
110
111 @ob_start();
112
113 $site_meta = empty($this->sites_meta[$site->site_id]) ? array() : $this->sites_meta[$site->site_id];
114
115 $ud_rpc = $this->rc->get_udrpc($site->key_name_indicator);
116 $ud_rpc->set_destination_url($site->url);
117
118 if (!empty($site_meta['http_username'])) {
119
120 $authentication_method = empty($site_meta['http_authentication_method']) ? 'basic' : $site_meta['http_authentication_method'];
121 $http_password = empty($site_meta['http_password']) ? '' : (string)$site_meta['http_password'];
122
123 if ('basic' != $authentication_method && version_compare(PHP_VERSION, '5.4', '<')) {
124 $reply = new WP_Error('no_digest_before_php54', sprintf(__('To use HTTP digest authentication, your server running the UpdraftCentral dashboard needs at least PHP %s (your version is %s)', 'updraftcentral'), '5.4', PHP_VERSION), PHP_VERSION);
125 } else {
126 // Guzzle supports HTTP digest authentication - the WP HTTP API doesn't.
127 require_once(UD_CENTRAL_DIR.'/composer/vendor/autoload.php');
128 $guzzle_client = new GuzzleHttp\Client();
129
130 if (!method_exists($ud_rpc, 'set_http_transport') || !method_exists($ud_rpc, 'set_http_credentials')) {
131 // That's the probable cause, because we can assume that UC has a bundled UDRPC that's new enough.
132 $reply = new WP_Error('incompatible_udrpc_php', sprintf(__('The loaded UDRPC library (%s) is too old - you probably need to update your installed UpdraftPlus on the server', 'updraftcentral'), $ud_rpc->version));
133 } else {
134 $ud_rpc->set_http_transport($guzzle_client);
135 $ud_rpc->set_http_credentials(array('username' => $site_meta['http_username'], 'password' => $http_password, 'authentication_method' => $authentication_method));
136 }
137
138 }
139
140 }
141
142 if (!empty($reply)) {
143 // Already an error condition - nothing to do
144 } elseif ($is_preencrypted) {
145 $reply = $this->send_message($ud_rpc, '__updraftcentral_internal_preencrypted', $post_data['wrapped_message'], 30);
146 } else {
147
148 $ud_rpc->set_key_local($site->key_local_private);
149 $ud_rpc->set_key_remote($site->key_remote_public);
150 $ud_rpc->activate_replay_protection();
151
152 $command = (string)$post_data['data']['command'];
153 $data = isset($post_data['data']['data']) ? $post_data['data']['data'] : null;
154
155 $reply = $this->send_message($ud_rpc, $command, $data, 30);
156 }
157
158 $caught_output = @ob_get_contents();
159 @ob_end_clean();
160
161 // Pass on PHP events from the remote side
162 if (!empty($this->php_events)) $response['php_events'] = $this->php_events;
163 if (!empty($caught_output)) $response['mothership_caught_output'] = $caught_output;
164 if (is_wp_error($reply)) {
165 $response['responsetype'] = 'error';
166 $response['message'] = $reply->get_error_message();
167 $response['code'] = $reply->get_error_code();
168 $response['data'] = $reply->get_error_data();
169 } elseif (is_array($reply) && !empty($reply['response']) && 'error' == $reply['response']) {
170 $response['responsetype'] = 'error';
171 $response['message'] = empty($reply['message']) ? __('The connection to the remote site returned an error', 'updraftcentral') : $reply['message'];
172 $response['data'] = $reply;
173 } elseif ((!$is_preencrypted && (!is_array($reply) || empty($reply['response']) || (('ping' == $command && 'pong' != $reply['response'])) && 'rpcok' != $reply['response'])) || ($is_preencrypted && null === ($decoded_reply = json_decode($reply, true)))) {
174 // If it is pre-encrypted, we expect a field 'udrpc_message' in the reply (after it's been JSON-decoded). We could check that. But instead, we just pass it back to the browser, since it'll be checked there anyway.
175 $response['responsetype'] = 'error';
176 $response['message'] = __('There was an error in contacting the remote site.', 'updraftcentral').' '.__("You should check that the remote site is online, is not firewalled, has remote control enabled, and that no security module is blocking the access. Then, check the logs on the remote site and your browser's JavaScript console.", 'updraftcentral').' '.__('If none of that helps, then you should try re-adding the site with a fresh key.', 'updraftcentral');
177 $response['data'] = $reply;
178 $response['code'] = 'no_pong';
179 } else {
180 $response['responsetype'] = 'ok';
181 $response['message'] = __('The site was connected to, and returned a response', 'updraftcentral');
182 if ($is_preencrypted) {
183 $response['wrapped_response'] = $decoded_reply;
184 } elseif ('siteinfo' == $command) {
185 $response['rpc_response'] = $this->deep_sanitize($reply);
186 } else {
187 $response['rpc_response'] = $reply;
188 }
189 }
190
191 return $response;
192 }
193
194 public function deep_sanitize($input, $sanitize_function = 'htmlspecialchars') {
195 if (is_string($input)) return call_user_func($sanitize_function, $input);
196 if (is_array($input)) {
197 foreach ($input as $k => $v) {
198 $input[$k] = $this->deep_sanitize($v, $sanitize_function);
199 }
200 }
201 return $input;
202 }
203
204 private function send_message($ud_rpc, $message, $data = null, $timeout = 30) {
205 $this->php_events = array();
206
207 if ('__updraftcentral_internal_preencrypted' == $message) {
208
209 $post_options = array(
210 'timeout' => $timeout,
211 'body' => $data,
212 );
213
214 $post_options = apply_filters('udrpc_post_options', $post_options, $message, $data, $timeout, $this);
215
216 try {
217 $post = $ud_rpc->http_post($post_options);
218 } catch (Exception $e) {
219 // Curl can return an error code 0, which causes WP_Error to return early, without recording the message. So, we prefix the code.
220 return new WP_Error('http_post_'.$e->getCode(), $e->getMessage());
221 }
222
223 if (is_wp_error($post)) return $post;
224
225 if (empty($post['response']) || empty($post['response']['code'])) return new WP_Error('empty_http_code', 'Unexpected HTTP response code');
226
227 if ($post['response']['code'] < 200 || $post['response']['code'] >= 300) return new WP_Error('unexpected_http_code', 'Unexpected HTTP response code ('.$post['response']['code'].')', $post);
228
229 if (empty($post['body'])) return new WP_Error('empty_response', 'Empty response from remote site');
230
231 return (string)$post['body'];
232
233 } else {
234 $response = $ud_rpc->send_message($message, $data, $timeout);
235 }
236
237 // TODO: Handle caught_output
238
239 if (is_array($response) && !empty($response['data']) && is_array($response['data']) && !empty($response['data']['php_events']) && !empty($response['data']['previous_data'])) {
240 // global $updraftplus;
241 $this->php_events = $response['data']['php_events'];
242 if (defined('WP_DEBUG') && WP_DEBUG) {
243 foreach ($response['data']['php_events'] as $logline) {
244 error_log("From remote side: ".$logline);
245 }
246 }
247 $response['data'] = $response['data']['previous_data'];
248 }
249 return $response;
250 }
251
252 public function dashboard_ajaxaction_sites_html($response) {
253 $response['responsetype'] = 'ok';
254 $response['sites_html'] = $this->get_sites_html();
255 $response['status_info'] = array(
256 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
257 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
258 );
259 $response['message'] = __('The site list has been refreshed.', 'updraftcentral');
260 return $response;
261 }
262
263 public function dashboard_ajaxaction_delete_site($response, $post_data) {
264 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
265
266 $deleted = $this->delete_site_by_id((int)$post_data['data']['site_id']);
267
268 if (is_wp_error($deleted)) {
269 $response = $deleted;
270 } else {
271 $response['responsetype'] = 'ok';
272 $response['status_info'] = array(
273 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
274 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
275 );
276 $response['sites_html'] = $this->get_sites_html();
277 $response['message'] = __('The site was successfully deleted from your dashboard.', 'updraftcentral');
278 }
279
280 } else {
281 $response['responsetype'] = 'error';
282 $response['code'] = 'missing_data';
283 $response['message'] = __('Missing information', 'updraftcentral');
284 }
285 return $response;
286 }
287
288
289 public function dashboard_ajaxaction_edit_site_connection_method($response, $post_data) {
290
291 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id'])) {
292
293 $site_id = (int)$post_data['data']['site_id'];
294
295 $connection_method = isset($post_data['data']['connection_method']) ? (string)$post_data['data']['connection_method'] : 'direct_default_auth';
296
297 $updated = $this->rc->wp_update('sites',
298 array(
299 'connection_method' => $connection_method,
300 ),
301 array(
302 'user_id' => $this->user_id,
303 'site_id' => $site_id,
304 ),
305 array(
306 '%s'
307 ),
308 array(
309 '%d',
310 '%d',
311 )
312 );
313
314 if (is_numeric($updated)) {
315 $response['responsetype'] = 'ok';
316
317 $this->load_user_sites();
318 $response['sites_html'] = $this->get_sites_html();
319 $response['status_info'] = array(
320 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
321 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
322 );
323
324 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
325 } else {
326 $response = $updated;
327 }
328
329 } else {
330 $response['responsetype'] = 'error';
331 $response['code'] = 'missing_data';
332 $response['message'] = __('Missing information', 'updraftcentral');
333 }
334 return $response;
335
336 }
337
338 public function dashboard_ajaxaction_edit_site_configuration($response, $post_data) {
339
340 if (isset($post_data['data']) && is_array($post_data['data']) && !empty($post_data['data']['site_id']) && isset($post_data['data']['description'])) {
341
342 $site_id = (int)$post_data['data']['site_id'];
343
344 $connection_method = isset($post_data['data']['connection_method']) ? (string)$post_data['data']['connection_method'] : 'direct_default_auth';
345 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && $post_data['data']['send_cors_headers']) ? 1 : 0;
346
347 $updated = $this->rc->wp_update('sites',
348 array(
349 'description' => (string)$post_data['data']['description'],
350 'connection_method' => $connection_method,
351 'send_cors_headers' => $send_cors_headers,
352 ),
353 array(
354 'user_id' => $this->user_id,
355 'site_id' => $site_id,
356 ),
357 array(
358 '%s',
359 '%s',
360 '%d'
361 ),
362 array(
363 '%d',
364 '%d',
365 )
366 );
367
368 if (is_numeric($updated)) {
369 $response['responsetype'] = 'ok';
370
371 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
372 if (!$extra_site_info_unparsed) {
373 $extra_site_info = array();
374 } else {
375 parse_str($extra_site_info_unparsed, $extra_site_info);
376 }
377
378 if (!empty($extra_site_info)) {
379 foreach ($extra_site_info as $meta_key => $meta_value) {
380 $this->rc->site_meta->update_site_meta($site_id, $meta_key, $meta_value);
381 }
382 }
383
384 $this->load_user_sites();
385 $response['sites_html'] = $this->get_sites_html();
386 $response['status_info'] = array(
387 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
388 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
389 );
390
391 $response['message'] = __('The site configuration was successfully edited.', 'updraftcentral');
392 } else {
393
394 $response = $updated;
395 }
396
397 } else {
398 $response['responsetype'] = 'error';
399 $response['code'] = 'missing_data';
400 $response['message'] = __('Missing information', 'updraftcentral');
401 }
402 return $response;
403 }
404
405 public function dashboard_ajaxaction_newsite($response, $post_data) {
406
407 if (empty($post_data['data']) || !is_array($post_data['data']) || empty($post_data['data']['key'])) {
408 $response['responsetype'] = 'error';
409 $response['code'] = 'empty';
410 $response['message'] = __('Please enter the site key.', 'updraftcentral');
411 } else {
412
413 $site_key = $post_data['data']['key'];
414
415 $extra_site_info_unparsed = empty($post_data['data']['extra_site_info']) ? false : $post_data['data']['extra_site_info'];
416 if (!$extra_site_info_unparsed) {
417 $extra_site_info = array();
418 } else {
419 parse_str($extra_site_info_unparsed, $extra_site_info);
420 }
421
422 $ud_rpc = $this->rc->get_udrpc();
423
424 // A bundle has these keys: key, name_indicator, url
425 $decode_bundle = $ud_rpc->decode_portable_bundle($site_key, 'base64_with_count');
426
427 if (!is_array($decode_bundle) || !empty($decode_bundle['code'])) {
428 $response['responsetype'] = 'error';
429 $response['message'] = __('Error:', 'updraftcentral');
430 $response['code'] = empty($decode_bundle['code']) ? 'could_not_decode' : $decode_bundle['code'];
431 if (!empty($decode_bundle['code']) && $decode_bundle['code'] == 'invalid_wrong_length') {
432 $response['message'] .= ' '.__('The entered key was the wrong length - please try again.', 'updraftcentral');
433 } elseif (!empty($decode_bundle['code']) && $decode_bundle['code'] == 'invalid_corrupt') {
434 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral').' ('.$decode_bundle['data'].')';
435 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['name_indicator'])) {
436 $response['message'] .= ' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
437 $response['data'] = $decode_bundle;
438 }
439 } elseif (empty($decode_bundle['key']) || empty($decode_bundle['url']) || empty($decode_bundle['user_id'])) {
440 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key was corrupt - please try again.', 'updraftcentral');
441 $response['code'] = 'corrupt_key';
442 $response['data'] = $decode_bundle;
443 } else {
444
445 if ($decode_bundle['url'] == trailingslashit(network_site_url()) && !apply_filters('updraftcentral_allow_self_control', true)) {
446 $response['responsetype'] = 'error';
447 $response['code'] = 'this_site';
448 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key does not belong to a remote site (it belongs to this one).', 'updraftcentral');
449 } elseif ($this->rc->url_looks_internal($url) && !$this->rc->url_looks_internal(site_url()) && !apply_filters('updraftcentral_allow_adding_internal_url', true, $url)) {
450 // The default is to allow it, because as long as your browser is running on the same machine as the site is on, it can work.
451 $response['responsetype'] = 'error';
452 $response['code'] = 'cant_add_localhost';
453 $response['message'] = __('Error:', 'updraftcentral').' '.__('The entered key belongs to a local development website - these cannot be controlled from this dashboard because it is not reachable from an external network.', 'updraftcentral');
454 } else {
455 // Was the key sent SSL to us directly?
456 $key = $decode_bundle['key'];
457 if (is_array($key) && !empty($key['key_hash']) && isset($key['key_id'])) {
458 global $wpdb;
459 // Allow them 3 hours to copy-and-paste their key
460 $wpdb->query("DELETE FROM ".$wpdb->prefix.$this->rc->table_prefix."site_temporary_keys WHERE created<=".(int)(time() - 10800));
461 $key_info = $this->rc->wp_get_row('site_temporary_keys', $wpdb->prepare('key_id=%d', $key['key_id']));
462 if (is_object($key_info) && !empty($key_info->key_local_private) && !empty($key_info->key_remote_public)) {
463 $this->rc->wp_delete('site_temporary_keys', array('key_id' => $key['key_id']));
464 $key_hash = hash('sha256', $key_info->key_remote_public);
465 if (hash_equals($key_hash, $key['key_hash'])) {
466 $key_local_private = $key_info->key_local_private;
467 $key_remote_public = $key_info->key_remote_public;
468 } else {
469 $response['responsetype'] = 'error';
470 $response['code'] = 'wrong_hash';
471 $response['message'] = __('Error:', 'updraftcentral').' '.apply_filters('updraftcentral_wrong_hash_message', __('This key could not be added, as it appears to be corrupt - please try again.', 'updraftcentral'));
472 return $response;
473 }
474 } else {
475 $response['responsetype'] = 'error';
476 $response['code'] = 'no_key_found';
477 $response['message'] = __('Error:', 'updraftcentral').' '.apply_filters('updraftcentral_no_key_found_message', __('This key could not be added - it may be too long since you generated it; please try again.', 'updraftcentral'));
478 return $response;
479 }
480
481 } elseif (!empty($decode_bundle['mothership_firewalled'])) {
482
483 // Need to do direct AJAX from the browser to the mothership to send our key
484
485 $ud_rpc = $this->rc->get_udrpc('central_host.updraftplus.com', true);
486 if (false != $ud_rpc->generate_new_keypair()) {
487 $key_remote_public = $key;
488 $key_local_private = $ud_rpc->get_key_local();
489 } else {
490 $response['responsetype'] = 'error';
491 $response['code'] = 'keygen_error';
492 $response['message'] = 'An error occurred when attempting to generate a new key-pair';
493 return $response;
494 }
495
496 } else {
497 $key_remote_public = $key;
498 $key_local_private = false;
499 }
500
501 $remote_site_id = empty($decode_bundle['ms_id']) ? 0 : $decode_bundle['ms_id'];
502 $description = isset($decode_bundle['site_title']) ? (string)$decode_bundle['site_title'] : '';
503
504 $send_cors_headers = (isset($post_data['data']['send_cors_headers']) && !$post_data['data']['send_cors_headers']) ? 0 : 1;
505 $connection_method = isset($post_data['data']['connection_method']) ? (string)$post_data['data']['connection_method'] : 'direct_default_auth';
506
507 $added = $this->add_site($decode_bundle['url'], $key_local_private, $key_remote_public, $decode_bundle['user_id'], $decode_bundle['user_login'], $decode_bundle['name_indicator'], $remote_site_id, $description, $connection_method, $send_cors_headers);
508
509 if (true === $added) {
510 $response['responsetype'] = 'ok';
511
512 global $wpdb;
513 $new_site_id = $wpdb->insert_id;
514
515 if (!empty($extra_site_info)) {
516 if (!is_array($this->sites_meta)) $this->sites_meta = array();
517 if (empty($this->sites_meta[$new_site_id])) $this->sites_meta[$new_site_id] = array();
518 foreach ($extra_site_info as $meta_key => $meta_value) {
519 if (!$meta_value) continue;
520 // Don't bother to save the default value on the initial adding of the site
521 if ('http_authentication_method' == $meta_key && 'basic' == $meta_value) continue;
522 // We update the in-memory copy because this is used by get_sites_html()
523 $this->sites_meta[$new_site_id][$meta_key] = $meta_value;
524 $this->rc->site_meta->add_site_meta($new_site_id, $meta_key, $meta_value);
525 }
526 }
527
528 // Return the new HTML widget to the front end
529 $response['sites_html'] = $this->get_sites_html();
530 $response['status_info'] = array(
531 'how_many_licences_in_use' => $this->licence_manager->how_many_licences_in_use(),
532 'how_many_licences_available' => $this->licence_manager->how_many_licences_available(),
533 );
534
535
536 $response['message'] = __('The key was successfully added.', 'updraftcentral').' '.__('It is for interacting with the following site: ', 'updraftcentral').htmlspecialchars($decode_bundle['url']);
537
538 if (!empty($decode_bundle['mothership_firewalled_callback_url'])) {
539 $response['key_needs_sending'] = array(
540 'site_id' => $new_site_id,
541 'url' => $decode_bundle['mothership_firewalled_callback_url'],
542 'updraft_key_index' => $decode_bundle['updraft_key_index'],
543 'remote_public_key' => $ud_rpc->get_key_remote()
544 );
545 }
546
547 } else {
548 $response['responsetype'] = 'error';
549 $response['code'] = $added->get_error_code();
550 $response['message'] = __('Error:', 'updraftcentral').' '.$added->get_error_message();
551 }
552
553 }
554 }
555 }
556 return $response;
557 }
558
559 public function load_user_sites_filter($sites) {
560 $how_many_licences_available = $this->licence_manager->how_many_licences_available();
561 $how_many_licences_in_use = count($sites);
562
563 if ($how_many_licences_available >= $how_many_licences_in_use || $how_many_licences_available < 0) return $sites;
564
565 $log_message = sprintf(__('You have more sites being managed (%d) than active licences (%d) - you will need to obtain more licences in order to manage all of your managed sites.', 'updraftcentral'), $how_many_licences_in_use, $how_many_licences_available);
566
567 $this->rc->log_notice($log_message, 'error', 'not_enough_licences');
568
569 $i = 0;
570 foreach ($sites as $site_id => $site) {
571 if ($i >= $how_many_licences_available) {
572 $site->unlicensed = true;
573 $sites[$site_id] = $site;
574 }
575 $i++;
576 }
577
578 return $sites;
579 }
580
581 public function load_user_sites() {
582 global $wpdb;
583 $sites = $wpdb->get_results("SELECT * FROM ".$this->sites_table." WHERE user_id=".$this->user_id);
584
585 $this->sites_meta = array();
586 $sites_meta = $wpdb->get_results("SELECT * FROM ".$this->sitemeta_table);
587 if (is_array($sites_meta)) {
588 foreach ($sites_meta as $meta_row) {
589 if (isset($meta_row->site_id)) {
590 // N.B. We are assuming a single value only for each key (the WP general scheme allows for multiple)
591 $this->sites_meta[$meta_row->site_id][$meta_row->meta_key] = $meta_row->meta_value;
592 }
593 }
594 }
595
596 if (is_array($sites)) {
597 $processed_sites = array();
598 foreach ($sites as $site) {
599 $processed_sites[$site->site_id] = $site;
600 }
601 $this->sites = apply_filters('updraftcentral_load_user_sites', $processed_sites, $this, $this->licence_manager);
602 return $this->sites;
603 } elseif (is_wp_error($sites)) {
604 $this->rc->log_notice($sites);
605 $this->sites = null;
606 return $sites;
607 }
608 }
609
610 public function get_sites_html() {
611
612 $ret = '';
613
614 // Get sites. Print a line for each of them.
615 if (empty($this->sites) || !is_array($this->sites)) {
616 $ret .= $this->rc->include_template('sites/none-set-up.php', true, array('common_urls' => $this->rc->get_common_urls()));
617 } else {
618 foreach ($this->sites as $site) {
619
620 $connection_method = isset($site->connection_method) ? (string)$site->connection_method : 'direct_default_auth';
621 $send_cors_headers = (isset($site->send_cors_headers) && !$site->send_cors_headers) ? 0 : 1;
622
623 $site_data_attributes = 'data-site_url="'.esc_attr($site->url).'" data-site_id="'.(int)$site->site_id.'" data-key_name_indicator="'.esc_attr($site->key_name_indicator).'" data-site_description="'.(($site->description) ? esc_attr($site->description) : esc_attr($site->url)).'" data-remote_user_id="'.(int)$site->remote_user_id.'" data-remote_user_login="'.esc_attr($site->remote_user_login).'"';
624
625 $site_meta = empty($this->sites_meta[$site->site_id]) ? array() : $this->sites_meta[$site->site_id];
626 if (!empty($site_meta)) {
627 if (!empty($site_meta['http_username'])) {
628 $http_password = empty($site_meta['http_password']) ? '' : $site_meta['http_password'];
629 $site_data_attributes .= ' data-http_username="'.esc_attr($site_meta['http_username']).'" data-http_password="'.esc_attr($http_password).'"';
630 if (!empty($site_meta['http_authentication_method'])) $site_data_attributes .= ' data-http_authentication_method="'.$site_meta['http_authentication_method'].'"';
631 }
632 }
633
634 if (empty($site->unlicensed)) {
635 if ('via_mothership_encrypting' != $connection_method ) {
636 $site_data_attributes .= ' data-site_remote_public_key="'.esc_attr($site->key_remote_public).'" data-site_local_private_key="'.esc_attr($site->key_local_private).'"';
637 }
638 } else {
639 $site_data_attributes .= ' data-site_unlicensed="1"';
640 }
641
642 $site_data_attributes .= ' data-connection_method="'.esc_attr($connection_method).'" data-send_cors_headers="'.$send_cors_headers.'"';
643
644 $ret .= $this->rc->include_template('sites/site-row.php', true, array('site' => $site, 'site_meta' => $site_meta, 'site_data_attributes' => $site_data_attributes));
645 }
646 }
647 return $ret;
648 }
649
650 /*
651 * Returns false if not authorised at all; or a timestamp if it's authorised until a particular date
652 *
653 * @return boolean|integer
654 */
655 public function authorised_for_site_until($site_id) {
656
657 if (!is_array($this->sites)) $this->load_user_sites();
658
659 if (is_array($this->sites)) {
660 foreach ($this->sites as $site) {
661 if ((int)$site_id == (int)$site->site_id && isset($site->licence_until)) {
662 return apply_filters('updraftcentral_authorised_for_site_until', (int)$site->licence_until, $site_id, $this->sites);
663 }
664 }
665 }
666
667 return apply_filters('updraftcentral_authorised_for_site_until', false, $site_id, $this->sites);
668
669 }
670
671 // Returns either true, or a WP_Error
672 public function add_site($url, $key_local_private, $key_remote_public, $remote_user_id, $remote_user_login, $key_name_indicator, $remote_site_id = 0, $description = '', $connection_method = 'direct_default_auth', $send_cors_headers = 1) {
673
674 if (!$this->user_can('add_site')) return new WP_Error('permission_denied', __('You do not have the permission to do this.', 'updraftcentral'), $this->user_id);
675
676 if (!$this->licence_manager->is_slot_available()) {
677 return new WP_Error('no_licences_available', apply_filters('updraftcentral_no_licences_available_message', __('You have no licences available - to add a site, you will need to obtain some more.', 'updraftcentral')));
678 }
679
680 $this->delete_site_by_url($url);
681
682 $added = $this->rc->wp_insert('sites',
683 array(
684 'user_id' => $this->user_id,
685 'url' => $url,
686 'key_local_private' => $key_local_private,
687 'key_remote_public' => $key_remote_public,
688 'description' => $description,
689 'connection_method' => $connection_method,
690 'send_cors_headers' => $send_cors_headers,
691 'sequence_id' => 0,
692 'remote_user_id' => $remote_user_id,
693 'remote_user_login' => $remote_user_login,
694 'remote_site_id' => $remote_site_id,
695 'key_name_indicator' => $key_name_indicator,
696 ),
697 array(
698 '%d',
699 '%s',
700 '%s',
701 '%s',
702 '%s',
703 '%s',
704 '%d',
705 '%d',
706 '%d',
707 '%s',
708 '%d',
709 '%s',
710 )
711 );
712
713 if (is_numeric($added)) {
714 $result = true;
715 } else {
716 $result = $added;
717 }
718
719 $this->load_user_sites();
720
721 return $result;
722
723 }
724
725 public function delete_site_meta($site_id) {
726 return $this->rc->wp_delete('sitemeta', array('site_id' => $site_id));
727 }
728
729 public function delete_site_by_id($site_id, $reload_user_sites = true) {
730 $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'site_id' => $site_id));
731 $this->delete_site_meta($site_id);
732 if ($reload_user_sites) $this->load_user_sites();
733 return $result;
734 }
735
736 public function delete_site_by_url($url) {
737
738 // We used to do a direct delete... but we need the site ID in order to be able to wipe the site meta
739 // $result = $this->rc->wp_delete('sites', array('user_id' => $this->user_id, 'url' => $url));
740
741 $result = 0;
742
743 foreach ($this->sites as $site_id => $site) {
744 if (strtolower($url) == strtolower($site->url)) {
745 $result += $this->delete_site_by_id($site_id, false);
746 }
747 }
748
749 $this->load_user_sites();
750 return $result;
751 }
752
753 // This just gives some potential for the future, currently - currently, there's nothing we're forbidding through this mechanism
754 public function user_can($do_what) {
755 $result = false;
756 switch ($do_what) {
757 case 'add_site':
758 $result = true;
759 break;
760 case 'delete_site':
761 $result = true;
762 break;
763 }
764 return apply_filters('updraftcentral_user_can', $result, $do_what, $this);
765 }
766
767 }
768 endif;
769