PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
2.1.7 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 weeks ago bodies 2 weeks ago check 2 weeks ago cli 2 weeks ago cron 2 weeks ago dashboard 2 weeks ago database 2 weeks ago external 2 weeks ago extracter 2 weeks ago htaccess 2 weeks ago notices 2 weeks ago progress 2 weeks ago scanner 2 weeks ago services 2 weeks ago staging 2 weeks ago traits 2 weeks ago uploader 2 weeks ago vendor 2 weeks ago zipper 2 weeks ago .htaccess 2 weeks ago activation.php 2 weeks ago ajax.php 2 weeks ago ajax_offline.php 2 weeks ago analyst.php 2 weeks ago backup-process.php 2 weeks ago class-backup-method-mananger.php 2 weeks ago cli-handler.php 2 weeks ago compatibility.php 2 weeks ago config.php 2 weeks ago config_v2.php 2 weeks ago constants.php 2 weeks ago file-explorer.php 2 weeks ago initializer.php 2 weeks ago logger.php 2 weeks ago offline.php 2 weeks ago
offline.php
220 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 add_action('bmip_keepalive_cron', [&$this, 'executeKeepAliveCron']);
32
33 // Handle Auth Handshake For M2M Connection (Ping server)
34 add_action('wp_ajax_nopriv_bmip_auth_handshake', [&$this, 'bmip_handle_handshake_request']);
35 add_action('wp_ajax_bmip_auth_handshake', [&$this, 'bmip_handle_handshake_request']);
36
37 if (is_user_logged_in() && current_user_can('administrator')) {
38 add_action('wp_ajax_backup_migration', [&$this, 'initializeOfflineAjax']);
39 }
40
41 add_filter('allowed_http_origins', function ($origins) {
42 $origins[] = 'https://backupbliss.com';
43 $origins[] = 'https://api.backupbliss.com';
44 return $origins;
45 });
46
47 // $TBU = get_option('bmip_to_be_uploaded', false);
48 // if ($TBU != false && (sizeof($TBU['current_upload']) > 0 || sizeof($TBU['queue']) > 0)) {
49
50 // }
51
52 add_action('admin_footer', [&$this, 'keepAliveJS']);
53
54 }
55
56 /**
57 * initializeOfflineAjax - Initialized Offline handlers for Ajax
58 *
59 * @return void
60 */
61 public function initializeOfflineAjax() {
62
63
64 // Check if the request comes from a logged-in admin (Browser context)
65 // OR from the Ping Server (M2M context)
66 $is_admin = current_user_can('manage_options') && check_ajax_referer('backup-migration-ajax', 'nonce', false);
67 $is_ping_server = $this->verify_ping_server_request();
68
69 if (!$is_admin && !$is_ping_server) {
70 wp_send_json_error('Unauthorized access', 403);
71 return;
72 }
73 // if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
74
75 // Extend execution time
76 if (BMP::isFunctionEnabled('headers_sent') && BMP::isFunctionEnabled('session_status')) {
77 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
78 if (BMP::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
79 if (BMP::isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
80 if (BMP::isFunctionEnabled('ini_set')) {
81 @ini_set('max_execution_time', '259200');
82 @ini_set('max_input_time', '259200');
83 }
84 }
85 }
86
87 if ((isset($_GET['token']) && ($_GET['token'] == 'bmip' || $_GET['token'] == 'bmi') && isset($_GET['f']))) {
88
89 if (empty($_GET)) return;
90
91 // Sanitize User Input
92 $post = BMP::sanitize($_GET);
93
94 } else if ((isset($_POST['token']) && ($_POST['token'] == 'bmip' || $_POST['token'] == 'bmi') && isset($_POST['f']))) {
95
96 if (empty($_POST)) return;
97
98 // Sanitize User Input
99 $post = BMP::sanitize($_POST);
100
101 }
102
103 if (!empty($post)) {
104 do_action("bmi_ajax_offline", $post);
105 }
106
107 // Execution error due to time limit
108 // register_shutdown_function([$this, 'execution_shutdown']);
109
110 // }
111
112 }
113
114 /**
115 * executeKeepAliveCron - Fallback keepalive trigger via WP-Cron
116 *
117 * @return void
118 */
119 public function executeKeepAliveCron() {
120 if (!class_exists('BMI\Plugin\BMI_Ajax_Offline')) {
121 if (file_exists(BMI_INCLUDES . '/ajax_offline.php')) {
122 require_once BMI_INCLUDES . '/ajax_offline.php';
123 } else {
124 return;
125 }
126 }
127
128 $ajaxOffline = new \BMI\Plugin\BMI_Ajax_Offline([]);
129 $ajaxOffline->keepAliveUnAuthorizedRefresh();
130 }
131
132 /**
133 * Verifies the Ping Server handshake.
134 * @return bool true if the request is verified, otherwise it sends a JSON error response and exits.
135 */
136 private function verify_ping_server_request() {
137 $stored_sk = get_option('bmi_sk_keepalive');
138 if (!isset($_SERVER['CONTENT_TYPE']) || stripos($_SERVER['CONTENT_TYPE'], 'application/json') === false) {
139 return false;
140 }
141 $raw = file_get_contents('php://input');
142 $data = json_decode($raw, true);
143 if (json_last_error() !== JSON_ERROR_NONE) {
144 return false;
145 }
146
147 $request_sk = sanitize_text_field( wp_unslash( isset($data['sk']) ? $data['sk'] : '' ) );
148
149 if (empty($stored_sk) || empty($request_sk)) {
150 return false;
151 }
152
153 // Constant-Time Comparison (Prevents Timing Attacks) when possible
154 if (!hash_equals($stored_sk, $request_sk)) {
155 return false;
156 }
157
158 update_option('bmi_cron_last_ping_time', current_time('timestamp'));
159
160 return true;
161 }
162
163 public function keepAliveJS() {
164 if ($this->ajaxInserted) return;
165
166 ?>
167 <script defer type="text/javascript" id="bmip-js-inline-remove-js">
168 function objectToQueryString(obj){
169 return Object.keys(obj).map(key => key + '=' + obj[key]).join('&');
170 }
171
172 function globalBMIKeepAlive() {
173 let xhr = new XMLHttpRequest();
174 let data = { action: "bmip_keepalive", token: "bmip", f: "refresh", nonce: "<?php echo esc_js( wp_create_nonce( 'backup-migration-ajax' ) ); ?>" };
175 let url = '<?php echo esc_url_raw( admin_url("admin-ajax.php") ); ?>' + '?' + objectToQueryString(data);
176 xhr.open('POST', url, true);
177 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
178 xhr.onreadystatechange = function () {
179 if (xhr.readyState === 4) {
180 let response;
181 if (response = JSON.parse(xhr.responseText)) {
182 if (typeof response.status != 'undefined' && response.status === 'success') {
183 //setTimeout(globalBMIKeepAlive, 3000);
184 } else {
185 //setTimeout(globalBMIKeepAlive, 20000);
186 }
187 }
188 }
189 };
190
191 xhr.send(JSON.stringify(data));
192 }
193
194 document.querySelector('#bmip-js-inline-remove-js').remove();
195 </script>
196 <?php
197
198 $this->ajaxInserted = true;
199 }
200
201 function bmip_handle_handshake_request() {
202 $incoming_sk = isset($_POST['sk']) ? sanitize_text_field($_POST['sk']) : '';
203 $challenge = isset($_POST['challenge']) ? sanitize_text_field($_POST['challenge']) : '';
204
205 $stored_sk = get_option('bmi_sk_keepalive');
206
207 if ( ! empty($stored_sk) && ! empty($incoming_sk) && hash_equals($stored_sk, $incoming_sk) ) {
208
209 header('Content-Type: text/plain');
210 echo esc_html( $challenge );
211 exit;
212
213 } else {
214 header('HTTP/1.0 403 Forbidden');
215 echo 'Invalid Handshake';
216 exit;
217 }
218 }
219 }
220