PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 5.3
MainWP Dashboard: Self-hosted WordPress Management for Agencies v5.3
6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-connect.php

class-mainwp-connect.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 5.3, at class/class-mainwp-connect.php

1,943 lines 77.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Connect
4 *
5 * MainWP Connect functions.
6 *
7 * @package MainWP/Dashboard
8 */
9
10 namespace MainWP\Dashboard;
11
12 /**
13 * Class MainWP_Connect
14 *
15 * @package MainWP\Dashboard
16 */
17 class MainWP_Connect { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
18
19 // phpcs:disable WordPress.DB.RestrictedFunctions, Generic.Metrics.CyclomaticComplexity, WordPress.WP.AlternativeFunctions, WordPress.PHP.NoSilencedErrors -- Using cURL functions.
20
21 /**
22 * Method get_class_name()
23 *
24 * Get Class Name.
25 *
26 * @return object Class name.
27 */
28 public static function get_class_name() {
29 return __CLASS__;
30 }
31
32 /**
33 * Method try visit.
34 *
35 * Try connecting to Child Site via cURL.
36 *
37 * @param string $url Child Site URL.
38 * @param bool $ssl_verifyhost Option to check SSL Certificate. Default = null.
39 * @param string $http_user HTTPAuth Username. Default = null.
40 * @param string $http_pass HTTPAuth Password. Default = null.
41 * @param int $sslVersion Child Site SSL Version.
42 * @param bool $forceUseIPv4 Option to force IP4. Default = null.
43 * @param bool $no_body Option to set CURLOPT_NOBODY option. Default = false.
44 *
45 * @return array $out. 'host IP, Returned HTTP Code, Error Message, http Status error message.
46 *
47 * @uses \MainWP\Dashboard\MainWP_Logger::debug()
48 * @uses \MainWP\Dashboard\MainWP_System::$version
49 * @uses \MainWP\Dashboard\MainWP_Utility::value_to_string()
50 * @uses \MainWP\Dashboard\MainWP_Utility::get_http_codes()
51 */
52 public static function try_visit( $url, $ssl_verifyhost = null, $http_user = null, $http_pass = null, $sslVersion = 0, $forceUseIPv4 = null, $no_body = false ) { // phpcs:ignore -- NOSONAR -Current complexity is the only way to achieve desired results, pull request solutions appreciated.
53
54 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
55 $postdata = array( 'test' => 'yes' );
56
57 $ch = curl_init();
58
59 $proxy = new \WP_HTTP_Proxy();
60 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
61 curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
62 curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
63 curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
64
65 if ( $proxy->use_authentication() ) {
66 curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
67 curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
68 }
69 }
70
71 curl_setopt( $ch, CURLOPT_URL, $url );
72 if ( $no_body ) {
73 curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'HEAD' ); // HTTP request is 'HEAD', but sometime return 4xx - error code.
74 }
75 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
76 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
77 curl_setopt( $ch, CURLOPT_POST, true );
78 curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
79 curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 );
80 curl_setopt( $ch, CURLOPT_USERAGENT, $agent );
81 curl_setopt( $ch, CURLOPT_ENCODING, 'none' );
82
83 if ( ! empty( $http_user ) && ! empty( $http_pass ) ) {
84 $http_pass = stripslashes( $http_pass );
85 curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" );
86 }
87
88 if ( $ssl_verifyhost ) {
89 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
90 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
91 } else {
92 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); // NOSONAR.
93 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); // NOSONAR.
94 }
95
96 curl_setopt( $ch, CURLOPT_SSLVERSION, $sslVersion );
97
98 $http_version = apply_filters( 'mainwp_curl_http_version', false, false, $url );
99 if ( false !== $http_version ) {
100 curl_setopt( $ch, CURLOPT_HTTP_VERSION, $http_version );
101 }
102
103 $curlopt_resolve = apply_filters( 'mainwp_curl_curlopt_resolve', false, false, $url );
104 if ( is_array( $curlopt_resolve ) && ! empty( $curlopt_resolve ) ) {
105 curl_setopt( $ch, CURLOPT_RESOLVE, $curlopt_resolve );
106 curl_setopt( $ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false );
107 }
108
109 $headers = array( 'X-Requested-With' => 'XMLHttpRequest' );
110 $headers['Expect'] = static::get_expect_header( $postdata );
111
112 if ( class_exists( '\WpOrg\Requests\Requests' ) ) {
113 $headers = \WpOrg\Requests\Requests::flatten( $headers );
114 } else {
115 $headers = \Requests::flatten( $headers );
116 }
117
118 curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'X-Requested-With: XMLHttpRequest' ) );
119 curl_setopt( $ch, CURLOPT_REFERER, get_option( 'siteurl' ) );
120
121 $force_use_ipv4 = false;
122 if ( null !== $forceUseIPv4 ) {
123 if ( 1 === $forceUseIPv4 ) {
124 $force_use_ipv4 = true;
125 } elseif ( 2 === $forceUseIPv4 ) {
126 if ( 1 === (int) get_option( 'mainwp_forceUseIPv4' ) ) {
127 $force_use_ipv4 = true;
128 }
129 }
130 } elseif ( 1 === (int) get_option( 'mainwp_forceUseIPv4' ) ) {
131 $force_use_ipv4 = true;
132 }
133
134 if ( $force_use_ipv4 && defined( 'CURLOPT_IPRESOLVE' ) && defined( 'CURL_IPRESOLVE_V4' ) ) {
135 curl_setopt( $ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
136 }
137
138 MainWP_Logger::instance()->debug( ' :: trying Visit :: [url=' . $url . ']' );
139
140 $disabled_functions = ini_get( 'disable_functions' );
141 if ( empty( $disabled_functions ) || ( stristr( $disabled_functions, 'curl_multi_exec' ) === false ) ) {
142 MainWP_Logger::instance()->debug( ' :: trying Visit :: curl_multi_exec => enabled.' );
143 $mh = curl_multi_init();
144 @curl_multi_add_handle( $mh, $ch );
145
146 do {
147 curl_multi_exec( $mh, $running );
148 curl_multi_select( $mh );
149 while ( $info = curl_multi_info_read( $mh ) ) {
150 $data = curl_multi_getcontent( $info['handle'] );
151 $err = curl_error( $info['handle'] );
152 $http_status = curl_getinfo( $info['handle'], CURLINFO_HTTP_CODE );
153 $realurl = curl_getinfo( $info['handle'], CURLINFO_EFFECTIVE_URL );
154 curl_multi_remove_handle( $mh, $info['handle'] );
155 }
156 usleep( 10000 );
157
158 } while ( $running > 0 );
159
160 if ( 'resource' === gettype( $mh ) ) {
161 curl_multi_close( $mh );
162 }
163 } else {
164 $data = curl_exec( $ch );
165 $err = curl_error( $ch );
166 $http_status = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
167 $realurl = curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );
168 if ( 'resource' === gettype( $ch ) ) {
169 curl_close( $ch );
170 }
171 }
172
173 MainWP_Logger::instance()->debug( ' :: tryVisit :: [url=' . $url . '] [http_status=' . $http_status . '] [error=' . $err . '] [data-start]' . $data . '[data-end]' );
174 MainWP_Logger::instance()->log_execution_time( 'tryVisit :: [url=' . $url . '] [http_status=' . $http_status . ']' );
175
176 $host = wp_parse_url( ( empty( $realurl ) ? $url : $realurl ), PHP_URL_HOST );
177 $ip = false;
178 $target = false;
179
180 $found = false;
181 $dnsRecord = @dns_get_record( $host );
182 MainWP_Logger::instance()->debug( ' :: tryVisit :: [dnsRecord=' . MainWP_Utility::value_to_string( $dnsRecord, 1 ) . ']' );
183
184 if ( false !== $dnsRecord && is_array( $dnsRecord ) ) {
185 if ( ! isset( $dnsRecord['ip'] ) ) {
186 foreach ( $dnsRecord as $dnsRec ) {
187 if ( isset( $dnsRec['ip'] ) ) {
188 $ip = $dnsRec['ip'];
189 break;
190 }
191 }
192 } else {
193 $ip = $dnsRecord['ip'];
194 }
195
196 if ( ! isset( $dnsRecord['host'] ) ) {
197 foreach ( $dnsRecord as $dnsRec ) {
198 if ( $dnsRec['host'] === $host ) {
199 if ( 'CNAME' === $dnsRec['type'] ) {
200 $target = $dnsRec['target'];
201 }
202 $found = true;
203 break;
204 }
205 }
206 } else {
207 $found = ( $dnsRecord['host'] === $host );
208 if ( 'CNAME' === $dnsRecord['type'] ) {
209 $target = $dnsRecord['target'];
210 }
211 }
212 }
213
214 if ( false === $ip ) {
215 $ip = gethostbynamel( $host );
216 }
217 if ( ( false !== $target ) && ( $target !== $host ) ) {
218 $host .= ' (CNAME: ' . $target . ')';
219 }
220
221 $out = array(
222 'host' => $host,
223 'httpCode' => $http_status,
224 'httpCodeString' => MainWP_Utility::get_http_codes( $http_status ),
225 );
226
227 if ( false !== $ip ) {
228 $out['ip'] = $ip;
229 $found = true;
230 }
231
232 $out['error'] = ( '' === $err && false === $found ? 'Invalid host.' : $err );
233
234 return $out;
235 }
236
237 /**
238 * Method check_ignored_http_code()
239 *
240 * Check if http error code is being ignored.
241 *
242 * @param mixed $value http error code.
243 * @param object|false $website website.
244 *
245 * @return bolean True|False.
246 */
247 public static function check_ignored_http_code( $value, $website = false ) {
248 $value = (int) $value;
249 if ( 200 === $value ) {
250 return true;
251 }
252
253 if ( ! is_object( $website ) || empty( $website->id ) ) {
254 return false;
255 }
256
257 $ignored_code = '';
258 if ( ! property_exists( $website, 'monitor_id' ) ) {
259 $primary_monitor = MainWP_DB_Uptime_Monitoring::instance()->get_monitor_by( $site_id, 'issub', 0 );
260
261 if ( $primary_monitor ) {
262 $global_settings = MainWP_Uptime_Monitoring_Handle::get_global_monitoring_settings();
263 $ignored_code = MainWP_Uptime_Monitoring_Connect::instance()->get_up_codes( $primary_monitor, $global_settings );
264 } else {
265 return false;
266 }
267 }
268
269 if ( ! empty( $ignored_code ) ) {
270 $ignored_code = explode( ',', $ignored_code );
271 foreach ( $ignored_code as $code ) {
272 $code = trim( $code );
273 if ( (int) $value === (int) $code ) {
274 return true;
275 }
276 }
277 }
278 return false;
279 }
280
281 /**
282 * Method check website status.
283 *
284 * Check if the Website returns and http errors.
285 *
286 * @param object $website Child Site information.
287 *
288 * @return mixed False|try visit result.
289 *
290 * @uses \MainWP\Dashboard\MainWP_Utility::is_domain_valid()
291 */
292 public static function check_website_status( $website ) { //phpcs:ignore -- NOSONAR - complexity.
293
294 if ( is_object( $website ) && isset( $website->id ) ) {
295 $primary_monitor = MainWP_DB_Uptime_Monitoring::instance()->get_monitor_by( $website->id, 'issub', 0 );
296 if ( $primary_monitor ) {
297 // return compatible uptime status here.
298 return MainWP_Uptime_Monitoring_Handle::check_website_uptime_monitoring_status( $primary_monitor, array( 'ignore_compatible_save' => 1 ) );
299 }
300 }
301
302 $http_user = null;
303 $http_pass = null;
304 $sslVersion = null;
305 $verifyCertificate = null;
306 $forceUseIPv4 = null;
307 if ( is_object( $website ) && isset( $website->url ) ) {
308 $url = $website->url;
309 $verifyCertificate = isset( $website->verify_certificate ) ? (int) $website->verify_certificate : null;
310 $forceUseIPv4 = $website->force_use_ipv4;
311 $http_user = $website->http_user;
312 $http_pass = $website->http_pass;
313 $sslVersion = $website->ssl_version;
314 } else {
315 $url = $website;
316 }
317
318 if ( ! MainWP_Utility::is_domain_valid( $url ) ) {
319 return false;
320 }
321
322 $ssl_verifyhost = false;
323
324 if ( 1 === $verifyCertificate ) {
325 $ssl_verifyhost = true;
326 } elseif ( 2 === $verifyCertificate || null === $verifyCertificate ) {
327 if ( ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === get_option( 'mainwp_sslVerifyCertificate' ) ) ) {
328 $ssl_verifyhost = true;
329 }
330 }
331
332 $noBody = false;
333 return static::try_visit( $url, $ssl_verifyhost, $http_user, $http_pass, $sslVersion, $forceUseIPv4, $noBody );
334 }
335
336 /**
337 * Method get_post_data_authed()
338 *
339 * Get authorized $_POST data & build query.
340 *
341 * @param mixed $website Array of Child Site Info.
342 * @param mixed $what What we are posting.
343 * @param null $params Post parameters.
344 *
345 * @return mixed null|http_build_query()
346 */
347 public static function get_post_data_authed( &$website, $what, $params = null ) { //phpcs:ignore -- NOSONAR - complex method.
348 if ( $website && '' !== $what ) {
349 $data = array();
350 $data['user'] = $website->adminname;
351 $data['function'] = $what;
352 $data['nonce'] = wp_rand( 0, 9999 );
353 $data['mainwpver'] = MainWP_System::$version;
354
355 $params_filter = apply_filters( 'mainwp_pre_fetch_authed_data', false, $params, $what, $website );
356 if ( is_array( $params_filter ) && ! empty( $params_filter ) ) {
357 $data = array_merge( $data, $params_filter );
358 }
359
360 if ( null !== $params ) {
361 $data = array_merge( $data, $params );
362 }
363
364 $alg = false;
365 $sign_success = null;
366 $use_seclib = false;
367
368 $data = apply_filters( 'mainwp_get_post_data_authed', $data, $website, $what, $params );
369 if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
370 $sign_success = MainWP_Connect_Lib::connect_sign( $what . $data['nonce'], $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
371 $use_seclib = true;
372 } elseif ( function_exists( 'openssl_verify' ) ) {
373 $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
374 $sign_success = static::connect_sign( $what . $data['nonce'], $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
375 if ( false !== $alg ) {
376 $data['sign_algo'] = $alg;
377 }
378 }
379
380 if ( $use_seclib ) {
381 $data['verifylib'] = 1;
382 }
383
384 if ( null !== $sign_success && empty( $sign_success ) ) {
385 $sign_error = '';
386 while ( $msg = openssl_error_string() ) {
387 if ( is_string( $msg ) ) {
388 $sign_error .= $msg;
389 }
390 }
391 MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT SIGN', 'FAILED :: [what=' . ( is_string( $what ) ? $what : '' ) . '] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . ']', false );
392 }
393
394 $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
395
396 /** This filter is documented in ../widgets/widget-mainwp-recent-posts.php */
397 $recent_number = apply_filters( 'mainwp_recent_posts_pages_number', 5 );
398 if ( 5 !== $recent_number ) {
399 $data['recent_number'] = $recent_number;
400 }
401
402 $scan_dir = apply_filters( 'mainwp_stats_scan_dir', false, $website );
403 if ( ! empty( $scan_dir ) ) {
404 $data['scan_dir'] = 1;
405 }
406
407 /**
408 * Current user global.
409 *
410 * @global string
411 */
412 global $current_user;
413
414 if ( ( ! defined( 'DOING_CRON' ) || false === DOING_CRON ) && ( ! defined( 'WP_CLI' ) || false === WP_CLI ) && is_object( $current_user ) && property_exists( $current_user, 'ID' ) && $current_user->ID ) {
415 /**
416 * Filter: mainwp_alter_login_user
417 *
418 * Filters users accounts so it allows you user to jump to child site under alternative administrator account.
419 *
420 * @param int $website->id Child site ID.
421 * @param int $current_user->ID User ID.
422 *
423 * @since Unknown
424 */
425 $alter_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
426 if ( ! empty( $alter_user ) ) {
427 $data['alt_user'] = rawurlencode( $alter_user );
428 }
429 }
430
431 return http_build_query( $data, '', '&' );
432 }
433
434 return null;
435 }
436
437 /**
438 * Method get_renew_post_data_authed()
439 *
440 * Get authorized $_POST data & build query for renew connection action only.
441 *
442 * @param mixed $website Array of Child Site Info.
443 * @param mixed $what What we are posting.
444 *
445 * @return mixed null|http_build_query()
446 */
447 private static function get_renew_post_data_authed( &$website, $what ) { // phpcs:ignore -- NOSONAR - complex.
448
449 if ( $website && '' !== $what ) {
450 $compat_what = 'disconnect'; // to compatible, renew will call disconnect.
451 $data = array();
452 $data['user'] = $website->adminname;
453 $data['function'] = $compat_what;
454 $data['nonce'] = wp_rand( 0, 9999 );
455
456 $alg = false;
457 $sign_success = null;
458 $use_seclib = false;
459
460 if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
461 // to disconnect.
462 $sign_success = MainWP_Connect_Lib::connect_sign( $compat_what . $data['nonce'], $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
463 $use_seclib = true;
464 } elseif ( function_exists( 'openssl_verify' ) ) {
465 $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
466 $sign_success = static::connect_sign( $compat_what . $data['nonce'], $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding.
467 if ( empty( $sign_success ) ) { // error from openssl, openssl_sign().
468 $alg = defined( 'OPENSSL_ALGO_SHA1' ) ? OPENSSL_ALGO_SHA1 : false; // to set default SHA1, to disconnect.
469 MainWP_Logger::instance()->debug_for_website( $website, 'get_renew_post_data_authed', '[' . $website->url . '] :: [openssl_sign:failed] :: Set sign_algo=SHA1' );
470 $sign_success = static::connect_sign( $compat_what . $data['nonce'], $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding.
471 }
472
473 if ( false !== $alg ) {
474 $data['sign_algo'] = $alg;
475 }
476 }
477
478 if ( $use_seclib ) {
479 $data['verifylib'] = 1;
480 }
481
482 if ( null !== $sign_success && empty( $sign_success ) ) {
483 $sign_error = '';
484 while ( $msg = openssl_error_string() ) {
485 if ( is_string( $msg ) ) {
486 $sign_error .= $msg;
487 }
488 }
489 MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT SIGN', 'FAILED :: [what=' . ( is_string( $what ) ? $what : '' ) . '] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . ']', false );
490 }
491
492 $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
493
494 return http_build_query( $data, '', '&' );
495 }
496 return null;
497 }
498
499
500 /**
501 * Method get_get_data_authed()
502 *
503 * Get authorized $_GET data & build query.
504 *
505 * @param mixed $website Child Site data.
506 * @param mixed $paramValue OpenSSL parameter.
507 * @param string $paramName Parameter name.
508 * @param bool $asArray true|false Default is false.
509 * @param array $other_params other params.
510 *
511 * @return string $url
512 */
513 public static function get_get_data_authed( $website, $paramValue, $paramName = 'where', $asArray = false, $other_params = array() ) { //phpcs:ignore -- NOSONAR - complex method.
514 $params = array();
515 if ( $website && '' !== $paramValue ) {
516
517 $sign_success = null;
518 $alg = false;
519 $use_seclib = false;
520 $nonce = wp_rand( 0, 9999 );
521 if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
522 $sign_success = MainWP_Connect_Lib::connect_sign( $paramValue . $nonce, $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
523 $use_seclib = true;
524 } elseif ( function_exists( 'openssl_verify' ) ) {
525 $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
526 $sign_success = static::connect_sign( $paramValue . $nonce, $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
527 }
528
529 $signature = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
530
531 if ( null !== $sign_success && empty( $sign_success ) ) {
532 $sign_error = '';
533 while ( $msg = openssl_error_string() ) {
534 if ( is_string( $msg ) ) {
535 $sign_error .= $msg;
536 }
537 }
538 MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT SIGN', 'FAILED :: [login_required=1] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . ']', false );
539 }
540
541 $params = array(
542 'login_required' => 1,
543 'user' => rawurlencode( $website->adminname ),
544 'mainwpsignature' => rawurlencode( $signature ),
545 'nonce' => $nonce,
546 $paramName => rawurlencode( $paramValue ),
547 );
548
549 if ( is_array( $other_params ) ) {
550 foreach ( $other_params as $name => $value ) {
551 if ( is_string( $name ) && ! empty( $name ) && is_scalar( $value ) ) {
552 $params[ sanitize_text_field( wp_unslash( $name ) ) ] = rawurlencode( sanitize_text_field( wp_unslash( $value ) ) );
553 }
554 }
555 }
556
557 if ( ! empty( $params['login_required'] ) && ! empty( $params['where'] ) ) {
558 $open_params = apply_filters( 'mainwp_open_site_login_required_params', false, $params, $website );
559 if ( is_array( $open_params ) && ! empty( $open_params ) ) {
560 $where_params = '';
561 foreach ( $open_params as $key => $value ) {
562 $where_params .= rawurlencode( sanitize_text_field( wp_unslash( $key ) ) ) . '=' . rawurlencode( sanitize_text_field( wp_unslash( $value ) ) ) . '&';
563 }
564 if ( ! empty( $where_params ) ) {
565 $params['where_params'] = rawurlencode( rtrim( $where_params, '&' ) );
566 }
567 }
568 }
569
570 if ( false !== $alg ) {
571 $params['sign_algo'] = $alg;
572 }
573
574 if ( ! empty( $use_seclib ) ) {
575 $params['verifylib'] = 1;
576 }
577
578 /**
579 * Current user global.
580 *
581 * @global string
582 */
583 global $current_user;
584
585 if ( ( ( ! defined( 'DOING_CRON' ) || false === DOING_CRON ) && ( ! defined( 'WP_CLI' ) || false === WP_CLI ) ) && $current_user && $current_user->ID ) {
586 /** This filter is documented in ../class/class-mainwp-connect.php */
587 $alter_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
588 if ( ! empty( $alter_user ) ) {
589 $params['alt_user'] = rawurlencode( $alter_user );
590 }
591 }
592 }
593
594 if ( $asArray ) {
595 return $params;
596 }
597
598 $url = ( isset( $website->url ) && '' !== $website->url ? $website->url : $website->siteurl );
599 $url .= ( substr( $url, - 1 ) !== '/' ? '/' : '' );
600 $url .= '?';
601
602 foreach ( $params as $key => $value ) {
603 $url .= $key . '=' . $value . '&';
604 }
605 return rtrim( $url, '&' );
606 }
607
608 /**
609 * Method connect_sign()
610 *
611 * Sign connect.
612 *
613 * @param string $data Data sign.
614 * @param string $signature signature.
615 * @param string $privkey Private key.
616 * @param mixed $algorithm signature algorithm.
617 * @param int $site_id site id.
618 *
619 * @return bool Success or not.
620 */
621 public static function connect_sign( $data, &$signature, $privkey, $algorithm, $site_id ) {
622 $de_privkey = MainWP_Encrypt_Data_Lib::instance()->decrypt_privkey( $privkey, $site_id );
623
624 if ( empty( $de_privkey ) ) {
625 $de_privkey = $privkey; // compatible.
626 }
627 if ( false === $algorithm ) {
628 return openssl_sign( $data, $signature, $de_privkey ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
629 } else {
630 return openssl_sign( $data, $signature, $de_privkey, $algorithm ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
631 }
632 }
633
634 /**
635 * Method get_post_data_not_authed()
636 *
637 * Get not authorized $_POST data.
638 *
639 * @param mixed $url Child site URL.
640 * @param mixed $admin Admin Username.
641 * @param mixed $what What function to perform.
642 * @param null $params Function parameters.
643 *
644 * @return mixed null|http_build_query()
645 */
646 public static function get_post_data_not_authed( $url, $admin, $what, $params = null ) {
647 if ( '' !== $url && '' !== $admin && '' !== $what ) {
648 $data = array();
649 $data['user'] = $admin;
650 $data['function'] = $what;
651 $data['mainwpver'] = MainWP_System::$version;
652
653 if ( null !== $params ) {
654 $data = array_merge( $data, $params );
655 }
656
657 return http_build_query( $data, '', '&' );
658 }
659
660 return null;
661 }
662
663 /**
664 * Method fetch_urls_authed()
665 *
666 * Fetch authorized URLs.
667 *
668 * @param object $websites Websites information.
669 * @param string $what Action to perform.
670 * @param array $params Request parameters.
671 * @param mixed $handler Request handler.
672 * @param mixed $output Request output.
673 * @param mixed $whatPage Request URL. Default /admin-ajax.php.
674 * @param array $others Request additional information.
675 *
676 * @return bool true|false
677 *
678 * @uses \MainWP\Dashboard\MainWP_System::$version
679 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_dir()
680 */
681 public static function fetch_urls_authed( &$websites, $what, $params, $handler, &$output, $whatPage = null, $others = array() ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity -- NOSONAR - complex function. Current complexity is the only way to achieve desired results, pull request solutions appreciated.
682
683 if ( ! is_array( $websites ) || empty( $websites ) ) {
684 return false;
685 }
686
687 if ( ! is_array( $params ) ) {
688 $params = array();
689 }
690
691 $chunkSize = apply_filters( 'mainwp_fetch_urls_chunk_size', 10 );
692 if ( count( $websites ) > $chunkSize ) {
693 $total = count( $websites );
694 $loops = ceil( $total / $chunkSize );
695 for ( $i = 0; $i < $loops; $i++ ) {
696 $newSites = array_slice( $websites, $i * $chunkSize, $chunkSize, true );
697 static::fetch_urls_authed( $newSites, $what, $params, $handler, $output, $whatPage, $others );
698 sleep( 5 );
699 }
700
701 return false;
702 }
703
704 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
705 $mh = curl_multi_init();
706
707 $timeout = 20 * 60 * 60;
708
709 $disabled_functions = ini_get( 'disable_functions' );
710 $handleToWebsite = array();
711 $requestUrls = array();
712 $requestHandles = array();
713
714 $dirs = MainWP_System_Utility::get_mainwp_dir();
715 $cookieDir = $dirs[0] . 'cookies';
716
717 static::init_cookiesdir( $cookieDir );
718
719 $_org_params = null;
720
721 foreach ( $websites as $website ) {
722
723 if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) {
724 MainWP_Demo_Handle::get_instance()->handle_fetch_urls_demo( $data, $website, $output, $what, $params );
725 continue;
726 }
727
728 $url = $website->url;
729 if ( '/' !== substr( $url, - 1 ) ) {
730 $url .= '/';
731 }
732
733 if ( false === strpos( $url, 'wp-admin' ) ) {
734 $url .= 'wp-admin/';
735 }
736
737 if ( null !== $whatPage ) {
738 $url .= $whatPage;
739 } else {
740 $url .= 'admin-ajax.php';
741 }
742
743 if ( property_exists( $website, 'http_user' ) ) {
744 $http_user = $website->http_user;
745 }
746 if ( property_exists( $website, 'http_pass' ) ) {
747 $http_pass = $website->http_pass;
748 }
749
750 if ( isset( $params ) && isset( $params['new_post'] ) ) {
751
752 if ( null === $_org_params ) {
753 $_org_params = $params;
754 }
755
756 /**
757 * Filter is being replaced with mainwp_pre_posting_posts.
758 *
759 * @deprecated
760 */
761 $params = apply_filters_deprecated(
762 'mainwp-pre-posting-posts',
763 array(
764 ( is_array( $params ) ? $params : array() ),
765 (object) array(
766 'id' => $website->id,
767 'url' => $website->url,
768 'name' => $website->name,
769 ),
770 ),
771 '4.0.7.2', // NOSONAR - not IP.
772 'mainwp_pre_posting_posts'
773 );
774
775 /**
776 * Filter: mainwp_pre_posting_posts
777 *
778 * Prepares parameters for the authenticated cURL post.
779 *
780 * @since 4.1
781 */
782 $params = apply_filters(
783 'mainwp_pre_posting_posts',
784 ( is_array( $params ) ? $params : array() ),
785 (object) array(
786 'id' => $website->id,
787 'url' => $website->url,
788 'name' => $website->name,
789 )
790 );
791 }
792
793 $ch = curl_init();
794
795 $proxy = new \WP_HTTP_Proxy();
796 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
797 curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
798 curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
799 curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
800
801 if ( $proxy->use_authentication() ) {
802 curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
803 curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
804 }
805 }
806
807 if ( ( null !== $website ) && ( ( property_exists( $website, 'wpe' ) && 1 !== $website->wpe ) || ( isset( $others['upgrade'] ) && ( true === $others['upgrade'] ) ) ) ) {
808 // to fix.
809 if ( defined( 'LOGGED_IN_SALT' ) && defined( 'NONCE_SALT' ) ) {
810 $cookie_salt = sha1( sha1( 'mainwp' . LOGGED_IN_SALT . $website->id ) . NONCE_SALT . 'WP_Cookie' ); // NOSONAR - safe for salt file name.
811 } else {
812 $cookie_salt = sha1( sha1( 'mainwp' . $website->id ) . 'WP_Cookie' ); // NOSONAR - safe for salt file name.
813 }
814 $cookieFile = $cookieDir . '/' . $cookie_salt;
815 if ( ! file_exists( $cookieFile ) ) {
816 @file_put_contents( $cookieFile, '' );
817 }
818
819 if ( file_exists( $cookieFile ) ) {
820 @chmod( $cookieFile, 0644 ); // NOSONAR - correct file permissions, owner: rwe, group & others: r.
821 curl_setopt( $ch, CURLOPT_COOKIEJAR, $cookieFile );
822 curl_setopt( $ch, CURLOPT_COOKIEFILE, $cookieFile );
823 }
824 }
825
826 curl_setopt( $ch, CURLOPT_URL, $url );
827 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
828 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
829 curl_setopt( $ch, CURLOPT_POST, true );
830
831 $postdata = static::get_post_data_authed( $website, $what, $params );
832 curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
833 curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 );
834 curl_setopt( $ch, CURLOPT_USERAGENT, $agent );
835 curl_setopt( $ch, CURLOPT_ENCODING, 'none' );
836 if ( ! empty( $http_user ) && ! empty( $http_pass ) ) {
837 $http_pass = stripslashes( $http_pass );
838 curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" );
839 }
840
841 $ssl_verifyhost = false;
842 $verifyCertificate = isset( $website->verify_certificate ) ? (int) $website->verify_certificate : null;
843 if ( null !== $verifyCertificate ) {
844 if ( 1 === $verifyCertificate ) {
845 $ssl_verifyhost = true;
846 } elseif ( 2 === $verifyCertificate ) {
847 if ( ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === (int) get_option( 'mainwp_sslVerifyCertificate' ) ) ) {
848 $ssl_verifyhost = true;
849 }
850 }
851 } elseif ( ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === (int) get_option( 'mainwp_sslVerifyCertificate' ) ) ) {
852 $ssl_verifyhost = true;
853 }
854
855 if ( $ssl_verifyhost ) {
856 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
857 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
858 } else {
859 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); // NOSONAR.
860 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); // NOSONAR.
861 }
862
863 curl_setopt( $ch, CURLOPT_SSLVERSION, $website->ssl_version );
864
865 if ( is_object( $website ) && property_exists( $website, 'id' ) ) {
866 $http_version = apply_filters( 'mainwp_curl_http_version', false, $website->id );
867 if ( false !== $http_version ) {
868 curl_setopt( $ch, CURLOPT_HTTP_VERSION, $http_version );
869 }
870
871 $curlopt_resolve = apply_filters( 'mainwp_curl_curlopt_resolve', false, $website->id, $website->url );
872 if ( is_array( $curlopt_resolve ) && ! empty( $curlopt_resolve ) ) {
873 curl_setopt( $ch, CURLOPT_RESOLVE, $curlopt_resolve );
874 curl_setopt( $ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false );
875 }
876 }
877
878 curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
879 MainWP_System_Utility::set_time_limit( $timeout );
880
881 if ( empty( $disabled_functions ) || ( false === stristr( $disabled_functions, 'curl_multi_exec' ) ) ) {
882 @curl_multi_add_handle( $mh, $ch );
883 }
884
885 $handleToWebsite[ static::get_resource_id( $ch ) ] = $website;
886 $requestUrls[ static::get_resource_id( $ch ) ] = $website->url;
887 $requestHandles[ static::get_resource_id( $ch ) ] = $ch;
888
889 if ( null !== $_org_params ) {
890 $params = $_org_params;
891 }
892 }
893
894 if ( empty( $disabled_functions ) || ( false === stristr( $disabled_functions, 'curl_multi_exec' ) ) ) {
895 $lastRun = 0;
896 do {
897 if ( 20 < time() - $lastRun ) {
898 MainWP_System_Utility::set_time_limit( $timeout );
899 $lastRun = time();
900 }
901
902 curl_multi_exec( $mh, $running );
903 curl_multi_select( $mh );
904 while ( $info = curl_multi_info_read( $mh ) ) {
905 $data = curl_multi_getcontent( $info['handle'] );
906 $contains = ( 0 < preg_match( '/<mainwp>(.*)<\/mainwp>/', $data, $results ) );
907 curl_multi_remove_handle( $mh, $info['handle'] );
908
909 if ( ! $contains && isset( $requestUrls[ static::get_resource_id( $info['handle'] ) ] ) ) {
910 curl_setopt( $info['handle'], CURLOPT_URL, $requestUrls[ static::get_resource_id( $info['handle'] ) ] );
911 curl_multi_add_handle( $mh, $info['handle'] );
912 unset( $requestUrls[ static::get_resource_id( $info['handle'] ) ] );
913 ++$running;
914 continue;
915 }
916
917 if ( null !== $handler ) {
918 $site = &$handleToWebsite[ static::get_resource_id( $info['handle'] ) ];
919 call_user_func_array( $handler, array( $data, $site, &$output, $params ) );
920 }
921
922 unset( $handleToWebsite[ static::get_resource_id( $info['handle'] ) ] );
923 if ( 'resource' === gettype( $info['handle'] ) ) {
924 curl_close( $info['handle'] );
925 }
926 unset( $info['handle'] );
927 }
928 usleep( 10000 );
929 } while ( $running > 0 );
930
931 if ( 'resource' === gettype( $mh ) ) {
932 curl_multi_close( $mh );
933 }
934 } else {
935 foreach ( $requestHandles as $ch ) {
936 $data = curl_exec( $ch );
937
938 if ( null !== $handler ) {
939 $site = &$handleToWebsite[ static::get_resource_id( $ch ) ];
940 call_user_func_array( $handler, array( $data, $site, &$output, $params ) );
941 }
942 }
943 }
944
945 return true;
946 }
947
948 /**
949 * Credits WordPress org.
950 *
951 * Get the correct "Expect" header for the given request data.
952 *
953 * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
954 * @return string The "Expect" header.
955 */
956 protected static function get_expect_header( $data ) {
957 if ( ! is_array( $data ) ) {
958 return strlen( (string) $data ) >= 1048576 ? '100-Continue' : '';
959 }
960
961 $bytesize = 0;
962 $iterator = new \RecursiveIteratorIterator( new \RecursiveArrayIterator( $data ) );
963
964 foreach ( $iterator as $datum ) {
965 $bytesize += strlen( (string) $datum );
966
967 if ( $bytesize >= 1048576 ) {
968 return '100-Continue';
969 }
970 }
971
972 return '';
973 }
974
975 /**
976 * Method get_resource_id()
977 *
978 * Get resource id.
979 *
980 * @param mixed $res The given resource.
981 *
982 * @return $result Resource ID only.
983 */
984 public static function get_resource_id( $res ) {
985 $result = false;
986 if ( is_a( $res, 'CurlHandle' ) ) {
987 $result = spl_object_hash( $res );
988 } elseif ( is_resource( $res ) ) {
989 $resourceString = (string) $res;
990 $exploded = explode( '#', $resourceString );
991 $result = array_pop( $exploded );
992 }
993 return $result;
994 }
995
996 /**
997 * Method get_lock_identifier().
998 *
999 * Get lock identifier.
1000 *
1001 * @param mixed $pLockName Provided Lock Name.
1002 *
1003 * @return mixed false|sem_get()|@fopen
1004 */
1005 public static function get_lock_identifier( $pLockName ) {
1006 if ( ( null === $pLockName ) || ( false === $pLockName ) ) {
1007 return false;
1008 }
1009
1010 if ( function_exists( 'sem_get' ) ) {
1011 return sem_get( $pLockName );
1012 } else {
1013 $fh = @fopen( sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'lock' . $pLockName . '.txt', 'w+' );
1014 if ( ! $fh ) {
1015 return false;
1016 }
1017
1018 return $fh;
1019 }
1020 }
1021
1022 /**
1023 * Method lock()
1024 *
1025 * Use sem_acquire or @flock to lock the $identifier.
1026 *
1027 * @param mixed $identifier Identifier.
1028 *
1029 * @return mixed false|sem_acquire()|@flock
1030 */
1031 public static function lock( $identifier ) {
1032 if ( ( null === $identifier ) || ( false === $identifier ) ) {
1033 return false;
1034 }
1035
1036 if ( function_exists( 'sem_acquire' ) ) {
1037 return sem_acquire( $identifier );
1038 } else {
1039 if ( ! is_resource( $identifier ) ) {
1040 return false; // to fix.
1041 }
1042 for ( $i = 0; $i < 3; $i++ ) {
1043 if ( @flock( $identifier, LOCK_EX ) ) {
1044 return $identifier;
1045 } else {
1046 sleep( 1 );
1047 }
1048 }
1049 return false;
1050 }
1051 }
1052
1053 /**
1054 * Method release()
1055 *
1056 * Use sem_release or @flock, @fclose to unlock $identifier.
1057 *
1058 * @param mixed $identifier Identifier.
1059 *
1060 * @return mixed false|sem_release()|@flock
1061 */
1062 public static function release( $identifier ) {
1063 if ( ( null === $identifier ) || ( false === $identifier ) ) {
1064 return false;
1065 }
1066
1067 if ( function_exists( 'sem_release' ) ) {
1068 return sem_release( $identifier );
1069 } else {
1070 if ( ! is_resource( $identifier ) ) {
1071 return false; // to fix.
1072 }
1073 @flock( $identifier, LOCK_UN );
1074 @fclose( $identifier );
1075 }
1076
1077 return false;
1078 }
1079
1080 /**
1081 * Method fetch_url_authed()
1082 *
1083 * Updates the child site via authenticated request.
1084 *
1085 * @param object $website Website information.
1086 * @param string $what Function to perform.
1087 * @param null $params Function parameters.
1088 * @param bool $checkConstraints Whether or not to check constraints.
1089 * @param bool $pForceFetch Whether or not to force the fetch.
1090 * @param bool $pRetryFailed Whether or not to retry the fetch process.
1091 * @param null $rawResponse Raw response.
1092 *
1093 * @return mixed $information
1094 *
1095 * @uses \MainWP\Dashboard\MainWP_Monitoring_Handler::handle_check_website()
1096 * @uses \MainWP\Dashboard\MainWP_Premium_Update::maybe_request_premium_updates()
1097 * @uses \MainWP\Dashboard\MainWP_Sync::sync_information_array()
1098 */
1099 public static function fetch_url_authed( // phpcs:ignore -- NOSONAR - complex.
1100 &$website,
1101 $what,
1102 $params = null,
1103 $checkConstraints = false,
1104 $pForceFetch = false,
1105 $pRetryFailed = true,
1106 $rawResponse = null
1107 ) {
1108 unset( $pForceFetch );
1109
1110 // to support demo data.
1111 if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) {
1112 return MainWP_Demo_Handle::get_instance()->handle_action_demo( $website, $what );
1113 }
1114
1115 if ( ! is_array( $params ) ) {
1116 $params = array();
1117 }
1118
1119 $others = array(
1120 'force_use_ipv4' => $website->force_use_ipv4,
1121 'upgrade' => ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ),
1122 );
1123
1124 $request_update = MainWP_Premium_Update::maybe_request_premium_updates( $website, $what, $params );
1125
1126 if ( isset( $rawResponse ) && $rawResponse ) {
1127 $others['raw_response'] = 'yes';
1128 }
1129
1130 $params['optimize'] = ( ( 1 === (int) get_option( 'mainwp_optimize', 1 ) ) ? 1 : 0 );
1131
1132 $updating_website = false;
1133 $type = '';
1134 $list = '';
1135 if ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ) {
1136 $updating_website = true;
1137 if ( 'upgradeplugintheme' === $what || 'upgradetranslation' === $what ) {
1138 $type = $params['type'];
1139 $list = $params['list'];
1140 } else {
1141 $type = 'wp';
1142 $list = '';
1143 }
1144 }
1145
1146 if ( $updating_website ) {
1147 /**
1148 * Action: mainwp_website_before_updated
1149 *
1150 * Fires before the child site update process.
1151 *
1152 * @param object $website Object containing child site info.
1153 * @param string $type Type parameter.
1154 * @param string $list List parameter.
1155 *
1156 * @since Unknown
1157 */
1158 do_action( 'mainwp_website_before_updated', $website, $type, $list );
1159 }
1160
1161 if ( 'renew' === $what ) {
1162 $postdata = static::get_renew_post_data_authed( $website, $what );
1163 } else {
1164 $postdata = static::get_post_data_authed( $website, $what, $params );
1165
1166 }
1167 $others['function'] = $what;
1168
1169 $information = array();
1170
1171 if ( ! $request_update ) {
1172 $information = static::fetch_url( $website, $website->url, $postdata, $checkConstraints, $website->verify_certificate, $pRetryFailed, $website->http_user, $website->http_pass, $website->ssl_version, $others );
1173 /**
1174 * Fires immediately after fetch url action.
1175 *
1176 * @param object $website website.
1177 * @param array $information information result data.
1178 * @param string $what action.
1179 * @param array $params params input array.
1180 * @param array $others others input array.
1181 *
1182 * @since 4.5.1.1
1183 */
1184 do_action( 'mainwp_fetch_url_authed', $website, $information, $what, $params, $others );
1185 } else {
1186 $slug = $params['list'];
1187 $information['upgrades'] = array( $slug => 1 );
1188 }
1189
1190 if ( is_array( $information ) && isset( $information['sync'] ) && ! empty( $information['sync'] ) ) {
1191 MainWP_Sync::sync_information_array( $website, $information['sync'] );
1192 unset( $information['sync'] );
1193 }
1194
1195 if ( $updating_website ) {
1196 /**
1197 * Action: mainwp_website_updated
1198 *
1199 * Fires after the child site update process.
1200 *
1201 * @param object $website Object containing child site info.
1202 * @param string $type Type parameter.
1203 * @param string $list List parameter.
1204 * @param array $information Array containing the information fetched from the child site.
1205 *
1206 * @since Unknown
1207 */
1208 do_action( 'mainwp_website_updated', $website, $type, $list, $information );
1209 if ( 1 === (int) get_option( 'mainwp_check_http_response', 0 ) ) {
1210 MainWP_Monitoring_Handler::handle_check_website( $website );
1211 }
1212 }
1213
1214 return $information;
1215 }
1216
1217 /**
1218 * Method fetch_url_not_authed()
1219 *
1220 * Fetch not authorized URL.
1221 *
1222 * @param string $url URL to fetch from.
1223 * @param string $admin Admin name.
1224 * @param string $what Function to perform.
1225 * @param null $params Function parameters.
1226 * @param bool $pForceFetch true|false Whether or not to force the fetch.
1227 * @param null $verifyCertificate Verify the SSL Certificate.
1228 * @param null $http_user htaccess username.
1229 * @param null $http_pass htaccess password.
1230 * @param integer $sslVersion SSL version to check for.
1231 * @param array $others Other functions to perform.
1232 * @param array $output Output values.
1233 *
1234 * @return mixed static::fetch_url() Fetch URL.
1235 */
1236 public static function fetch_url_not_authed( // NOSONAR - compatible.
1237 $url,
1238 $admin,
1239 $what,
1240 $params = null,
1241 $pForceFetch = false,
1242 $verifyCertificate = null,
1243 $http_user = null,
1244 $http_pass = null,
1245 $sslVersion = 0,
1246 $others = array(),
1247 &$output = array()
1248 ) {
1249 unset( $pForceFetch );
1250
1251 if ( empty( $params ) ) {
1252 $params = array();
1253 }
1254
1255 $postdata = static::get_post_data_not_authed( $url, $admin, $what, $params );
1256 $website = null;
1257
1258 $others['function'] = $what;
1259 return static::fetch_url( $website, $url, $postdata, false, $verifyCertificate, true, $http_user, $http_pass, $sslVersion, $others, $output );
1260 }
1261
1262 /**
1263 * Method fetch_url()
1264 *
1265 * Fetch URL.
1266 *
1267 * @param object $website Child Site info.
1268 * @param string $url URL to fetch from.
1269 * @param mixed $postdata Post data to fetch.
1270 * @param bool $checkConstraints true|false Whether or not to check constraints.
1271 * @param null $verifyCertificate Verify SSL Certificate.
1272 * @param bool $pRetryFailed ture|false Whether or not the Retry has failed.
1273 * @param null $http_user htaccess username.
1274 * @param null $http_pass htaccess password.
1275 * @param integer $sslVersion SSL version.
1276 * @param array $others Other functions to perform.
1277 * @param array $output Output values.
1278 *
1279 * @throws \Exception Exception message.
1280 *
1281 * @return mixed static::fetch_url_site()
1282 */
1283 public static function fetch_url( // phpcs:ignore -- NOSONAR - complex.
1284 &$website,
1285 $url,
1286 $postdata,
1287 $checkConstraints = false,
1288 $verifyCertificate = null,
1289 $pRetryFailed = true,
1290 $http_user = null,
1291 $http_pass = null,
1292 $sslVersion = 0,
1293 $others = array(),
1294 &$output = array()
1295 ) {
1296
1297 $start = time();
1298
1299 try {
1300 $tmpUrl = $url;
1301 if ( '/' !== substr( $tmpUrl, - 1 ) ) {
1302 $tmpUrl .= '/';
1303 }
1304
1305 if ( false === strpos( $url, 'wp-admin' ) ) {
1306 $tmpUrl .= 'wp-admin/admin-ajax.php';
1307 }
1308
1309 return static::fetch_url_site( $website, $tmpUrl, $postdata, $checkConstraints, $verifyCertificate, $http_user, $http_pass, $sslVersion, $others, $output );
1310 } catch ( \Exception $e ) {
1311 if ( ! $pRetryFailed || ( 30 < ( time() - $start ) ) ) {
1312 throw $e;
1313 }
1314
1315 try {
1316 return static::fetch_url_site( $website, $url, $postdata, $checkConstraints, $verifyCertificate, $http_user, $http_pass, $sslVersion, $others, $output );
1317 } catch ( \Exception $ex ) {
1318 throw $e;
1319 }
1320 }
1321 }
1322
1323 /**
1324 * Method fetch_url_site()
1325 *
1326 * M Fetch URL.
1327 *
1328 * @param object $website Child Site info.
1329 * @param string $url URL to fetch from.
1330 * @param mixed $postdata Post data to fetch.
1331 * @param bool $checkConstraints true|false Whether or not to check constraints.
1332 * @param null $verifyCertificate Verify SSL Certificate.
1333 * @param null $http_user htaccess username.
1334 * @param null $http_pass htaccess password.
1335 * @param integer $sslVersion SSL version.
1336 * @param array $others Other functions to perform.
1337 * @param array $output Output values.
1338 *
1339 * @return mixed $data, $information.
1340 * @throws MainWP_Exception Exception message.
1341 *
1342 * @uses \MainWP\Dashboard\MainWP_DB_Common::insert_or_update_request_log()
1343 * @uses \MainWP\Dashboard\MainWP_Exception
1344 * @uses \MainWP\Dashboard\MainWP_Logger::debug_for_website()
1345 * @uses \MainWP\Dashboard\MainWP_System::$version
1346 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_dir()
1347 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_child_response()
1348 * @uses \MainWP\Dashboard\MainWP_Utility::value_to_string()
1349 * @uses \MainWP\Dashboard\MainWP_Utility::end_session()
1350 */
1351 public static function fetch_url_site( // phpcs:ignore -- NOSONAR - complex method. Current complexity is the only way to achieve desired results, pull request solutions appreciated.
1352 &$website,
1353 $url,
1354 $postdata,
1355 $checkConstraints = false,
1356 $verifyCertificate = null,
1357 $http_user = null,
1358 $http_pass = null,
1359 $sslVersion = 0,
1360 $others = array(),
1361 &$output = array()
1362 ) {
1363
1364 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
1365
1366 if ( ! empty( $website ) ) {
1367 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Request to [' . $url . '] [' . MainWP_Utility::value_to_string( $postdata, 1 ) . ']' );
1368 }
1369
1370 $identifier = null;
1371 if ( $checkConstraints ) {
1372 static::check_constraints( $identifier, $website );
1373 }
1374
1375 if ( null !== $website ) {
1376 MainWP_DB_Common::instance()->insert_or_update_request_log( $website->id, null, microtime( true ), null );
1377 }
1378
1379 if ( null !== $identifier ) {
1380 static::release( $identifier );
1381 }
1382
1383 $dirs = MainWP_System_Utility::get_mainwp_dir();
1384 $cookieDir = $dirs[0] . 'cookies';
1385
1386 static::init_cookiesdir( $cookieDir );
1387
1388 $ch = curl_init();
1389
1390 $proxy = new \WP_HTTP_Proxy();
1391 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
1392 curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
1393 curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
1394 curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
1395
1396 if ( $proxy->use_authentication() ) {
1397 curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
1398 curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
1399 }
1400 }
1401
1402 if ( ( null !== $website ) && ( ( property_exists( $website, 'wpe' ) && 1 !== $website->wpe ) || ( isset( $others['upgrade'] ) && ( true === $others['upgrade'] ) ) ) ) {
1403 // to fix.
1404 if ( defined( 'LOGGED_IN_SALT' ) && defined( 'NONCE_SALT' ) ) {
1405 $cookie_salt = sha1( sha1( 'mainwp' . LOGGED_IN_SALT . $website->id ) . NONCE_SALT . 'WP_Cookie' ); // NOSONAR - safe for salt file name.
1406 } else {
1407 $cookie_salt = sha1( sha1( 'mainwp' . $website->id ) . 'WP_Cookie' ); // NOSONAR - safe for salt file name.
1408 }
1409 $cookieFile = $cookieDir . '/' . $cookie_salt;
1410 if ( ! file_exists( $cookieFile ) ) {
1411 @file_put_contents( $cookieFile, '' );
1412 }
1413
1414 if ( file_exists( $cookieFile ) ) {
1415 @chmod( $cookieFile, 0644 ); // NOSONAR - correct file permissions, owner: rwe, group & others: r.
1416 curl_setopt( $ch, CURLOPT_COOKIEJAR, $cookieFile );
1417 curl_setopt( $ch, CURLOPT_COOKIEFILE, $cookieFile );
1418 }
1419 }
1420
1421 curl_setopt( $ch, CURLOPT_URL, $url );
1422 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1423 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
1424 curl_setopt( $ch, CURLOPT_POST, true );
1425 curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
1426 curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 );
1427 curl_setopt( $ch, CURLOPT_USERAGENT, $agent );
1428 curl_setopt( $ch, CURLOPT_ENCODING, 'none' );
1429
1430 if ( ! empty( $http_user ) && ! empty( $http_pass ) ) {
1431 $http_pass = stripslashes( $http_pass );
1432 curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" );
1433 }
1434
1435 $ssl_verifyhost = false;
1436 if ( null !== $verifyCertificate ) {
1437 if ( 1 === (int) $verifyCertificate ) {
1438 $ssl_verifyhost = true;
1439 } elseif ( 2 === (int) $verifyCertificate ) {
1440 if ( ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === (int) get_option( 'mainwp_sslVerifyCertificate' ) ) ) {
1441 $ssl_verifyhost = true;
1442 }
1443 }
1444 } elseif ( ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === (int) get_option( 'mainwp_sslVerifyCertificate' ) ) ) {
1445 $ssl_verifyhost = true;
1446 }
1447
1448 if ( $ssl_verifyhost ) {
1449 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
1450 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
1451 } else {
1452 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); // NOSONAR.
1453 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); // NOSONAR.
1454 }
1455
1456 curl_setopt( $ch, CURLOPT_SSLVERSION, $sslVersion );
1457
1458 $http_version = apply_filters( 'mainwp_curl_http_version', false, $website ? $website->id : false, $url );
1459 if ( false !== $http_version ) {
1460 curl_setopt( $ch, CURLOPT_HTTP_VERSION, $http_version );
1461 }
1462
1463 $curlopt_resolve = false;
1464
1465 if ( is_object( $website ) && property_exists( $website, 'id' ) ) {
1466 $curlopt_resolve = apply_filters( 'mainwp_curl_curlopt_resolve', false, $website->id, $website->url );
1467 }
1468
1469 if ( is_array( $curlopt_resolve ) && ! empty( $curlopt_resolve ) ) {
1470 curl_setopt( $ch, CURLOPT_RESOLVE, $curlopt_resolve );
1471 curl_setopt( $ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false );
1472 }
1473
1474 $headers = array( 'X-Requested-With' => 'XMLHttpRequest' );
1475 $headers['Expect'] = static::get_expect_header( $postdata );
1476
1477 if ( class_exists( '\WpOrg\Requests\Requests' ) ) {
1478 $headers = \WpOrg\Requests\Requests::flatten( $headers );
1479 } else {
1480 $headers = \Requests::flatten( $headers );
1481 }
1482
1483 curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
1484 curl_setopt( $ch, CURLOPT_REFERER, get_option( 'siteurl' ) );
1485
1486 $force_use_ipv4 = false;
1487 $forceUseIPv4 = isset( $others['force_use_ipv4'] ) ? (int) $others['force_use_ipv4'] : null;
1488 if ( null !== $forceUseIPv4 ) {
1489 if ( 1 === $forceUseIPv4 ) {
1490 $force_use_ipv4 = true;
1491 } elseif ( 2 === $forceUseIPv4 ) {
1492 if ( 1 === (int) get_option( 'mainwp_forceUseIPv4' ) ) {
1493 $force_use_ipv4 = true;
1494 }
1495 }
1496 } elseif ( 1 === (int) get_option( 'mainwp_forceUseIPv4' ) ) {
1497 $force_use_ipv4 = true;
1498 }
1499
1500 if ( $force_use_ipv4 && defined( 'CURLOPT_IPRESOLVE' ) && defined( 'CURL_IPRESOLVE_V4' ) ) {
1501 curl_setopt( $ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
1502 }
1503
1504 $timeout = 20 * 60 * 60;
1505 curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
1506 MainWP_System_Utility::set_time_limit( $timeout );
1507
1508 MainWP_Utility::end_session();
1509
1510 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Executing handlers' );
1511
1512 $disabled_functions = ini_get( 'disable_functions' );
1513 if ( empty( $disabled_functions ) || ( false === stristr( $disabled_functions, 'curl_multi_exec' ) ) ) {
1514 $mh = @curl_multi_init();
1515 @curl_multi_add_handle( $mh, $ch );
1516
1517 $lastRun = 0;
1518 do {
1519 if ( 20 < time() - $lastRun ) {
1520 MainWP_System_Utility::set_time_limit( $timeout );
1521 $lastRun = time();
1522 }
1523 @curl_multi_exec( $mh, $running );
1524 @curl_multi_select( $mh );
1525 while ( $info = @curl_multi_info_read( $mh ) ) {
1526 $data = @curl_multi_getcontent( $info['handle'] );
1527
1528 $http_status = @curl_getinfo( $info['handle'], CURLINFO_HTTP_CODE );
1529 $err = @curl_error( $info['handle'] );
1530 $real_url = @curl_getinfo( $info['handle'], CURLINFO_EFFECTIVE_URL );
1531
1532 @curl_multi_remove_handle( $mh, $info['handle'] );
1533 }
1534 usleep( 10000 );
1535 } while ( $running > 0 );
1536 if ( 'resource' === gettype( $mh ) ) {
1537 @curl_multi_close( $mh );
1538 }
1539 } else {
1540 $data = @curl_exec( $ch );
1541 $http_status = @curl_getinfo( $ch, CURLINFO_HTTP_CODE );
1542 $err = @curl_error( $ch );
1543 $real_url = @curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );
1544 }
1545
1546 $host = wp_parse_url( $real_url, PHP_URL_HOST );
1547 $ip = gethostbyname( $host );
1548
1549 if ( null !== $website ) {
1550 MainWP_DB_Common::instance()->insert_or_update_request_log( $website->id, $ip, null, microtime( true ) );
1551 }
1552
1553 $raw_response = isset( $others['raw_response'] ) && 'yes' === $others['raw_response'] ? true : false;
1554
1555 $output['fetch_data'] = $data;
1556 $output['http_status'] = (int) $http_status;
1557
1558 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'http status: [' . $http_status . '] err: [' . $err . ']' );
1559 if ( '400' === $http_status ) {
1560 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'post data: [' . MainWP_Utility::value_to_string( $postdata, 1 ) . ']' );
1561 }
1562
1563 MainWP_Logger::instance()->log_execution_time( 'fetch_url_site :: [url=' . $url . ']' );
1564
1565 $thr_error = null;
1566
1567 if ( ( false === $data ) && empty( $http_status ) ) {
1568 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] HTTP Error: [status=0][' . $err . ']' );
1569 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1570 } elseif ( empty( $data ) && ! empty( $err ) ) {
1571 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] HTTP Error: [status=' . $http_status . '][' . $err . ']' );
1572 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1573 } elseif ( 0 < preg_match( '/<mainwp>(.*)<\/mainwp>/', $data, $results ) ) {
1574 $result = $results[1];
1575 $information = MainWP_System_Utility::get_child_response( base64_decode( $result ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
1576 unset( $output['fetch_data'] ); // hide the data.
1577 $pdt = is_string( $postdata ) ? $postdata : '';
1578 $data_log = is_array( $postdata ) ? print_r( $postdata, true ) : $pdt; //phpcs:ignore -- good.
1579 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', '[' . $url . '] postdata [' . $data_log . '] information: [OK]' ); //phpcs:ignore -- ok.
1580 return $information;
1581 } elseif ( 200 === (int) $http_status && ! empty( $err ) ) {
1582 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1583 } elseif ( $raw_response ) {
1584 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Response: [RAW]' );
1585 return $data;
1586 } else {
1587 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] Error: NOMAINWP [data=' . ( is_string( $data ) ? $data : 'OBJECT' ) . ']' );
1588 $detect_wsidchk = is_string( $data ) ? strpos( $data, 'wsidchk' ) : false;
1589 if ( false !== $detect_wsidchk ) {
1590 $thr_error = new MainWP_Exception( 'ERROR:Connection Failed. We suspect that Imunify360, a security layer added by your host, is causing this problem. Please contact your host to whitelist your Dashboard IP in their system. If you need help determining your MainWP Dashboard site IP address, check with your hosting provider.', $url );
1591 } else {
1592 $thr_error = new MainWP_Exception( 'NOMAINWP', $url ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1593 }
1594 }
1595
1596 if ( null !== $thr_error ) {
1597 $thr_error->set_data( $data );
1598 throw $thr_error;
1599 }
1600 }
1601
1602 /**
1603 * Method check_constraints()
1604 *
1605 * Check connection delay constraints.
1606 *
1607 * @param mixed $identifier Lock identifier.
1608 * @param mixed $website Object child site.
1609 *
1610 * @uses \MainWP\Dashboard\MainWP_DB_Common::close_open_requests()
1611 * @uses \MainWP\Dashboard\MainWP_DB::get_wp_ip()
1612 * @uses \MainWP\Dashboard\MainWP_Utility::end_session()
1613 */
1614 private static function check_constraints( &$identifier, $website ) { // phpcs:ignore -- NOSONAR -Current complexity is the only way to achieve desired results, pull request solutions appreciated.
1615 $semLock = '103218';
1616 $identifier = static::get_lock_identifier( $semLock );
1617 $minimumDelay = ( ( false === get_option( 'mainwp_minimumDelay' ) ) ? 200 : get_option( 'mainwp_minimumDelay' ) );
1618 if ( 0 < $minimumDelay ) {
1619 $minimumDelay = $minimumDelay / 1000;
1620 }
1621 $minimumIPDelay = ( ( false === get_option( 'mainwp_minimumIPDelay' ) ) ? 1000 : get_option( 'mainwp_minimumIPDelay' ) );
1622 if ( 0 < $minimumIPDelay ) {
1623 $minimumIPDelay = $minimumIPDelay / 1000;
1624 }
1625
1626 MainWP_Utility::end_session();
1627 $delay = true;
1628 while ( $delay ) {
1629 static::lock( $identifier );
1630 if ( 0 < $minimumDelay && static::check_constraints_last_request( $identifier, $minimumDelay ) ) {
1631 continue;
1632 }
1633
1634 if ( 0 < $minimumIPDelay && null !== $website ) {
1635 $ip = MainWP_DB::instance()->get_wp_ip( $website->id );
1636 if ( null !== $ip && '' !== $ip && static::check_constraints_last_request( $identifier, $minimumIPDelay, $ip ) ) {
1637 continue;
1638 }
1639 }
1640 $delay = false;
1641 }
1642
1643 $maximumRequests = ( ( false === get_option( 'mainwp_maximumRequests' ) ) ? 4 : get_option( 'mainwp_maximumRequests' ) );
1644 $maximumIPRequests = ( ( false === get_option( 'mainwp_maximumIPRequests' ) ) ? 1 : get_option( 'mainwp_maximumIPRequests' ) );
1645
1646 $first = true;
1647 $delay = true;
1648 while ( $delay ) {
1649 if ( ! $first ) {
1650 static::lock( $identifier );
1651 } else {
1652 $first = false;
1653 }
1654
1655 MainWP_DB_Common::instance()->close_open_requests();
1656
1657 if ( 0 < $maximumRequests && static::check_constraints_open_requests( $identifier, $maximumRequests ) ) {
1658 continue;
1659 }
1660
1661 if ( 0 < $maximumIPRequests && null !== $website ) {
1662 $ip = MainWP_DB::instance()->get_wp_ip( $website->id );
1663 if ( null !== $ip && '' !== $ip && static::check_constraints_open_requests( $identifier, $maximumIPRequests, $ip ) ) {
1664 continue;
1665 }
1666 }
1667 $delay = false;
1668 }
1669 }
1670
1671 /**
1672 * Method check_constraints_last_request().
1673 *
1674 * Check constraints for last requests.
1675 *
1676 * @param mixed $identifier connect identifier.
1677 * @param int $minimumDelay minimum delay.
1678 * @param string|null $ip ip address.
1679 *
1680 * @uses \MainWP\Dashboard\MainWP_DB_Common::get_last_request_timestamp()
1681 */
1682 private static function check_constraints_last_request( $identifier, $minimumDelay, $ip = null ) {
1683 $lastRequest = MainWP_DB_Common::instance()->get_last_request_timestamp( $ip );
1684 if ( $lastRequest > ( ( microtime( true ) ) - $minimumDelay ) ) {
1685 static::release( $identifier );
1686 $sleep = ( $minimumDelay - ( ( microtime( true ) ) - $lastRequest ) ) * 1000 * 1000;
1687 $sleep = intval( $sleep );
1688 usleep( $sleep );
1689 return true;
1690 }
1691 return false;
1692 }
1693
1694 /**
1695 * Method check_constraints_open_requests().
1696 *
1697 * Check constraints for open requests.
1698 *
1699 * @param mixed $identifier connect identifier.
1700 * @param int $maximumRequests maximum requests.
1701 * @param string|null $ip ip address.
1702 *
1703 * @uses \MainWP\Dashboard\MainWP_DB_Common::get_nrof_open_requests()
1704 */
1705 private static function check_constraints_open_requests( $identifier, $maximumRequests, $ip = null ) {
1706 $nrOfOpenRequests = MainWP_DB_Common::instance()->get_nrof_open_requests( $ip );
1707 if ( $nrOfOpenRequests >= $maximumRequests ) {
1708 static::release( $identifier );
1709 usleep( 200000 );
1710 return true;
1711 }
1712 return false;
1713 }
1714
1715 /**
1716 * Method download_to_file()
1717 *
1718 * Download to file.
1719 *
1720 * @param mixed $url Download URL.
1721 * @param mixed $file File to download to.
1722 * @param bool $size Size of file.
1723 * @param null $http_user htaccess username.
1724 * @param null $http_pass htaccess password.
1725 *
1726 * @throws MainWP_Exception Exception message.
1727 *
1728 * @uses \MainWP\Dashboard\MainWP_Exception
1729 * @uses \MainWP\Dashboard\MainWP_System::$version
1730 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_wp_file_system()
1731 */
1732 public static function download_to_file( $url, $file, $size = false, $http_user = null, $http_pass = null ) { // phpcs:ignore -- NOSONAR - complex.
1733
1734 $hasWPFileSystem = MainWP_System_Utility::get_wp_file_system();
1735
1736 /**
1737 * WordPress files system object.
1738 *
1739 * @global object
1740 */
1741 global $wp_filesystem;
1742
1743 if ( $wp_filesystem->exists( $file ) && ( ( false === $size ) || ( $wp_filesystem->size( $file ) > $size ) ) ) {
1744 $wp_filesystem->delete( $file );
1745 }
1746
1747 if ( ! $wp_filesystem->exists( dirname( $file ) ) ) {
1748 $wp_filesystem->mkdir( dirname( $file ), 0777 );
1749 }
1750
1751 if ( ! $wp_filesystem->exists( dirname( $file ) ) ) {
1752 throw new MainWP_Exception( esc_html__( 'MainWP plugin could not create directory in order to download the file.', 'mainwp' ) );
1753 }
1754
1755 if ( $hasWPFileSystem && ! empty( $wp_filesystem ) ) {
1756 if ( ! $wp_filesystem->is_writable( @dirname( $file ) ) ) {
1757 throw new MainWP_Exception( esc_html__( 'MainWP upload directory is not writable.', 'mainwp' ) );
1758 }
1759 } elseif ( ! is_writable( @dirname( $file ) ) ) { //phpcs:ignore -- ok.
1760 throw new MainWP_Exception( esc_html__( 'MainWP upload directory is not writable.', 'mainwp' ) );
1761 }
1762
1763 $fp = fopen( $file, 'a' );
1764 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
1765 if ( false !== $size && $wp_filesystem->exists( $file ) ) {
1766 $size = $wp_filesystem->size( $file );
1767 $url .= '&foffset=' . $size;
1768 }
1769 $ch = curl_init( str_replace( ' ', '%20', $url ) );
1770
1771 $proxy = new \WP_HTTP_Proxy();
1772 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
1773 curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
1774 curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
1775 curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
1776
1777 if ( $proxy->use_authentication() ) {
1778 curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
1779 curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
1780 }
1781 }
1782 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
1783 curl_setopt( $ch, CURLOPT_HEADER, false );
1784 curl_setopt( $ch, CURLOPT_FILE, $fp );
1785
1786 curl_setopt( $ch, CURLOPT_USERAGENT, $agent );
1787 curl_setopt( $ch, CURLOPT_ENCODING, 'none' );
1788
1789 curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
1790 if ( ! empty( $http_user ) && ! empty( $http_pass ) ) {
1791 $http_pass = stripslashes( $http_pass );
1792 curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" );
1793 }
1794 curl_exec( $ch );
1795 if ( 'resource' === gettype( $ch ) ) {
1796 curl_close( $ch );
1797 }
1798 fclose( $fp );
1799 }
1800
1801 /**
1802 * Method init_coockiesdir()
1803 *
1804 * Check for cookies directory and create it if it doesn't already exist,
1805 * set the file permissions and update htaccess.
1806 *
1807 * @param mixed $cookieDir Cookies directory.
1808 *
1809 * @return void
1810 *
1811 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_wp_file_system()
1812 */
1813 public static function init_cookiesdir( $cookieDir ) {
1814
1815 $hasWPFileSystem = MainWP_System_Utility::get_wp_file_system();
1816
1817 /**
1818 * WordPress files system object.
1819 *
1820 * @global object
1821 */
1822 global $wp_filesystem;
1823
1824 if ( $hasWPFileSystem && ! empty( $wp_filesystem ) ) {
1825
1826 if ( ! $wp_filesystem->is_dir( $cookieDir ) ) {
1827 $wp_filesystem->mkdir( $cookieDir, 0777 );
1828 }
1829
1830 if ( ! file_exists( $cookieDir . '/.htaccess' ) ) {
1831 $file_htaccess = $cookieDir . '/.htaccess';
1832 $wp_filesystem->put_contents( $file_htaccess, 'deny from all' );
1833 }
1834
1835 if ( ! file_exists( $cookieDir . '/index.php' ) ) {
1836 $file_index = $cookieDir . '/index.php';
1837 $wp_filesystem->touch( $file_index );
1838 }
1839 } else {
1840
1841 if ( ! file_exists( $cookieDir ) ) {
1842 @mkdir( $cookieDir, 0777, true );
1843 }
1844
1845 if ( ! file_exists( $cookieDir . '/.htaccess' ) ) {
1846 $file_htaccess = @fopen( $cookieDir . '/.htaccess', 'w+' );
1847 @fwrite( $file_htaccess, 'deny from all' );
1848 @fclose( $file_htaccess );
1849 }
1850
1851 if ( ! file_exists( $cookieDir . '/index.php' ) ) {
1852 $file_index = @fopen( $cookieDir . '/index.php', 'w+' );
1853 @fclose( $file_index );
1854 }
1855 }
1856 }
1857
1858 /**
1859 * Method get_file_content()
1860 *
1861 * Get contents of file.
1862 *
1863 * @param mixed $url File Location.
1864 *
1865 * @return mixed false|$data
1866 *
1867 * @uses \MainWP\Dashboard\MainWP_System::$version
1868 */
1869 public static function get_file_content( $url ) {
1870 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
1871 $ch = curl_init();
1872
1873 $proxy = new \WP_HTTP_Proxy();
1874 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
1875 curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
1876 curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
1877 curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
1878
1879 if ( $proxy->use_authentication() ) {
1880 curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
1881 curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
1882 }
1883 }
1884
1885 curl_setopt( $ch, CURLOPT_HEADER, 0 );
1886 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
1887 curl_setopt( $ch, CURLOPT_URL, $url );
1888 curl_setopt( $ch, CURLOPT_USERAGENT, $agent );
1889 curl_setopt( $ch, CURLOPT_ENCODING, 'none' );
1890
1891 $data = @curl_exec( $ch );
1892 $httpCode = @curl_getinfo( $ch, CURLINFO_HTTP_CODE );
1893 if ( 'resource' === gettype( $ch ) ) {
1894 curl_close( $ch );
1895 }
1896 if ( 200 === (int) $httpCode ) {
1897 return $data;
1898 } else {
1899 return false;
1900 }
1901 }
1902
1903 /**
1904 * Method get_favico_url()
1905 *
1906 * Get Child Site favicon URL.
1907 *
1908 * @param mixed $website Child Site info.
1909 *
1910 * @return mixed $faviurl Favicon URL.
1911 *
1912 * @uses \MainWP\Dashboard\MainWP_DB::get_website_option()
1913 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_icons_dir()
1914 * @uses \MainWP\Dashboard\MainWP_Utility::remove_http_prefix()
1915 */
1916 public static function get_favico_url( $website ) {
1917 $favi = MainWP_DB::instance()->get_website_option( $website, 'favi_icon', '' );
1918 $faviurl = '';
1919
1920 if ( ! empty( $favi ) ) {
1921 if ( false !== strpos( $favi, 'favi-' . intval( $website->id ) . '-' ) ) {
1922 $dirs = MainWP_System_Utility::get_icons_dir();
1923 if ( file_exists( $dirs[0] . $favi ) ) {
1924 $faviurl = $dirs[1] . $favi;
1925 } else {
1926 $faviurl = '';
1927 }
1928 } elseif ( ( 0 === strpos( $favi, '//' ) ) || ( 0 === strpos( $favi, 'http' ) ) ) {
1929 $faviurl = $favi;
1930 } else {
1931 $faviurl = $website->url . $favi;
1932 $faviurl = MainWP_Utility::remove_http_prefix( $faviurl );
1933 }
1934 }
1935
1936 if ( empty( $faviurl ) ) {
1937 $faviurl = false;
1938 }
1939
1940 return $faviurl;
1941 }
1942 }
1943