PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.4
Backup Migration v2.1.4
2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / offline.php
backup-backup / includes Last commit date
banner 2 months ago bodies 2 months ago check 2 months ago cli 2 months ago cron 2 months ago dashboard 2 months ago database 2 months ago external 2 months ago extracter 2 months ago htaccess 2 months ago notices 2 months ago progress 2 months ago scanner 2 months ago staging 2 months ago traits 2 months ago uploader 2 months ago vendor 2 months ago zipper 2 months ago .htaccess 2 months ago activation.php 2 months ago ajax.php 2 months ago ajax_offline.php 2 months ago analyst.php 2 months ago backup-process.php 2 months ago class-backup-method-mananger.php 2 months ago cli-handler.php 2 months ago compatibility.php 2 months ago config.php 2 months ago constants.php 2 months ago file-explorer.php 2 months ago initializer.php 2 months ago logger.php 2 months ago offline.php 2 months ago
offline.php
199 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin;
5 use BMI\Plugin\Backup_Migration_Plugin as BMP;
6 use BMI\Plugin\BMI_Logger as Logger;
7 // Exit on direct access
8 if (!defined('ABSPATH')) {
9 exit;
10 }
11
12 /**
13 * Offline Methods Manager
14 */
15 class BMI_Offline {
16
17 public $ajaxInserted = false;
18
19 /**
20 * __construct - Initializer (loads offline modules)
21 */
22 function __construct() {
23 add_action('bmi_ajax_offline', function($post=[]){
24 if (BMI_DEBUG)
25 Logger::error("FREE AJAX OFFLINE");
26 require_once BMI_INCLUDES . '/ajax_offline.php';
27 $ajaxoffline = new BMI_Ajax_Offline($post);
28 });
29 add_action('wp_ajax_bmip_keepalive', [&$this, 'initializeOfflineAjax']);
30 add_action('wp_ajax_nopriv_bmip_keepalive', [&$this, 'initializeOfflineAjax']);
31
32 // Handle Auth Handshake For M2M Connection (Ping server)
33 add_action('wp_ajax_nopriv_bmip_auth_handshake', [&$this, 'bmip_handle_handshake_request']);
34 add_action('wp_ajax_bmip_auth_handshake', [&$this, 'bmip_handle_handshake_request']);
35
36 if (is_user_logged_in() && current_user_can('administrator')) {
37 add_action('wp_ajax_backup_migration', [&$this, 'initializeOfflineAjax']);
38 }
39
40 add_filter('allowed_http_origins', function ($origins) {
41 $origins[] = 'https://backupbliss.com';
42 $origins[] = 'https://api.backupbliss.com';
43 return $origins;
44 });
45
46 // $TBU = get_option('bmip_to_be_uploaded', false);
47 // if ($TBU != false && (sizeof($TBU['current_upload']) > 0 || sizeof($TBU['queue']) > 0)) {
48
49 // }
50
51 add_action('admin_footer', [&$this, 'keepAliveJS']);
52
53 }
54
55 /**
56 * initializeOfflineAjax - Initialized Offline handlers for Ajax
57 *
58 * @return void
59 */
60 public function initializeOfflineAjax() {
61
62
63 // Check if the request comes from a logged-in admin (Browser context)
64 // OR from the Ping Server (M2M context)
65 $is_admin = current_user_can('manage_options') && check_ajax_referer('backup-migration-ajax', 'nonce', false);
66 $is_ping_server = $this->verify_ping_server_request();
67
68 if (!$is_admin && !$is_ping_server) {
69 wp_send_json_error('Unauthorized access', 403);
70 return;
71 }
72 // if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
73
74 // Extend execution time
75 if (BMP::isFunctionEnabled('headers_sent') && BMP::isFunctionEnabled('session_status')) {
76 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
77 if (BMP::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
78 if (BMP::isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
79 if (BMP::isFunctionEnabled('ini_set')) {
80 @ini_set('max_execution_time', '259200');
81 @ini_set('max_input_time', '259200');
82 }
83 }
84 }
85
86 if ((isset($_GET['token']) && ($_GET['token'] == 'bmip' || $_GET['token'] == 'bmi') && isset($_GET['f']))) {
87
88 if (empty($_GET)) return;
89
90 // Sanitize User Input
91 $post = BMP::sanitize($_GET);
92
93 } else if ((isset($_POST['token']) && ($_POST['token'] == 'bmip' || $_POST['token'] == 'bmi') && isset($_POST['f']))) {
94
95 if (empty($_POST)) return;
96
97 // Sanitize User Input
98 $post = BMP::sanitize($_POST);
99
100 }
101
102 if (!empty($post)) {
103 do_action("bmi_ajax_offline", $post);
104 }
105
106 // Execution error due to time limit
107 // register_shutdown_function([$this, 'execution_shutdown']);
108
109 // }
110
111 }
112
113 /**
114 * Verifies the Ping Server handshake.
115 * @return bool true if the request is verified, otherwise it sends a JSON error response and exits.
116 */
117 private function verify_ping_server_request() {
118 $stored_sk = get_option('bmi_sk_keepalive');
119 if (!isset($_SERVER['CONTENT_TYPE']) || stripos($_SERVER['CONTENT_TYPE'], 'application/json') === false) {
120 return false;
121 }
122 $raw = file_get_contents('php://input');
123 $data = json_decode($raw, true);
124 if (json_last_error() !== JSON_ERROR_NONE) {
125 return false;
126 }
127
128 $request_sk = sanitize_text_field( wp_unslash( isset($data['sk']) ? $data['sk'] : '' ) );
129
130 if (empty($stored_sk) || empty($request_sk)) {
131 return false;
132 }
133
134 // Constant-Time Comparison (Prevents Timing Attacks) when possible
135 if (!hash_equals($stored_sk, $request_sk)) {
136 return false;
137 }
138
139 return true;
140 }
141
142 public function keepAliveJS() {
143 if ($this->ajaxInserted) return;
144
145 ?>
146 <script defer type="text/javascript" id="bmip-js-inline-remove-js">
147 function objectToQueryString(obj){
148 return Object.keys(obj).map(key => key + '=' + obj[key]).join('&');
149 }
150
151 function globalBMIKeepAlive() {
152 let xhr = new XMLHttpRequest();
153 let data = { action: "bmip_keepalive", token: "bmip", f: "refresh", nonce: "<?php echo esc_js( wp_create_nonce( 'backup-migration-ajax' ) ); ?>" };
154 let url = '<?php echo esc_url_raw( admin_url("admin-ajax.php") ); ?>' + '?' + objectToQueryString(data);
155 xhr.open('POST', url, true);
156 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
157 xhr.onreadystatechange = function () {
158 if (xhr.readyState === 4) {
159 let response;
160 if (response = JSON.parse(xhr.responseText)) {
161 if (typeof response.status != 'undefined' && response.status === 'success') {
162 //setTimeout(globalBMIKeepAlive, 3000);
163 } else {
164 //setTimeout(globalBMIKeepAlive, 20000);
165 }
166 }
167 }
168 };
169
170 xhr.send(JSON.stringify(data));
171 }
172
173 document.querySelector('#bmip-js-inline-remove-js').remove();
174 </script>
175 <?php
176
177 $this->ajaxInserted = true;
178 }
179
180 function bmip_handle_handshake_request() {
181 $incoming_sk = isset($_POST['sk']) ? sanitize_text_field($_POST['sk']) : '';
182 $challenge = isset($_POST['challenge']) ? sanitize_text_field($_POST['challenge']) : '';
183
184 $stored_sk = get_option('bmi_sk_keepalive');
185
186 if ( ! empty($stored_sk) && ! empty($incoming_sk) && hash_equals($stored_sk, $incoming_sk) ) {
187
188 header('Content-Type: text/plain');
189 echo esc_html( $challenge );
190 exit;
191
192 } else {
193 header('HTTP/1.0 403 Forbidden');
194 echo 'Invalid Handshake';
195 exit;
196 }
197 }
198 }
199