| @@ -8,8 +8,13 @@ | ||
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | 10 | namespace MainWP\Dashboard; |
| 11 | 11 | |
| 12 | +// Exit if accessed directly. | |
| 13 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 14 | + exit; | |
| 15 | +} | |
| 16 | + | |
| 12 | 17 | /** |
| 13 | 18 | * Class MainWP_Connect |
| 14 | 19 | * |
| 15 | 20 | * @package MainWP\Dashboard |
| @@ -71,10 +76,13 @@ | ||
| 71 | 76 | curl_setopt( $ch, CURLOPT_URL, $url ); |
| 72 | 77 | if ( $no_body ) { |
| 73 | 78 | curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'HEAD' ); // HTTP request is 'HEAD', but sometime return 4xx - error code. |
| 74 | 79 | } |
| 80 | + | |
| 81 | + $follow_loc = apply_filters( 'mainwp_try_visit_follow_location', false ); // to support for case compatible. | |
| 82 | + | |
| 75 | 83 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); |
| 76 | - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); | |
| 84 | + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, $follow_loc ? true : false ); | |
| 77 | 85 | curl_setopt( $ch, CURLOPT_POST, true ); |
| 78 | 86 | curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata ); |
| 79 | 87 | curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 ); |
| 80 | 88 | curl_setopt( $ch, CURLOPT_USERAGENT, $agent ); |
| @@ -107,8 +115,9 @@ | ||
| 107 | 115 | } |
| 108 | 116 | |
| 109 | 117 | $headers = array( 'X-Requested-With' => 'XMLHttpRequest' ); |
| 110 | 118 | $headers['Expect'] = static::get_expect_header( $postdata ); |
| 119 | + $headers = apply_filters( 'mainwp_connect_http_request_headers', $headers, false ); | |
| 111 | 120 | |
| 112 | 121 | if ( class_exists( '\WpOrg\Requests\Requests' ) ) { |
| 113 | 122 | $headers = \WpOrg\Requests\Requests::flatten( $headers ); |
| 114 | 123 | } else { |
| @@ -114,9 +123,9 @@ | ||
| 114 | 123 | } else { |
| 115 | 124 | $headers = \Requests::flatten( $headers ); |
| 116 | 125 | } |
| 117 | 126 | |
| 118 | - curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'X-Requested-With: XMLHttpRequest' ) ); | |
| 127 | + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); | |
| 119 | 128 | curl_setopt( $ch, CURLOPT_REFERER, get_option( 'siteurl' ) ); |
| 120 | 129 | |
| 121 | 130 | $force_use_ipv4 = false; |
| 122 | 131 | if ( null !== $forceUseIPv4 ) { |
| @@ -136,8 +145,10 @@ | ||
| 136 | 145 | } |
| 137 | 146 | |
| 138 | 147 | MainWP_Logger::instance()->debug( ' :: trying Visit :: [url=' . $url . ']' ); |
| 139 | 148 | |
| 149 | + $http_version = false; | |
| 150 | + | |
| 140 | 151 | $disabled_functions = ini_get( 'disable_functions' ); |
| 141 | 152 | if ( empty( $disabled_functions ) || ( stristr( $disabled_functions, 'curl_multi_exec' ) === false ) ) { |
| 142 | 153 | MainWP_Logger::instance()->debug( ' :: trying Visit :: curl_multi_exec => enabled.' ); |
| 143 | 154 | $mh = curl_multi_init(); |
| @@ -143,22 +154,36 @@ | ||
| 143 | 154 | $mh = curl_multi_init(); |
| 144 | 155 | @curl_multi_add_handle( $mh, $ch ); |
| 145 | 156 | |
| 146 | 157 | do { |
| 147 | - curl_multi_exec( $mh, $running ); | |
| 148 | - curl_multi_select( $mh ); | |
| 158 | + do { | |
| 159 | + $mrc = curl_multi_exec( $mh, $running ); | |
| 160 | + } while ( CURLM_CALL_MULTI_PERFORM === $mrc ); | |
| 161 | + | |
| 162 | + if ( $running ) { | |
| 163 | + $rc = curl_multi_select( $mh, 1.0 ); | |
| 164 | + if ( -1 === $rc ) { | |
| 165 | + usleep( 100000 ); | |
| 166 | + } | |
| 167 | + } | |
| 168 | + | |
| 149 | 169 | while ( $info = curl_multi_info_read( $mh ) ) { |
| 150 | 170 | $data = curl_multi_getcontent( $info['handle'] ); |
| 151 | 171 | $err = curl_error( $info['handle'] ); |
| 152 | 172 | $http_status = curl_getinfo( $info['handle'], CURLINFO_HTTP_CODE ); |
| 153 | 173 | $realurl = curl_getinfo( $info['handle'], CURLINFO_EFFECTIVE_URL ); |
| 174 | + if ( defined( 'CURLINFO_HTTP_VERSION' ) ) { | |
| 175 | + $http_version = curl_getinfo( $info['handle'], CURLINFO_HTTP_VERSION ); | |
| 176 | + } | |
| 177 | + | |
| 154 | 178 | curl_multi_remove_handle( $mh, $info['handle'] ); |
| 179 | + curl_close( $info['handle'] ); | |
| 155 | 180 | } |
| 156 | 181 | usleep( 10000 ); |
| 157 | 182 | |
| 158 | 183 | } while ( $running > 0 ); |
| 159 | 184 | |
| 160 | - if ( 'resource' === gettype( $mh ) ) { | |
| 185 | + if ( static::is_valid_curl_handle( $mh ) ) { | |
| 161 | 186 | curl_multi_close( $mh ); |
| 162 | 187 | } |
| 163 | 188 | } else { |
| 164 | 189 | $data = curl_exec( $ch ); |
| @@ -164,14 +189,18 @@ | ||
| 164 | 189 | $data = curl_exec( $ch ); |
| 165 | 190 | $err = curl_error( $ch ); |
| 166 | 191 | $http_status = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); |
| 167 | 192 | $realurl = curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL ); |
| 168 | - if ( 'resource' === gettype( $ch ) ) { | |
| 193 | + | |
| 194 | + if ( defined( 'CURLINFO_HTTP_VERSION' ) ) { | |
| 195 | + $http_version = curl_getinfo( $ch, CURLINFO_HTTP_VERSION ); | |
| 196 | + } | |
| 197 | + | |
| 198 | + if ( static::is_valid_curl_handle( $ch ) ) { | |
| 169 | 199 | curl_close( $ch ); |
| 170 | 200 | } |
| 171 | 201 | } |
| 172 | 202 | |
| 173 | - MainWP_Logger::instance()->debug( ' :: tryVisit :: [url=' . $url . '] [http_status=' . $http_status . '] [error=' . $err . '] [data-start]' . $data . '[data-end]' ); | |
| 174 | 203 | MainWP_Logger::instance()->log_execution_time( 'tryVisit :: [url=' . $url . '] [http_status=' . $http_status . ']' ); |
| 175 | 204 | |
| 176 | 205 | $host = wp_parse_url( ( empty( $realurl ) ? $url : $realurl ), PHP_URL_HOST ); |
| 177 | 206 | $ip = false; |
| @@ -176,10 +205,15 @@ | ||
| 176 | 205 | $host = wp_parse_url( ( empty( $realurl ) ? $url : $realurl ), PHP_URL_HOST ); |
| 177 | 206 | $ip = false; |
| 178 | 207 | $target = false; |
| 179 | 208 | |
| 209 | + // Ask only for the record types read below. dns_get_record() defaults to DNS_ANY, which | |
| 210 | + // most resolvers now refuse or answer with a stub (RFC 8482), so it buys retries and | |
| 211 | + // timeouts instead of answers. Names that exist only in the hosts file are not resolved | |
| 212 | + // here at all -- dns_get_record() never reads the hosts file -- they fall through to the | |
| 213 | + // gethostbynamel() call below. | |
| 180 | 214 | $found = false; |
| 181 | - $dnsRecord = @dns_get_record( $host ); | |
| 215 | + $dnsRecord = @dns_get_record( $host, DNS_A | DNS_AAAA | DNS_CNAME ); | |
| 182 | 216 | MainWP_Logger::instance()->debug( ' :: tryVisit :: [dnsRecord=' . MainWP_Utility::value_to_string( $dnsRecord, 1 ) . ']' ); |
| 183 | 217 | |
| 184 | 218 | if ( false !== $dnsRecord && is_array( $dnsRecord ) ) { |
| 185 | 219 | if ( ! isset( $dnsRecord['ip'] ) ) { |
| @@ -221,10 +255,19 @@ | ||
| 221 | 255 | $out = array( |
| 222 | 256 | 'host' => $host, |
| 223 | 257 | 'httpCode' => $http_status, |
| 224 | 258 | 'httpCodeString' => MainWP_Utility::get_http_codes( $http_status ), |
| 259 | + 'httpVersion' => $http_version, | |
| 225 | 260 | ); |
| 226 | 261 | |
| 262 | + $hidden_data = '[hidden response data]'; | |
| 263 | + | |
| 264 | + if ( ( false === $ip || $ip === $host || ! static::validate_ip( $ip ) ) && apply_filters( 'mainwp_hide_raw_connection_response_data', true ) ) { // Failed to resolve hostname. | |
| 265 | + $data = $hidden_data; | |
| 266 | + } | |
| 267 | + | |
| 268 | + MainWP_Logger::instance()->debug( ' :: tryVisit :: [url=' . $url . '] [http_status=' . $http_status . '] [http_version=' . ( false === $http_version ? 'N/A' : MainWP_System_Utility::get_http_version_const_str( $http_version ) ) . '] [error=' . $err . '] [data-start]' . $data . '[data-end]' ); | |
| 269 | + | |
| 227 | 270 | if ( false !== $ip ) { |
| 228 | 271 | $out['ip'] = $ip; |
| 229 | 272 | $found = true; |
| 230 | 273 | } |
| @@ -233,9 +276,24 @@ | ||
| 233 | 276 | |
| 234 | 277 | return $out; |
| 235 | 278 | } |
| 236 | 279 | |
| 280 | + | |
| 237 | 281 | /** |
| 282 | + * Method validate_ip(). | |
| 283 | + * | |
| 284 | + * @param string $ip IP check. | |
| 285 | + * @return bool Check IP result. | |
| 286 | + */ | |
| 287 | + public static function validate_ip( $ip ) { | |
| 288 | + // Validate the IP and check for private and reserved ranges. | |
| 289 | + if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) { | |
| 290 | + return true; | |
| 291 | + } | |
| 292 | + return false; | |
| 293 | + } | |
| 294 | + | |
| 295 | + /** | |
| 238 | 296 | * Method check_ignored_http_code() |
| 239 | 297 | * |
| 240 | 298 | * Check if http error code is being ignored. |
| 241 | 299 | * |
| @@ -243,28 +301,32 @@ | ||
| 243 | 301 | * @param object|false $website website. |
| 244 | 302 | * |
| 245 | 303 | * @return bolean True|False. |
| 246 | 304 | */ |
| 247 | - public static function check_ignored_http_code( $value, $website = false ) { | |
| 248 | - $value = (int) $value; | |
| 249 | - if ( 200 === $value ) { | |
| 250 | - return true; | |
| 251 | - } | |
| 305 | + public static function check_ignored_http_code( $value, $website = false ) { // phpcs:ignore -- NOSONAR -complex method. | |
| 306 | + $value = (int) $value; | |
| 307 | + $site_id = is_object( $website ) && ! empty( $website->id ) ? $website->id : 0; | |
| 308 | + $global_settings = MainWP_Uptime_Monitoring_Handle::get_global_monitoring_settings(); | |
| 252 | 309 | |
| 253 | - if ( ! is_object( $website ) || empty( $website->id ) ) { | |
| 254 | - return false; | |
| 255 | - } | |
| 310 | + $ignored_code = ''; | |
| 256 | 311 | |
| 257 | - $ignored_code = ''; | |
| 258 | - if ( ! property_exists( $website, 'monitor_id' ) ) { | |
| 312 | + if ( $site_id ) { | |
| 313 | + | |
| 259 | 314 | $primary_monitor = MainWP_DB_Uptime_Monitoring::instance()->get_monitor_by( $site_id, 'issub', 0 ); |
| 315 | + $global_settings = MainWP_Uptime_Monitoring_Handle::get_global_monitoring_settings(); | |
| 260 | 316 | |
| 317 | + $mo_active = 0; | |
| 261 | 318 | 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 ); | |
| 319 | + $mo_active = MainWP_Uptime_Monitoring_Connect::get_apply_setting( 'active', (int) $primary_monitor->active, $global_settings, -1, 0 ); | |
| 320 | + } | |
| 321 | + | |
| 322 | + if ( $mo_active ) { | |
| 323 | + $ignored_code = MainWP_Uptime_Monitoring_Connect::instance()->get_up_codes( $primary_monitor, $global_settings ); | |
| 264 | 324 | } else { |
| 265 | - return false; | |
| 325 | + $ignored_code = is_array( $global_settings ) && isset( $global_settings['up_status_codes'] ) ? $global_settings['up_status_codes'] : ''; | |
| 266 | 326 | } |
| 327 | + } else { | |
| 328 | + $ignored_code = ! empty( $global_settings['up_status_codes'] ) ? $global_settings['up_status_codes'] : ''; | |
| 267 | 329 | } |
| 268 | 330 | |
| 269 | 331 | if ( ! empty( $ignored_code ) ) { |
| 270 | 332 | $ignored_code = explode( ',', $ignored_code ); |
| @@ -283,20 +345,27 @@ | ||
| 283 | 345 | * |
| 284 | 346 | * Check if the Website returns and http errors. |
| 285 | 347 | * |
| 286 | 348 | * @param object $website Child Site information. |
| 349 | + * @param bool $chk_http_site Check site http response. | |
| 287 | 350 | * |
| 288 | 351 | * @return mixed False|try visit result. |
| 289 | 352 | * |
| 290 | 353 | * @uses \MainWP\Dashboard\MainWP_Utility::is_domain_valid() |
| 291 | 354 | */ |
| 292 | - public static function check_website_status( $website ) { //phpcs:ignore -- NOSONAR - complexity. | |
| 355 | + public static function check_website_status( $website, $chk_http_site = false ) { //phpcs:ignore -- NOSONAR - complexity. | |
| 293 | 356 | |
| 294 | 357 | if ( is_object( $website ) && isset( $website->id ) ) { |
| 295 | 358 | $primary_monitor = MainWP_DB_Uptime_Monitoring::instance()->get_monitor_by( $website->id, 'issub', 0 ); |
| 296 | 359 | if ( $primary_monitor ) { |
| 297 | 360 | // return compatible uptime status here. |
| 298 | - return MainWP_Uptime_Monitoring_Handle::check_website_uptime_monitoring_status( $primary_monitor, array( 'ignore_compatible_save' => 1 ) ); | |
| 361 | + return MainWP_Uptime_Monitoring_Handle::check_website_uptime_monitoring_status( | |
| 362 | + $primary_monitor, | |
| 363 | + array( | |
| 364 | + 'ignore_compatible_save' => 1, | |
| 365 | + 'check_http_site' => $chk_http_site, | |
| 366 | + ) | |
| 367 | + ); // to ignore save compatible uptime status. | |
| 299 | 368 | } |
| 300 | 369 | } |
| 301 | 370 | |
| 302 | 371 | $http_user = null; |
| @@ -307,11 +376,14 @@ | ||
| 307 | 376 | if ( is_object( $website ) && isset( $website->url ) ) { |
| 308 | 377 | $url = $website->url; |
| 309 | 378 | $verifyCertificate = isset( $website->verify_certificate ) ? (int) $website->verify_certificate : null; |
| 310 | 379 | $forceUseIPv4 = $website->force_use_ipv4; |
| 311 | - $http_user = $website->http_user; | |
| 312 | - $http_pass = $website->http_pass; | |
| 313 | - $sslVersion = $website->ssl_version; | |
| 380 | + // MWP-1548: decrypt at the boundary so HTTP Basic Auth gets | |
| 381 | + // the plaintext credentials. Legacy plaintext rows pass | |
| 382 | + // through unchanged via the helper's fallback. | |
| 383 | + $http_user = MainWP_Credential_Storage::decrypt_credential( $website->http_user ); | |
| 384 | + $http_pass = MainWP_Credential_Storage::decrypt_credential( $website->http_pass ); | |
| 385 | + $sslVersion = $website->ssl_version; | |
| 314 | 386 | } else { |
| 315 | 387 | $url = $website; |
| 316 | 388 | } |
| 317 | 389 | |
| @@ -340,12 +412,22 @@ | ||
| 340 | 412 | * |
| 341 | 413 | * @param mixed $website Array of Child Site Info. |
| 342 | 414 | * @param mixed $what What we are posting. |
| 343 | 415 | * @param null $params Post parameters. |
| 416 | + * @param array $others Other data. | |
| 344 | 417 | * |
| 345 | 418 | * @return mixed null|http_build_query() |
| 346 | 419 | */ |
| 347 | - public static function get_post_data_authed( &$website, $what, $params = null ) { //phpcs:ignore -- NOSONAR - complex method. | |
| 420 | + public static function get_post_data_authed( &$website, $what, $params = null, $others = array() ) { //phpcs:ignore -- NOSONAR - complex method. | |
| 421 | + | |
| 422 | + if ( ! is_array( $others ) ) { | |
| 423 | + $others = array(); | |
| 424 | + } | |
| 425 | + | |
| 426 | + $verify_signature = ! empty( $others['verify_signature'] ) ? true : false; | |
| 427 | + $http_user_plain = ! empty( $others['http_user_plain'] ) ? $others['http_user_plain'] : ''; | |
| 428 | + $http_pass_plain = ! empty( $others['http_pass_plain'] ) ? $others['http_pass_plain'] : ''; | |
| 429 | + | |
| 348 | 430 | if ( $website && '' !== $what ) { |
| 349 | 431 | $data = array(); |
| 350 | 432 | $data['user'] = $website->adminname; |
| 351 | 433 | $data['function'] = $what; |
| @@ -351,9 +433,9 @@ | ||
| 351 | 433 | $data['function'] = $what; |
| 352 | 434 | $data['nonce'] = wp_rand( 0, 9999 ); |
| 353 | 435 | $data['mainwpver'] = MainWP_System::$version; |
| 354 | 436 | |
| 355 | - $params_filter = apply_filters( 'mainwp_pre_fetch_authed_data', false, $params, $what, $website ); | |
| 437 | + $params_filter = apply_filters( 'mainwp_pre_fetch_authed_data', false, $params, $what, $website, $verify_signature ); | |
| 356 | 438 | if ( is_array( $params_filter ) && ! empty( $params_filter ) ) { |
| 357 | 439 | $data = array_merge( $data, $params_filter ); |
| 358 | 440 | } |
| 359 | 441 | |
| @@ -364,47 +446,12 @@ | ||
| 364 | 446 | $alg = false; |
| 365 | 447 | $sign_success = null; |
| 366 | 448 | $use_seclib = false; |
| 367 | 449 | |
| 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 | - } | |
| 450 | + $data = apply_filters( 'mainwp_get_post_data_authed', $data, $website, $what, $params, $verify_signature ); | |
| 379 | 451 | |
| 380 | - if ( $use_seclib ) { | |
| 381 | - $data['verifylib'] = 1; | |
| 382 | - } | |
| 452 | + $alt_user = ''; | |
| 383 | 453 | |
| 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 | 454 | /** |
| 408 | 455 | * Current user global. |
| 409 | 456 | * |
| 410 | 457 | * @global string |
| @@ -421,14 +468,125 @@ | ||
| 421 | 468 | * @param int $current_user->ID User ID. |
| 422 | 469 | * |
| 423 | 470 | * @since Unknown |
| 424 | 471 | */ |
| 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 ); | |
| 472 | + $alt_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID ); | |
| 473 | + | |
| 474 | + } | |
| 475 | + | |
| 476 | + $child_support_adv_sign = 1 === (int) MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' ); | |
| 477 | + | |
| 478 | + $alg = false; | |
| 479 | + $sign_value = $what . $data['nonce']; // Legacy signature data. | |
| 480 | + | |
| 481 | + if ( ! $verify_signature || ! $child_support_adv_sign ) { | |
| 482 | + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { | |
| 483 | + $sign_success = MainWP_Connect_Lib::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 484 | + $use_seclib = true; | |
| 485 | + } elseif ( function_exists( 'openssl_verify' ) ) { | |
| 486 | + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website ); | |
| 487 | + $sign_success = static::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 488 | + if ( false !== $alg ) { | |
| 489 | + $data['sign_algo'] = $alg; | |
| 490 | + } | |
| 428 | 491 | } |
| 492 | + | |
| 493 | + if ( null !== $sign_success && empty( $sign_success ) ) { | |
| 494 | + $sign_error = ''; | |
| 495 | + while ( $msg = openssl_error_string() ) { | |
| 496 | + if ( is_string( $msg ) ) { | |
| 497 | + $sign_error .= $msg; | |
| 498 | + } | |
| 499 | + } | |
| 500 | + $pk_info = ! empty( $website->privkey ) ? substr( $website->privkey, 0, 10 ) : ''; | |
| 501 | + 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 . '] :: [pkey start =' . $pk_info . '...]', false ); | |
| 502 | + } | |
| 503 | + } else { | |
| 504 | + $signature = 'useadvancedmainwpsignature'; | |
| 429 | 505 | } |
| 430 | 506 | |
| 507 | + $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 508 | + | |
| 509 | + if ( $verify_signature ) { | |
| 510 | + $ts = time(); | |
| 511 | + $data_sign_v2 = array( | |
| 512 | + 'base_function' => $what, | |
| 513 | + 'nonce' => $data['nonce'], | |
| 514 | + 'expires' => $ts + 60, | |
| 515 | + 'user' => $website->adminname, | |
| 516 | + 'req_id' => wp_generate_uuid4(), | |
| 517 | + ); | |
| 518 | + | |
| 519 | + if ( ! empty( $alt_user ) ) { | |
| 520 | + $data_sign_v2['alt_user'] = rawurlencode( $alt_user ); | |
| 521 | + } | |
| 522 | + | |
| 523 | + if ( 'process_premium_updates' === $what ) { | |
| 524 | + $add_sign_params = array( 'premium_perform', 'premium_type', 'list' ); | |
| 525 | + foreach ( $add_sign_params as $_name ) { | |
| 526 | + if ( isset( $params[ $_name ] ) ) { | |
| 527 | + $data_sign_v2[ $_name ] = $params[ $_name ]; | |
| 528 | + } | |
| 529 | + } | |
| 530 | + } | |
| 531 | + | |
| 532 | + $sign_success_v2 = null; | |
| 533 | + | |
| 534 | + $sign_value_v2 = wp_json_encode( $data_sign_v2 ); | |
| 535 | + | |
| 536 | + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { | |
| 537 | + $use_seclib = true; | |
| 538 | + $sign_success_v2 = MainWP_Connect_Lib::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 539 | + } elseif ( function_exists( 'openssl_verify' ) ) { | |
| 540 | + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website ); | |
| 541 | + $sign_success_v2 = static::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 542 | + if ( false !== $alg ) { | |
| 543 | + $data['sign_algo'] = $alg; | |
| 544 | + } | |
| 545 | + } | |
| 546 | + | |
| 547 | + $data['data_signature'] = $sign_value_v2; | |
| 548 | + $data['mainwpsignature_adv'] = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 549 | + | |
| 550 | + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) { | |
| 551 | + $sign_error = ''; | |
| 552 | + while ( $msg = openssl_error_string() ) { | |
| 553 | + if ( is_string( $msg ) ) { | |
| 554 | + $sign_error .= $msg; | |
| 555 | + } | |
| 556 | + } | |
| 557 | + $pk_info = ! empty( $website->privkey ) ? substr( $website->privkey, 0, 10 ) : ''; | |
| 558 | + MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT ADV SIGN', 'FAILED :: [what=' . ( is_string( $what ) ? $what : '' ) . '] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . '] :: [pkey start =' . $pk_info . '...]', false ); | |
| 559 | + } | |
| 560 | + } | |
| 561 | + | |
| 562 | + if ( $use_seclib ) { | |
| 563 | + $data['verifylib'] = 1; | |
| 564 | + } | |
| 565 | + | |
| 566 | + if ( ! empty( $alt_user ) ) { | |
| 567 | + $data['alt_user'] = rawurlencode( $alt_user ); | |
| 568 | + } | |
| 569 | + | |
| 570 | + /** This filter is documented in ../widgets/widget-mainwp-recent-posts.php */ | |
| 571 | + $recent_number = apply_filters( 'mainwp_recent_posts_pages_number', 5 ); | |
| 572 | + if ( 5 !== $recent_number ) { | |
| 573 | + $data['recent_number'] = $recent_number; | |
| 574 | + } | |
| 575 | + | |
| 576 | + $scan_dir = apply_filters( 'mainwp_stats_scan_dir', false, $website ); | |
| 577 | + if ( ! empty( $scan_dir ) ) { | |
| 578 | + $data['scan_dir'] = 1; | |
| 579 | + } | |
| 580 | + | |
| 581 | + if ( 'process_premium_updates' === $what ) { | |
| 582 | + if ( ! empty( $http_user_plain ) && ! empty( $http_pass_plain ) ) { | |
| 583 | + // For post data. | |
| 584 | + $data['wp_http_user'] = $http_user_plain; | |
| 585 | + $data['wp_http_pass'] = $http_pass_plain; | |
| 586 | + } | |
| 587 | + $data['sslVerify'] = $website->verify_certificate ? 1 : 0; | |
| 588 | + } | |
| 431 | 589 | return http_build_query( $data, '', '&' ); |
| 432 | 590 | } |
| 433 | 591 | |
| 434 | 592 | return null; |
| @@ -440,13 +598,20 @@ | ||
| 440 | 598 | * Get authorized $_POST data & build query for renew connection action only. |
| 441 | 599 | * |
| 442 | 600 | * @param mixed $website Array of Child Site Info. |
| 443 | 601 | * @param mixed $what What we are posting. |
| 602 | + * @param array $others Other data. | |
| 444 | 603 | * |
| 445 | 604 | * @return mixed null|http_build_query() |
| 446 | 605 | */ |
| 447 | - private static function get_renew_post_data_authed( &$website, $what ) { // phpcs:ignore -- NOSONAR - complex. | |
| 606 | + private static function get_renew_post_data_authed( &$website, $what, $others = array() ) { // phpcs:ignore -- NOSONAR - complex. | |
| 448 | 607 | |
| 608 | + if ( ! is_array( $others ) ) { | |
| 609 | + $others = array(); | |
| 610 | + } | |
| 611 | + | |
| 612 | + $verify_signature = ! empty( $others['verify_signature'] ) ? true : false; | |
| 613 | + | |
| 449 | 614 | if ( $website && '' !== $what ) { |
| 450 | 615 | $compat_what = 'disconnect'; // to compatible, renew will call disconnect. |
| 451 | 616 | $data = array(); |
| 452 | 617 | $data['user'] = $website->adminname; |
| @@ -452,8 +617,10 @@ | ||
| 452 | 617 | $data['user'] = $website->adminname; |
| 453 | 618 | $data['function'] = $compat_what; |
| 454 | 619 | $data['nonce'] = wp_rand( 0, 9999 ); |
| 455 | 620 | |
| 621 | + $sign_value = $compat_what . $data['nonce']; // compatible format. | |
| 622 | + | |
| 456 | 623 | $alg = false; |
| 457 | 624 | $sign_success = null; |
| 458 | 625 | $use_seclib = false; |
| 459 | 626 | |
| @@ -458,17 +625,17 @@ | ||
| 458 | 625 | $use_seclib = false; |
| 459 | 626 | |
| 460 | 627 | if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { |
| 461 | 628 | // 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. | |
| 629 | + $sign_success = MainWP_Connect_Lib::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 463 | 630 | $use_seclib = true; |
| 464 | 631 | } elseif ( function_exists( 'openssl_verify' ) ) { |
| 465 | 632 | $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. | |
| 633 | + $sign_success = static::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding. | |
| 467 | 634 | if ( empty( $sign_success ) ) { // error from openssl, openssl_sign(). |
| 468 | 635 | $alg = defined( 'OPENSSL_ALGO_SHA1' ) ? OPENSSL_ALGO_SHA1 : false; // to set default SHA1, to disconnect. |
| 469 | 636 | 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. | |
| 637 | + $sign_success = static::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding. | |
| 471 | 638 | } |
| 472 | 639 | |
| 473 | 640 | if ( false !== $alg ) { |
| 474 | 641 | $data['sign_algo'] = $alg; |
| @@ -474,11 +641,9 @@ | ||
| 474 | 641 | $data['sign_algo'] = $alg; |
| 475 | 642 | } |
| 476 | 643 | } |
| 477 | 644 | |
| 478 | - if ( $use_seclib ) { | |
| 479 | - $data['verifylib'] = 1; | |
| 480 | - } | |
| 645 | + $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 481 | 646 | |
| 482 | 647 | if ( null !== $sign_success && empty( $sign_success ) ) { |
| 483 | 648 | $sign_error = ''; |
| 484 | 649 | while ( $msg = openssl_error_string() ) { |
| @@ -488,10 +653,55 @@ | ||
| 488 | 653 | } |
| 489 | 654 | 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 | 655 | } |
| 491 | 656 | |
| 492 | - $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 657 | + if ( $verify_signature ) { | |
| 658 | + $ts = time(); | |
| 659 | + $data_sign_v2 = array( | |
| 660 | + 'base_function' => $compat_what, | |
| 661 | + 'nonce' => $data['nonce'], | |
| 662 | + 'expires' => $ts + 60, | |
| 663 | + 'user' => $website->adminname, | |
| 664 | + 'req_id' => wp_generate_uuid4(), | |
| 665 | + ); | |
| 666 | + $sign_value_v2 = wp_json_encode( $data_sign_v2 ); | |
| 493 | 667 | |
| 668 | + $sign_success_v2 = null; | |
| 669 | + | |
| 670 | + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { | |
| 671 | + // to disconnect. | |
| 672 | + $sign_success_v2 = MainWP_Connect_Lib::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 673 | + } elseif ( function_exists( 'openssl_verify' ) ) { | |
| 674 | + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website ); | |
| 675 | + $sign_success_v2 = static::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding. | |
| 676 | + if ( empty( $sign_success_v2 ) ) { // error from openssl, openssl_sign(). | |
| 677 | + $alg = defined( 'OPENSSL_ALGO_SHA1' ) ? OPENSSL_ALGO_SHA1 : false; // to set default SHA1, to disconnect. | |
| 678 | + MainWP_Logger::instance()->debug_for_website( $website, 'get_renew_post_data_authed', '[' . $website->url . '] :: [openssl_sign:failed] :: Set sign_algo=SHA1' ); | |
| 679 | + $sign_success_v2 = static::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for keys encoding. | |
| 680 | + } | |
| 681 | + if ( false !== $alg ) { | |
| 682 | + $data['sign_algo'] = $alg; | |
| 683 | + } | |
| 684 | + } | |
| 685 | + | |
| 686 | + $data['data_signature'] = $sign_value_v2; | |
| 687 | + $data['mainwpsignature_adv'] = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 688 | + | |
| 689 | + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) { | |
| 690 | + $sign_error = ''; | |
| 691 | + while ( $msg = openssl_error_string() ) { | |
| 692 | + if ( is_string( $msg ) ) { | |
| 693 | + $sign_error .= $msg; | |
| 694 | + } | |
| 695 | + } | |
| 696 | + MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT ADV SIGN', 'FAILED :: [what=' . ( is_string( $what ) ? $what : '' ) . '] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . ']', false ); | |
| 697 | + } | |
| 698 | + } | |
| 699 | + | |
| 700 | + if ( $use_seclib ) { | |
| 701 | + $data['verifylib'] = 1; | |
| 702 | + } | |
| 703 | + | |
| 494 | 704 | return http_build_query( $data, '', '&' ); |
| 495 | 705 | } |
| 496 | 706 | return null; |
| 497 | 707 | } |
| @@ -506,12 +716,16 @@ | ||
| 506 | 716 | * @param mixed $paramValue OpenSSL parameter. |
| 507 | 717 | * @param string $paramName Parameter name. |
| 508 | 718 | * @param bool $asArray true|false Default is false. |
| 509 | 719 | * @param array $other_params other params. |
| 720 | + * @param string $custom_url Optional. Override the target base URL for browser-bound | |
| 721 | + * requests ( see MainWP_Site_Url_Corrector::browser_target_url() ). | |
| 722 | + * Default null keeps the stored URL � server-side callers | |
| 723 | + * ( backups, premium updates ) must not pass this. | |
| 510 | 724 | * |
| 511 | 725 | * @return string $url |
| 512 | 726 | */ |
| 513 | - public static function get_get_data_authed( $website, $paramValue, $paramName = 'where', $asArray = false, $other_params = array() ) { //phpcs:ignore -- NOSONAR - complex method. | |
| 727 | + public static function get_get_data_authed( $website, $paramValue, $paramName = 'where', $asArray = false, $other_params = array(), $custom_url = null ) { //phpcs:ignore -- NOSONAR - complex method. | |
| 514 | 728 | $params = array(); |
| 515 | 729 | if ( $website && '' !== $paramValue ) { |
| 516 | 730 | |
| 517 | 731 | $sign_success = null; |
| @@ -517,26 +731,50 @@ | ||
| 517 | 731 | $sign_success = null; |
| 518 | 732 | $alg = false; |
| 519 | 733 | $use_seclib = false; |
| 520 | 734 | $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. | |
| 735 | + | |
| 736 | + /** | |
| 737 | + * Current user global. | |
| 738 | + * | |
| 739 | + * @global string | |
| 740 | + */ | |
| 741 | + global $current_user; | |
| 742 | + | |
| 743 | + $alt_user = ''; | |
| 744 | + if ( ( ( ! defined( 'DOING_CRON' ) || false === DOING_CRON ) && ( ! defined( 'WP_CLI' ) || false === WP_CLI ) ) && $current_user && $current_user->ID ) { | |
| 745 | + /** This filter is documented in ../class/class-mainwp-connect.php */ | |
| 746 | + $alt_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID ); | |
| 527 | 747 | } |
| 528 | 748 | |
| 529 | - $signature = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 749 | + $verify_signature = is_array( $other_params ) && ! empty( $other_params['verify_signature'] ); | |
| 530 | 750 | |
| 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; | |
| 751 | + $child_support_adv_sign = 1 === (int) MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' ); | |
| 752 | + | |
| 753 | + if ( ! $verify_signature || ! $child_support_adv_sign ) { | |
| 754 | + $sign_value = $paramValue . $nonce; // compatible format. | |
| 755 | + | |
| 756 | + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { | |
| 757 | + $sign_success = MainWP_Connect_Lib::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 758 | + $use_seclib = true; | |
| 759 | + } elseif ( function_exists( 'openssl_verify' ) ) { | |
| 760 | + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website ); | |
| 761 | + $sign_success = static::connect_sign( $sign_value, $signature, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 762 | + } | |
| 763 | + | |
| 764 | + $signature = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 765 | + | |
| 766 | + if ( null !== $sign_success && empty( $sign_success ) ) { | |
| 767 | + $sign_error = ''; | |
| 768 | + while ( $msg = openssl_error_string() ) { | |
| 769 | + if ( is_string( $msg ) ) { | |
| 770 | + $sign_error .= $msg; | |
| 771 | + } | |
| 536 | 772 | } |
| 773 | + 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 ); | |
| 537 | 774 | } |
| 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 ); | |
| 775 | + } else { | |
| 776 | + $signature = base64_encode( 'useadvancedmainwpsignature' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 539 | 777 | } |
| 540 | 778 | |
| 541 | 779 | $params = array( |
| 542 | 780 | 'login_required' => 1, |
| @@ -545,8 +783,62 @@ | ||
| 545 | 783 | 'nonce' => $nonce, |
| 546 | 784 | $paramName => rawurlencode( $paramValue ), |
| 547 | 785 | ); |
| 548 | 786 | |
| 787 | + if ( false !== $alg ) { | |
| 788 | + $params['sign_algo'] = $alg; | |
| 789 | + } | |
| 790 | + | |
| 791 | + if ( $verify_signature ) { | |
| 792 | + $ts = time(); | |
| 793 | + $data_sign_v2 = array( | |
| 794 | + 'base_function' => $paramValue, | |
| 795 | + 'where' => rawurlencode( $paramName ), | |
| 796 | + 'nonce' => $nonce, | |
| 797 | + 'expires' => $ts + 60, | |
| 798 | + 'user' => $website->adminname, | |
| 799 | + 'req_id' => wp_generate_uuid4(), | |
| 800 | + ); | |
| 801 | + | |
| 802 | + if ( ! empty( $alt_user ) ) { | |
| 803 | + $data_sign_v2['alt_user'] = rawurlencode( $alt_user ); | |
| 804 | + } | |
| 805 | + | |
| 806 | + $sign_success_v2 = null; | |
| 807 | + | |
| 808 | + $sign_value_v2 = wp_json_encode( $data_sign_v2 ); | |
| 809 | + | |
| 810 | + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) { | |
| 811 | + $sign_success_v2 = MainWP_Connect_Lib::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 812 | + $use_seclib = true; | |
| 813 | + } elseif ( function_exists( 'openssl_verify' ) ) { | |
| 814 | + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website ); | |
| 815 | + $sign_success_v2 = static::connect_sign( $sign_value_v2, $signature_v2, base64_decode( $website->privkey ), $alg, $website->id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 816 | + if ( false !== $alg ) { | |
| 817 | + $params['sign_algo'] = $alg; | |
| 818 | + } | |
| 819 | + } | |
| 820 | + | |
| 821 | + $signature_v2 = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 822 | + | |
| 823 | + $params['data_signature'] = rawurlencode( $sign_value_v2 ); | |
| 824 | + $params['mainwpsignature_adv'] = rawurlencode( $signature_v2 ); | |
| 825 | + | |
| 826 | + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) { | |
| 827 | + $sign_error = ''; | |
| 828 | + while ( $msg = openssl_error_string() ) { | |
| 829 | + if ( is_string( $msg ) ) { | |
| 830 | + $sign_error .= $msg; | |
| 831 | + } | |
| 832 | + } | |
| 833 | + MainWP_Logger::instance()->warning_for_website( $website, 'CONNECT ADV SIGN', 'FAILED :: [login_required=1] :: [seclib=' . intval( $use_seclib ) . '] :: [algorithm=' . $alg . '] :: [openssl_sign error =' . $sign_error . ']', false ); | |
| 834 | + } | |
| 835 | + } | |
| 836 | + | |
| 837 | + if ( ! empty( $alt_user ) ) { | |
| 838 | + $params['alt_user'] = rawurlencode( $alt_user ); | |
| 839 | + } | |
| 840 | + | |
| 549 | 841 | if ( is_array( $other_params ) ) { |
| 550 | 842 | foreach ( $other_params as $name => $value ) { |
| 551 | 843 | if ( is_string( $name ) && ! empty( $name ) && is_scalar( $value ) ) { |
| 552 | 844 | $params[ sanitize_text_field( wp_unslash( $name ) ) ] = rawurlencode( sanitize_text_field( wp_unslash( $value ) ) ); |
| @@ -566,30 +858,11 @@ | ||
| 566 | 858 | } |
| 567 | 859 | } |
| 568 | 860 | } |
| 569 | 861 | |
| 570 | - if ( false !== $alg ) { | |
| 571 | - $params['sign_algo'] = $alg; | |
| 572 | - } | |
| 573 | - | |
| 574 | 862 | if ( ! empty( $use_seclib ) ) { |
| 575 | 863 | $params['verifylib'] = 1; |
| 576 | 864 | } |
| 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 | 865 | } |
| 593 | 866 | |
| 594 | 867 | if ( $asArray ) { |
| 595 | 868 | return $params; |
| @@ -594,9 +867,13 @@ | ||
| 594 | 867 | if ( $asArray ) { |
| 595 | 868 | return $params; |
| 596 | 869 | } |
| 597 | 870 | |
| 598 | - $url = ( isset( $website->url ) && '' !== $website->url ? $website->url : $website->siteurl ); | |
| 871 | + if ( null !== $custom_url && '' !== $custom_url ) { | |
| 872 | + $url = $custom_url; | |
| 873 | + } else { | |
| 874 | + $url = ( isset( $website->url ) && '' !== $website->url ? $website->url : $website->siteurl ); | |
| 875 | + } | |
| 599 | 876 | $url .= ( substr( $url, - 1 ) !== '/' ? '/' : '' ); |
| 600 | 877 | $url .= '?'; |
| 601 | 878 | |
| 602 | 879 | foreach ( $params as $key => $value ) { |
| @@ -621,8 +898,12 @@ | ||
| 621 | 898 | public static function connect_sign( $data, &$signature, $privkey, $algorithm, $site_id ) { |
| 622 | 899 | $de_privkey = MainWP_Encrypt_Data_Lib::instance()->decrypt_privkey( $privkey, $site_id ); |
| 623 | 900 | |
| 624 | 901 | if ( empty( $de_privkey ) ) { |
| 902 | + MainWP_Logger::instance()->debug( 'Error: Failed to decrypt the priv key.' ); | |
| 903 | + } | |
| 904 | + | |
| 905 | + if ( empty( $de_privkey ) ) { | |
| 625 | 906 | $de_privkey = $privkey; // compatible. |
| 626 | 907 | } |
| 627 | 908 | if ( false === $algorithm ) { |
| 628 | 909 | return openssl_sign( $data, $signature, $de_privkey ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. |
| @@ -660,11 +941,30 @@ | ||
| 660 | 941 | return null; |
| 661 | 942 | } |
| 662 | 943 | |
| 663 | 944 | /** |
| 945 | + * Format a privacy-safe diagnostic for an unexpected Child response. | |
| 946 | + * | |
| 947 | + * @param mixed $website Website information. | |
| 948 | + * @param mixed $data Raw response data. | |
| 949 | + * | |
| 950 | + * @return string Redacted diagnostic message. | |
| 951 | + */ | |
| 952 | + private static function format_unexpected_response_log( $website, $data ) { | |
| 953 | + $site_id = 0; | |
| 954 | + if ( is_object( $website ) && property_exists( $website, 'id' ) && 0 < (int) $website->id ) { | |
| 955 | + $site_id = (int) $website->id; | |
| 956 | + } | |
| 957 | + | |
| 958 | + $response_bytes = is_string( $data ) ? strlen( $data ) : 0; | |
| 959 | + | |
| 960 | + return 'curl_multi_getcontent :: unexpected response :: [siteid=' . $site_id . '] :: [response_bytes=' . $response_bytes . ']'; | |
| 961 | + } | |
| 962 | + | |
| 963 | + /** | |
| 664 | 964 | * Method fetch_urls_authed() |
| 665 | 965 | * |
| 666 | - * Fetch authorized URLs. | |
| 966 | + * Fetches data from child sites if authenticated. | |
| 667 | 967 | * |
| 668 | 968 | * @param object $websites Websites information. |
| 669 | 969 | * @param string $what Action to perform. |
| 670 | 970 | * @param array $params Request parameters. |
| @@ -687,9 +987,12 @@ | ||
| 687 | 987 | if ( ! is_array( $params ) ) { |
| 688 | 988 | $params = array(); |
| 689 | 989 | } |
| 690 | 990 | |
| 691 | - $chunkSize = apply_filters( 'mainwp_fetch_urls_chunk_size', 10 ); | |
| 991 | + $sleep_int = (int) get_option( 'mainwp_chunksleepinterval', 5 ); | |
| 992 | + $chunkSize = (int) get_option( 'mainwp_chunksitesnumber', 10 ); | |
| 993 | + | |
| 994 | + $chunkSize = apply_filters( 'mainwp_fetch_urls_chunk_size', $chunkSize ); | |
| 692 | 995 | if ( count( $websites ) > $chunkSize ) { |
| 693 | 996 | $total = count( $websites ); |
| 694 | 997 | $loops = ceil( $total / $chunkSize ); |
| 695 | 998 | for ( $i = 0; $i < $loops; $i++ ) { |
| @@ -694,9 +997,9 @@ | ||
| 694 | 997 | $loops = ceil( $total / $chunkSize ); |
| 695 | 998 | for ( $i = 0; $i < $loops; $i++ ) { |
| 696 | 999 | $newSites = array_slice( $websites, $i * $chunkSize, $chunkSize, true ); |
| 697 | 1000 | static::fetch_urls_authed( $newSites, $what, $params, $handler, $output, $whatPage, $others ); |
| 698 | - sleep( 5 ); | |
| 1001 | + sleep( $sleep_int ); | |
| 699 | 1002 | } |
| 700 | 1003 | |
| 701 | 1004 | return false; |
| 702 | 1005 | } |
| @@ -703,9 +1006,26 @@ | ||
| 703 | 1006 | |
| 704 | 1007 | $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)'; |
| 705 | 1008 | $mh = curl_multi_init(); |
| 706 | 1009 | |
| 707 | - $timeout = 20 * 60 * 60; | |
| 1010 | + /** | |
| 1011 | + * Filter: mainwp_fetch_url_site_timeout | |
| 1012 | + * | |
| 1013 | + * Filters the request timeout ( CURLOPT_TIMEOUT + PHP time limit ) used | |
| 1014 | + * for child site requests. Defaults to 20 hours to accommodate the | |
| 1015 | + * longest operations ( upgrades, backups ); short-lived callers such as | |
| 1016 | + * the URL-correction verify probe bound it much tighter. | |
| 1017 | + * | |
| 1018 | + * @param int $timeout Timeout in seconds. Default 72000 ( 20 hours ). | |
| 1019 | + * Values below 1 are ignored ( 0 would disable the | |
| 1020 | + * cURL timeout entirely ) and fall back to the default. | |
| 1021 | + * | |
| 1022 | + * @since 6.2 | |
| 1023 | + */ | |
| 1024 | + $timeout = (int) apply_filters( 'mainwp_fetch_url_site_timeout', 20 * 60 * 60 ); | |
| 1025 | + if ( $timeout <= 0 ) { | |
| 1026 | + $timeout = 20 * 60 * 60; | |
| 1027 | + } | |
| 708 | 1028 | |
| 709 | 1029 | $disabled_functions = ini_get( 'disable_functions' ); |
| 710 | 1030 | $handleToWebsite = array(); |
| 711 | 1031 | $requestUrls = array(); |
| @@ -739,13 +1059,16 @@ | ||
| 739 | 1059 | } else { |
| 740 | 1060 | $url .= 'admin-ajax.php'; |
| 741 | 1061 | } |
| 742 | 1062 | |
| 1063 | + $http_user = null; | |
| 1064 | + $http_pass = null; | |
| 1065 | + | |
| 743 | 1066 | if ( property_exists( $website, 'http_user' ) ) { |
| 744 | - $http_user = $website->http_user; | |
| 1067 | + $http_user = MainWP_Credential_Storage::decrypt_credential( $website->http_user ); | |
| 745 | 1068 | } |
| 746 | 1069 | if ( property_exists( $website, 'http_pass' ) ) { |
| 747 | - $http_pass = $website->http_pass; | |
| 1070 | + $http_pass = MainWP_Credential_Storage::decrypt_credential( $website->http_pass ); | |
| 748 | 1071 | } |
| 749 | 1072 | |
| 750 | 1073 | if ( isset( $params ) && isset( $params['new_post'] ) ) { |
| 751 | 1074 | |
| @@ -808,9 +1131,10 @@ | ||
| 808 | 1131 | // to fix. |
| 809 | 1132 | if ( defined( 'LOGGED_IN_SALT' ) && defined( 'NONCE_SALT' ) ) { |
| 810 | 1133 | $cookie_salt = sha1( sha1( 'mainwp' . LOGGED_IN_SALT . $website->id ) . NONCE_SALT . 'WP_Cookie' ); // NOSONAR - safe for salt file name. |
| 811 | 1134 | } else { |
| 812 | - $cookie_salt = sha1( sha1( 'mainwp' . $website->id ) . 'WP_Cookie' ); // NOSONAR - safe for salt file name. | |
| 1135 | + // MWP-1558: misconfigured WP installs (no salts) previously used unsalted SHA1, which is enumerable. Fall back to the per-install MainWP filename secret instead. | |
| 1136 | + $cookie_salt = MainWP_System_Utility::get_private_filename( 'cookies', $website->id, 'WP_Cookie' ); | |
| 813 | 1137 | } |
| 814 | 1138 | $cookieFile = $cookieDir . '/' . $cookie_salt; |
| 815 | 1139 | if ( ! file_exists( $cookieFile ) ) { |
| 816 | 1140 | @file_put_contents( $cookieFile, '' ); |
| @@ -831,8 +1155,9 @@ | ||
| 831 | 1155 | $postdata = static::get_post_data_authed( $website, $what, $params ); |
| 832 | 1156 | curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata ); |
| 833 | 1157 | curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 ); |
| 834 | 1158 | curl_setopt( $ch, CURLOPT_USERAGENT, $agent ); |
| 1159 | + curl_setopt( $ch, CURLOPT_REFERER, get_option( 'siteurl' ) ); | |
| 835 | 1160 | curl_setopt( $ch, CURLOPT_ENCODING, 'none' ); |
| 836 | 1161 | if ( ! empty( $http_user ) && ! empty( $http_pass ) ) { |
| 837 | 1162 | $http_pass = stripslashes( $http_pass ); |
| 838 | 1163 | curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" ); |
| @@ -891,10 +1216,12 @@ | ||
| 891 | 1216 | } |
| 892 | 1217 | } |
| 893 | 1218 | |
| 894 | 1219 | if ( empty( $disabled_functions ) || ( false === stristr( $disabled_functions, 'curl_multi_exec' ) ) ) { |
| 895 | - $lastRun = 0; | |
| 1220 | + $lastRun = 0; | |
| 1221 | + $retry_added = false; | |
| 896 | 1222 | do { |
| 1223 | + $retry_added = false; | |
| 897 | 1224 | if ( 20 < time() - $lastRun ) { |
| 898 | 1225 | MainWP_System_Utility::set_time_limit( $timeout ); |
| 899 | 1226 | $lastRun = time(); |
| 900 | 1227 | } |
| @@ -905,16 +1232,32 @@ | ||
| 905 | 1232 | $data = curl_multi_getcontent( $info['handle'] ); |
| 906 | 1233 | $contains = ( 0 < preg_match( '/<mainwp>(.*)<\/mainwp>/', $data, $results ) ); |
| 907 | 1234 | curl_multi_remove_handle( $mh, $info['handle'] ); |
| 908 | 1235 | |
| 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; | |
| 1236 | + $rid = static::get_resource_id( $info['handle'] ); | |
| 1237 | + if ( ! $contains && isset( $requestUrls[ $rid ] ) ) { | |
| 1238 | + curl_setopt( $info['handle'], CURLOPT_URL, $requestUrls[ $rid ] ); | |
| 1239 | + curl_setopt( $info['handle'], CURLOPT_FRESH_CONNECT, true ); | |
| 1240 | + curl_setopt( $info['handle'], CURLOPT_FORBID_REUSE, true ); | |
| 1241 | + $add_retry = curl_multi_add_handle( $mh, $info['handle'] ); | |
| 1242 | + if ( CURLM_OK === $add_retry ) { | |
| 1243 | + $mrc = curl_multi_exec( $mh, $running ); | |
| 1244 | + | |
| 1245 | + if ( CURLM_OK === $mrc ) { | |
| 1246 | + $retry_added = true; | |
| 1247 | + unset( $requestUrls[ $rid ] ); | |
| 1248 | + continue; // libcurl updates $running automatically. | |
| 1249 | + } | |
| 1250 | + | |
| 1251 | + curl_multi_remove_handle( $mh, $info['handle'] ); | |
| 1252 | + } | |
| 915 | 1253 | } |
| 916 | 1254 | |
| 1255 | + if ( ! $contains ) { | |
| 1256 | + $log_website = isset( $handleToWebsite[ $rid ] ) ? $handleToWebsite[ $rid ] : null; | |
| 1257 | + MainWP_Logger::instance()->debug( static::format_unexpected_response_log( $log_website, $data ) ); | |
| 1258 | + } | |
| 1259 | + | |
| 917 | 1260 | if ( null !== $handler ) { |
| 918 | 1261 | $site = &$handleToWebsite[ static::get_resource_id( $info['handle'] ) ]; |
| 919 | 1262 | call_user_func_array( $handler, array( $data, $site, &$output, $params ) ); |
| 920 | 1263 | } |
| @@ -919,17 +1262,17 @@ | ||
| 919 | 1262 | call_user_func_array( $handler, array( $data, $site, &$output, $params ) ); |
| 920 | 1263 | } |
| 921 | 1264 | |
| 922 | 1265 | unset( $handleToWebsite[ static::get_resource_id( $info['handle'] ) ] ); |
| 923 | - if ( 'resource' === gettype( $info['handle'] ) ) { | |
| 1266 | + if ( static::is_valid_curl_handle( $info['handle'] ) ) { | |
| 924 | 1267 | curl_close( $info['handle'] ); |
| 925 | 1268 | } |
| 926 | 1269 | unset( $info['handle'] ); |
| 927 | 1270 | } |
| 928 | 1271 | usleep( 10000 ); |
| 929 | - } while ( $running > 0 ); | |
| 1272 | + } while ( $running > 0 || $retry_added ); | |
| 930 | 1273 | |
| 931 | - if ( 'resource' === gettype( $mh ) ) { | |
| 1274 | + if ( static::is_valid_curl_handle( $mh ) ) { | |
| 932 | 1275 | curl_multi_close( $mh ); |
| 933 | 1276 | } |
| 934 | 1277 | } else { |
| 935 | 1278 | foreach ( $requestHandles as $ch ) { |
| @@ -952,9 +1295,9 @@ | ||
| 952 | 1295 | * |
| 953 | 1296 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD. |
| 954 | 1297 | * @return string The "Expect" header. |
| 955 | 1298 | */ |
| 956 | - protected static function get_expect_header( $data ) { | |
| 1299 | + public static function get_expect_header( $data ) { | |
| 957 | 1300 | if ( ! is_array( $data ) ) { |
| 958 | 1301 | return strlen( (string) $data ) >= 1048576 ? '100-Continue' : ''; |
| 959 | 1302 | } |
| 960 | 1303 | |
| @@ -1088,8 +1431,9 @@ | ||
| 1088 | 1431 | * @param bool $checkConstraints Whether or not to check constraints. |
| 1089 | 1432 | * @param bool $pForceFetch Whether or not to force the fetch. |
| 1090 | 1433 | * @param bool $pRetryFailed Whether or not to retry the fetch process. |
| 1091 | 1434 | * @param null $rawResponse Raw response. |
| 1435 | + * @param bool $verify_signature Wether verify signature data. | |
| 1092 | 1436 | * |
| 1093 | 1437 | * @return mixed $information |
| 1094 | 1438 | * |
| 1095 | 1439 | * @uses \MainWP\Dashboard\MainWP_Monitoring_Handler::handle_check_website() |
| @@ -1102,9 +1446,10 @@ | ||
| 1102 | 1446 | $params = null, |
| 1103 | 1447 | $checkConstraints = false, |
| 1104 | 1448 | $pForceFetch = false, |
| 1105 | 1449 | $pRetryFailed = true, |
| 1106 | - $rawResponse = null | |
| 1450 | + $rawResponse = null, | |
| 1451 | + $verify_signature = false | |
| 1107 | 1452 | ) { |
| 1108 | 1453 | unset( $pForceFetch ); |
| 1109 | 1454 | |
| 1110 | 1455 | // to support demo data. |
| @@ -1111,8 +1456,42 @@ | ||
| 1111 | 1456 | if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) { |
| 1112 | 1457 | return MainWP_Demo_Handle::get_instance()->handle_action_demo( $website, $what ); |
| 1113 | 1458 | } |
| 1114 | 1459 | |
| 1460 | + /** | |
| 1461 | + * Filter to mock fetch_url_authed response before any HTTP/signing occurs. | |
| 1462 | + * | |
| 1463 | + * This filter fires early, before OpenSSL signing or HTTP requests, allowing | |
| 1464 | + * tests to bypass child site communication entirely. | |
| 1465 | + * | |
| 1466 | + * SECURITY WARNING - TEST ONLY: | |
| 1467 | + * This filter ONLY fires when ALL of the following conditions are met: | |
| 1468 | + * 1. MAINWP_TESTING_MODE constant is defined and true | |
| 1469 | + * 2. A PHPUnit test harness constant is present (WP_TESTS_DOMAIN, PHPUNIT_COMPOSER_INSTALL, or WP_TESTS_DIR) | |
| 1470 | + * | |
| 1471 | + * This triple-check prevents malicious code from defining MAINWP_TESTING_MODE | |
| 1472 | + * in production to spoof child site responses. | |
| 1473 | + * | |
| 1474 | + * IMPORTANT: MAINWP_TESTING_MODE must ONLY be defined in the PHPUnit bootstrap | |
| 1475 | + * file (tests/bootstrap.php). Defining it in production code, wp-config.php, or | |
| 1476 | + * plugin files would create a security vulnerability allowing response spoofing. | |
| 1477 | + * | |
| 1478 | + * @since 5.4 | |
| 1479 | + * | |
| 1480 | + * @param mixed $pre_result Return non-false to short-circuit and return this value. | |
| 1481 | + * @param object $website Website object being communicated with. | |
| 1482 | + * @param string $what Action being performed (e.g., 'plugin_action'). | |
| 1483 | + * @param array $params Request parameters. | |
| 1484 | + * @return mixed Array to return early, false to proceed normally. | |
| 1485 | + */ | |
| 1486 | + $is_phpunit_env = defined( 'WP_TESTS_DOMAIN' ) || defined( 'PHPUNIT_COMPOSER_INSTALL' ) || ( defined( 'WP_TESTS_DIR' ) && WP_TESTS_DIR ); | |
| 1487 | + if ( defined( 'MAINWP_TESTING_MODE' ) && MAINWP_TESTING_MODE && $is_phpunit_env ) { | |
| 1488 | + $pre_result = apply_filters( 'mainwp_fetch_url_authed_pre', false, $website, $what, $params, $verify_signature ); | |
| 1489 | + if ( false !== $pre_result ) { | |
| 1490 | + return $pre_result; | |
| 1491 | + } | |
| 1492 | + } | |
| 1493 | + | |
| 1115 | 1494 | if ( ! is_array( $params ) ) { |
| 1116 | 1495 | $params = array(); |
| 1117 | 1496 | } |
| 1118 | 1497 | |
| @@ -1120,10 +1499,8 @@ | ||
| 1120 | 1499 | 'force_use_ipv4' => $website->force_use_ipv4, |
| 1121 | 1500 | 'upgrade' => ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ), |
| 1122 | 1501 | ); |
| 1123 | 1502 | |
| 1124 | - $request_update = MainWP_Premium_Update::maybe_request_premium_updates( $website, $what, $params ); | |
| 1125 | - | |
| 1126 | 1503 | if ( isset( $rawResponse ) && $rawResponse ) { |
| 1127 | 1504 | $others['raw_response'] = 'yes'; |
| 1128 | 1505 | } |
| 1129 | 1506 | |
| @@ -1131,11 +1508,17 @@ | ||
| 1131 | 1508 | |
| 1132 | 1509 | $updating_website = false; |
| 1133 | 1510 | $type = ''; |
| 1134 | 1511 | $list = ''; |
| 1135 | - if ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ) { | |
| 1512 | + | |
| 1513 | + $premium_update = 'process_premium_updates' === $what && ! empty( $params['premium_perform'] ) && 'premium_update' === $params['premium_perform'] ? true : false; | |
| 1514 | + | |
| 1515 | + if ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what || $premium_update ) { | |
| 1136 | 1516 | $updating_website = true; |
| 1137 | - if ( 'upgradeplugintheme' === $what || 'upgradetranslation' === $what ) { | |
| 1517 | + if ( $premium_update ) { | |
| 1518 | + $type = $params['premium_type']; | |
| 1519 | + $list = $params['list']; | |
| 1520 | + } elseif ( 'upgradeplugintheme' === $what || 'upgradetranslation' === $what ) { | |
| 1138 | 1521 | $type = $params['type']; |
| 1139 | 1522 | $list = $params['list']; |
| 1140 | 1523 | } else { |
| 1141 | 1524 | $type = 'wp'; |
| @@ -1157,36 +1540,69 @@ | ||
| 1157 | 1540 | */ |
| 1158 | 1541 | do_action( 'mainwp_website_before_updated', $website, $type, $list ); |
| 1159 | 1542 | } |
| 1160 | 1543 | |
| 1544 | + $information = array(); | |
| 1545 | + $output = array(); | |
| 1546 | + | |
| 1547 | + if ( 'stats' === $what || ( 'upgradeplugintheme' === $what && isset( $params['type'] ) ) ) { | |
| 1548 | + $request_update = MainWP_Premium_Update::maybe_request_premium_updates( $website, $what, $params, $output_result ); | |
| 1549 | + if ( $request_update ) { | |
| 1550 | + // Return the information here. | |
| 1551 | + return $output_result; | |
| 1552 | + } | |
| 1553 | + } | |
| 1554 | + | |
| 1555 | + // MWP-1548: decrypt http_user / http_pass before they hit the | |
| 1556 | + // outbound HTTP Basic Auth header. Legacy plaintext rows pass | |
| 1557 | + // through unchanged. | |
| 1558 | + $http_user_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_user ); | |
| 1559 | + $http_pass_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_pass ); | |
| 1560 | + | |
| 1161 | 1561 | if ( 'renew' === $what ) { |
| 1162 | - $postdata = static::get_renew_post_data_authed( $website, $what ); | |
| 1562 | + $postdata = static::get_renew_post_data_authed( | |
| 1563 | + $website, | |
| 1564 | + $what, | |
| 1565 | + array( | |
| 1566 | + 'verify_signature' => $verify_signature, | |
| 1567 | + ) | |
| 1568 | + ); | |
| 1163 | 1569 | } else { |
| 1164 | - $postdata = static::get_post_data_authed( $website, $what, $params ); | |
| 1570 | + $postdata = static::get_post_data_authed( | |
| 1571 | + $website, | |
| 1572 | + $what, | |
| 1573 | + $params, | |
| 1574 | + array( | |
| 1575 | + 'verify_signature' => $verify_signature, | |
| 1576 | + 'http_user_plain' => $http_user_plain, | |
| 1577 | + 'http_pass_plain' => $http_pass_plain, | |
| 1578 | + ) | |
| 1579 | + ); | |
| 1165 | 1580 | |
| 1166 | 1581 | } |
| 1582 | + | |
| 1167 | 1583 | $others['function'] = $what; |
| 1168 | 1584 | |
| 1169 | - $information = array(); | |
| 1585 | + $information = static::fetch_url( $website, $website->url, $postdata, $checkConstraints, $website->verify_certificate, $pRetryFailed, $http_user_plain, $http_pass_plain, $website->ssl_version, $others, $output ); | |
| 1170 | 1586 | |
| 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 ); | |
| 1587 | + if ( ! empty( $output ) ) { | |
| 1588 | + if ( ! is_array( $information ) ) { | |
| 1589 | + $information = array(); | |
| 1590 | + } | |
| 1591 | + $information['fetch_url_output'] = $output; | |
| 1188 | 1592 | } |
| 1593 | + /** | |
| 1594 | + * Fires immediately after fetch url action. | |
| 1595 | + * | |
| 1596 | + * @param object $website website. | |
| 1597 | + * @param array $information information result data. | |
| 1598 | + * @param string $what action. | |
| 1599 | + * @param array $params params input array. | |
| 1600 | + * @param array $others others input array. | |
| 1601 | + * | |
| 1602 | + * @since 4.5.1.1 | |
| 1603 | + */ | |
| 1604 | + do_action( 'mainwp_fetch_url_authed', $website, $information, $what, $params, $others ); | |
| 1189 | 1605 | |
| 1190 | 1606 | if ( is_array( $information ) && isset( $information['sync'] ) && ! empty( $information['sync'] ) ) { |
| 1191 | 1607 | MainWP_Sync::sync_information_array( $website, $information['sync'] ); |
| 1192 | 1608 | unset( $information['sync'] ); |
| @@ -1206,9 +1622,9 @@ | ||
| 1206 | 1622 | * @since Unknown |
| 1207 | 1623 | */ |
| 1208 | 1624 | do_action( 'mainwp_website_updated', $website, $type, $list, $information ); |
| 1209 | 1625 | if ( 1 === (int) get_option( 'mainwp_check_http_response', 0 ) ) { |
| 1210 | - MainWP_Monitoring_Handler::handle_check_website( $website ); | |
| 1626 | + MainWP_Monitoring_Handler::handle_check_website( $website, true ); | |
| 1211 | 1627 | } |
| 1212 | 1628 | } |
| 1213 | 1629 | |
| 1214 | 1630 | return $information; |
| @@ -1232,9 +1648,9 @@ | ||
| 1232 | 1648 | * @param array $output Output values. |
| 1233 | 1649 | * |
| 1234 | 1650 | * @return mixed static::fetch_url() Fetch URL. |
| 1235 | 1651 | */ |
| 1236 | - public static function fetch_url_not_authed( // NOSONAR - compatible. | |
| 1652 | + public static function fetch_url_not_authed( // phpcs:ignore -- NOSONAR - compatible. | |
| 1237 | 1653 | $url, |
| 1238 | 1654 | $admin, |
| 1239 | 1655 | $what, |
| 1240 | 1656 | $params = null, |
| @@ -1360,8 +1776,41 @@ | ||
| 1360 | 1776 | $others = array(), |
| 1361 | 1777 | &$output = array() |
| 1362 | 1778 | ) { |
| 1363 | 1779 | |
| 1780 | + /** | |
| 1781 | + * Enables data to be returned prior to connecting to the site. | |
| 1782 | + * | |
| 1783 | + * Dev/test override only. Gated behind the MAINWP_DEV_FILTERS_ENABLED | |
| 1784 | + * constant so the filter does not dispatch in production. The filter | |
| 1785 | + * receives plaintext HTTP Basic Auth credentials and the full $website | |
| 1786 | + * DB row (including privkey); enabling it in production would expose | |
| 1787 | + * those values to any 3rd-party plugin hooking the filter. | |
| 1788 | + * | |
| 1789 | + * To enable in a dev/test environment, add to wp-config.php: | |
| 1790 | + * define( 'MAINWP_DEV_FILTERS_ENABLED', true ); | |
| 1791 | + * | |
| 1792 | + * @since 5.5 | |
| 1793 | + * | |
| 1794 | + * @param mixed false | |
| 1795 | + * @param mixed $website | |
| 1796 | + * @param mixed $url | |
| 1797 | + * @param mixed $postdata | |
| 1798 | + * @param mixed $checkConstraints | |
| 1799 | + * @param mixed $verifyCertificate | |
| 1800 | + * @param mixed $http_user | |
| 1801 | + * @param mixed $http_pass | |
| 1802 | + * @param mixed $sslVersion | |
| 1803 | + * @param mixed $others | |
| 1804 | + * @param mixed $output | |
| 1805 | + */ | |
| 1806 | + if ( defined( 'MAINWP_DEV_FILTERS_ENABLED' ) && MAINWP_DEV_FILTERS_ENABLED ) { | |
| 1807 | + $dev_data = apply_filters( 'mainwp_dev_return_data_before_connect_site', false, $website, $url, $postdata, $checkConstraints, $verifyCertificate, $http_user, $http_pass, $sslVersion, $others, $output ); | |
| 1808 | + if ( false !== $dev_data ) { | |
| 1809 | + return $dev_data; | |
| 1810 | + } | |
| 1811 | + } | |
| 1812 | + | |
| 1364 | 1813 | $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)'; |
| 1365 | 1814 | |
| 1366 | 1815 | if ( ! empty( $website ) ) { |
| 1367 | 1816 | MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Request to [' . $url . '] [' . MainWP_Utility::value_to_string( $postdata, 1 ) . ']' ); |
| @@ -1384,8 +1833,10 @@ | ||
| 1384 | 1833 | $cookieDir = $dirs[0] . 'cookies'; |
| 1385 | 1834 | |
| 1386 | 1835 | static::init_cookiesdir( $cookieDir ); |
| 1387 | 1836 | |
| 1837 | + $fetch_track_id = MainWP_Execution_Helper::execute_call_track( 'start_point', $website, $postdata ); | |
| 1838 | + | |
| 1388 | 1839 | $ch = curl_init(); |
| 1389 | 1840 | |
| 1390 | 1841 | $proxy = new \WP_HTTP_Proxy(); |
| 1391 | 1842 | if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
| @@ -1403,9 +1854,10 @@ | ||
| 1403 | 1854 | // to fix. |
| 1404 | 1855 | if ( defined( 'LOGGED_IN_SALT' ) && defined( 'NONCE_SALT' ) ) { |
| 1405 | 1856 | $cookie_salt = sha1( sha1( 'mainwp' . LOGGED_IN_SALT . $website->id ) . NONCE_SALT . 'WP_Cookie' ); // NOSONAR - safe for salt file name. |
| 1406 | 1857 | } else { |
| 1407 | - $cookie_salt = sha1( sha1( 'mainwp' . $website->id ) . 'WP_Cookie' ); // NOSONAR - safe for salt file name. | |
| 1858 | + // MWP-1558: misconfigured WP installs (no salts) previously used unsalted SHA1, which is enumerable. Fall back to the per-install MainWP filename secret instead. | |
| 1859 | + $cookie_salt = MainWP_System_Utility::get_private_filename( 'cookies', $website->id, 'WP_Cookie' ); | |
| 1408 | 1860 | } |
| 1409 | 1861 | $cookieFile = $cookieDir . '/' . $cookie_salt; |
| 1410 | 1862 | if ( ! file_exists( $cookieFile ) ) { |
| 1411 | 1863 | @file_put_contents( $cookieFile, '' ); |
| @@ -1473,8 +1925,10 @@ | ||
| 1473 | 1925 | |
| 1474 | 1926 | $headers = array( 'X-Requested-With' => 'XMLHttpRequest' ); |
| 1475 | 1927 | $headers['Expect'] = static::get_expect_header( $postdata ); |
| 1476 | 1928 | |
| 1929 | + $headers = apply_filters( 'mainwp_connect_http_request_headers', $headers, $website ); | |
| 1930 | + | |
| 1477 | 1931 | if ( class_exists( '\WpOrg\Requests\Requests' ) ) { |
| 1478 | 1932 | $headers = \WpOrg\Requests\Requests::flatten( $headers ); |
| 1479 | 1933 | } else { |
| 1480 | 1934 | $headers = \Requests::flatten( $headers ); |
| @@ -1500,9 +1954,25 @@ | ||
| 1500 | 1954 | if ( $force_use_ipv4 && defined( 'CURLOPT_IPRESOLVE' ) && defined( 'CURL_IPRESOLVE_V4' ) ) { |
| 1501 | 1955 | curl_setopt( $ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); |
| 1502 | 1956 | } |
| 1503 | 1957 | |
| 1504 | - $timeout = 20 * 60 * 60; | |
| 1958 | + $what = ''; | |
| 1959 | + if ( is_array( $others ) && isset( $others['function'] ) ) { | |
| 1960 | + $what = $others['function']; | |
| 1961 | + } | |
| 1962 | + | |
| 1963 | + if ( 'deactivate' === $what ) { | |
| 1964 | + $timeout = 120; // 2 minutes. | |
| 1965 | + } else { | |
| 1966 | + $timeout = 20 * 60 * 60; | |
| 1967 | + } | |
| 1968 | + | |
| 1969 | + /** This filter is documented in class/class-mainwp-connect.php */ | |
| 1970 | + $timeout = (int) apply_filters( 'mainwp_fetch_url_site_timeout', $timeout, $what ); | |
| 1971 | + if ( $timeout <= 0 ) { | |
| 1972 | + $timeout = 20 * 60 * 60; // values below 1 would disable the cURL timeout entirely. | |
| 1973 | + } | |
| 1974 | + | |
| 1505 | 1975 | curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout ); |
| 1506 | 1976 | MainWP_System_Utility::set_time_limit( $timeout ); |
| 1507 | 1977 | |
| 1508 | 1978 | MainWP_Utility::end_session(); |
| @@ -1514,27 +1984,38 @@ | ||
| 1514 | 1984 | $mh = @curl_multi_init(); |
| 1515 | 1985 | @curl_multi_add_handle( $mh, $ch ); |
| 1516 | 1986 | |
| 1517 | 1987 | $lastRun = 0; |
| 1988 | + $running = null; | |
| 1989 | + | |
| 1518 | 1990 | do { |
| 1519 | 1991 | if ( 20 < time() - $lastRun ) { |
| 1520 | 1992 | MainWP_System_Utility::set_time_limit( $timeout ); |
| 1521 | 1993 | $lastRun = time(); |
| 1522 | 1994 | } |
| 1523 | - @curl_multi_exec( $mh, $running ); | |
| 1524 | - @curl_multi_select( $mh ); | |
| 1995 | + | |
| 1996 | + do { | |
| 1997 | + $mrc = curl_multi_exec( $mh, $running ); | |
| 1998 | + } while ( CURLM_CALL_MULTI_PERFORM === $mrc ); | |
| 1999 | + | |
| 2000 | + $rc = curl_multi_select( $mh, 1.0 ); | |
| 2001 | + if ( -1 === $rc ) { | |
| 2002 | + usleep( 100000 ); | |
| 2003 | + } | |
| 2004 | + | |
| 1525 | 2005 | while ( $info = @curl_multi_info_read( $mh ) ) { |
| 1526 | - $data = @curl_multi_getcontent( $info['handle'] ); | |
| 1527 | - | |
| 2006 | + $data = @curl_multi_getcontent( $info['handle'] ); | |
| 1528 | 2007 | $http_status = @curl_getinfo( $info['handle'], CURLINFO_HTTP_CODE ); |
| 1529 | 2008 | $err = @curl_error( $info['handle'] ); |
| 1530 | 2009 | $real_url = @curl_getinfo( $info['handle'], CURLINFO_EFFECTIVE_URL ); |
| 1531 | 2010 | |
| 1532 | 2011 | @curl_multi_remove_handle( $mh, $info['handle'] ); |
| 2012 | + curl_close( $info['handle'] ); | |
| 1533 | 2013 | } |
| 1534 | 2014 | usleep( 10000 ); |
| 1535 | 2015 | } while ( $running > 0 ); |
| 1536 | - if ( 'resource' === gettype( $mh ) ) { | |
| 2016 | + | |
| 2017 | + if ( static::is_valid_curl_handle( $mh ) ) { | |
| 1537 | 2018 | @curl_multi_close( $mh ); |
| 1538 | 2019 | } |
| 1539 | 2020 | } else { |
| 1540 | 2021 | $data = @curl_exec( $ch ); |
| @@ -1540,13 +2021,16 @@ | ||
| 1540 | 2021 | $data = @curl_exec( $ch ); |
| 1541 | 2022 | $http_status = @curl_getinfo( $ch, CURLINFO_HTTP_CODE ); |
| 1542 | 2023 | $err = @curl_error( $ch ); |
| 1543 | 2024 | $real_url = @curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL ); |
| 2025 | + curl_close( $ch ); | |
| 1544 | 2026 | } |
| 1545 | 2027 | |
| 1546 | 2028 | $host = wp_parse_url( $real_url, PHP_URL_HOST ); |
| 1547 | 2029 | $ip = gethostbyname( $host ); |
| 1548 | 2030 | |
| 2031 | + MainWP_Execution_Helper::execute_call_track( 'end_point', $website, $postdata, $fetch_track_id, 'fetch site' ); | |
| 2032 | + | |
| 1549 | 2033 | if ( null !== $website ) { |
| 1550 | 2034 | MainWP_DB_Common::instance()->insert_or_update_request_log( $website->id, $ip, null, microtime( true ) ); |
| 1551 | 2035 | } |
| 1552 | 2036 | |
| @@ -1551,9 +2035,20 @@ | ||
| 1551 | 2035 | } |
| 1552 | 2036 | |
| 1553 | 2037 | $raw_response = isset( $others['raw_response'] ) && 'yes' === $others['raw_response'] ? true : false; |
| 1554 | 2038 | |
| 1555 | - $output['fetch_data'] = $data; | |
| 2039 | + $hidden_data = '[hidden response data]'; | |
| 2040 | + | |
| 2041 | + if ( ! apply_filters( 'mainwp_hide_raw_connection_response_data', true ) ) { | |
| 2042 | + $hidden_data = $data; | |
| 2043 | + } | |
| 2044 | + | |
| 2045 | + if ( ! is_array( $output ) ) { | |
| 2046 | + $output = array(); | |
| 2047 | + } | |
| 2048 | + | |
| 2049 | + $output['fetch_data'] = $hidden_data; | |
| 2050 | + | |
| 1556 | 2051 | $output['http_status'] = (int) $http_status; |
| 1557 | 2052 | |
| 1558 | 2053 | MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'http status: [' . $http_status . '] err: [' . $err . ']' ); |
| 1559 | 2054 | if ( '400' === $http_status ) { |
| @@ -1563,39 +2058,85 @@ | ||
| 1563 | 2058 | MainWP_Logger::instance()->log_execution_time( 'fetch_url_site :: [url=' . $url . ']' ); |
| 1564 | 2059 | |
| 1565 | 2060 | $thr_error = null; |
| 1566 | 2061 | |
| 2062 | + if ( in_array( $what, array( 'installplugintheme', 'upgradeplugintheme', 'upgradetranslation', 'upgrade', 'stats', 'renew', 'reconnect' ), true ) ) { | |
| 2063 | + MainWP_Cache_Helper::invalidate_cache_group( MainWP_Cache_Helper::CGR_UPDATES ); | |
| 2064 | + MainWP_Cache_Warm_Helper::invalidate_pages_by_site_actions( $what ); | |
| 2065 | + } | |
| 2066 | + | |
| 2067 | + if ( 'process_premium_updates' === $what ) { | |
| 2068 | + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'function: [process_premium_updates] response data: [' . MainWP_Utility::value_to_string( $data ) . ']' ); | |
| 2069 | + } | |
| 2070 | + | |
| 1567 | 2071 | if ( ( false === $data ) && empty( $http_status ) ) { |
| 1568 | 2072 | 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 | |
| 2073 | + $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped | |
| 2074 | + $output['error_category'] = 'http_error'; | |
| 1570 | 2075 | } elseif ( empty( $data ) && ! empty( $err ) ) { |
| 1571 | 2076 | 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 | |
| 2077 | + $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped | |
| 2078 | + $output['error_category'] = 'http_error'; | |
| 2079 | + $output['error_code'] = 'http_request_failed'; | |
| 2080 | + $output['error_message'] = $err; | |
| 1573 | 2081 | } 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. | |
| 2082 | + $output['connection_step'] = 'verify_credentials'; | |
| 2083 | + $result = $results[1]; | |
| 2084 | + $information = MainWP_System_Utility::get_child_response( base64_decode( $result ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible. | |
| 1576 | 2085 | unset( $output['fetch_data'] ); // hide the data. |
| 1577 | 2086 | $pdt = is_string( $postdata ) ? $postdata : ''; |
| 1578 | 2087 | $data_log = is_array( $postdata ) ? print_r( $postdata, true ) : $pdt; //phpcs:ignore -- good. |
| 1579 | 2088 | MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', '[' . $url . '] postdata [' . $data_log . '] information: [OK]' ); //phpcs:ignore -- ok. |
| 2089 | + | |
| 2090 | + $error_code = is_array( $information ) && isset( $information['error_code'] ) ? sanitize_text_field( wp_unslash( $information['error_code'] ) ) : ''; | |
| 2091 | + if ( ! empty( $error_code ) ) { | |
| 2092 | + $output['child_error_code'] = $error_code; | |
| 2093 | + } | |
| 2094 | + | |
| 2095 | + if ( is_array( $information ) && $website ) { | |
| 2096 | + // Process to ensure compatibility with old and new Child versions. | |
| 2097 | + $new_sync = isset( $information['support_advanced_sign'] ); | |
| 2098 | + $old_sync = ! $new_sync && ( isset( $information['mainwpdir'], $information['uniqueId'] ) || isset( $information['version'], $information['wpversion'], $information['wpe'] ) ); | |
| 2099 | + if ( $new_sync || $old_sync ) { | |
| 2100 | + $adv_sign_support = MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' ); | |
| 2101 | + if ( ! empty( $information['support_advanced_sign'] ) && 0 === (int) $adv_sign_support ) { | |
| 2102 | + MainWP_DB::instance()->update_website_option( $website, 'support_advanced_sign', 1 ); | |
| 2103 | + } elseif ( empty( $information['support_advanced_sign'] ) && 1 === (int) $adv_sign_support ) { | |
| 2104 | + MainWP_DB::instance()->update_website_option( $website, 'support_advanced_sign', 0 ); | |
| 2105 | + } | |
| 2106 | + } | |
| 2107 | + } | |
| 2108 | + | |
| 2109 | + if ( 'process_premium_updates' === $what ) { | |
| 2110 | + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'function: [process_premium_updates] decoded data: [' . MainWP_Utility::value_to_string( $information ) . ']' ); | |
| 2111 | + } | |
| 2112 | + | |
| 1580 | 2113 | return $information; |
| 1581 | 2114 | } elseif ( 200 === (int) $http_status && ! empty( $err ) ) { |
| 1582 | - $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped | |
| 2115 | + $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped | |
| 2116 | + $output['error_category'] = 'http_error'; | |
| 2117 | + $output['error_code'] = 'http_request_failed'; | |
| 2118 | + $output['error_message'] = $err; | |
| 2119 | + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', '[' . $url . '] error [' . $err . ']' ); //phpcs:ignore -- ok. | |
| 1583 | 2120 | } elseif ( $raw_response ) { |
| 1584 | 2121 | MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Response: [RAW]' ); |
| 1585 | 2122 | return $data; |
| 1586 | 2123 | } else { |
| 1587 | - MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] Error: NOMAINWP [data=' . ( is_string( $data ) ? $data : 'OBJECT' ) . ']' ); | |
| 2124 | + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] Error: NOMAINWP [data=' . $hidden_data . ']' ); | |
| 1588 | 2125 | $detect_wsidchk = is_string( $data ) ? strpos( $data, 'wsidchk' ) : false; |
| 1589 | 2126 | 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 ); | |
| 2127 | + $err_msg = '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.'; | |
| 2128 | + $thr_error = new MainWP_Exception( 'ERROR:' . $err_msg, $url ); | |
| 1591 | 2129 | } else { |
| 1592 | 2130 | $thr_error = new MainWP_Exception( 'NOMAINWP', $url ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped |
| 2131 | + $err_msg = 'Connection Failed. Please ensure that the MainWP Child plugin is installed and activated on the child site.'; | |
| 1593 | 2132 | } |
| 2133 | + $output['error_category'] = 'child_plugin_missing'; | |
| 2134 | + $output['error_message'] = $err_msg; | |
| 1594 | 2135 | } |
| 1595 | 2136 | |
| 1596 | 2137 | if ( null !== $thr_error ) { |
| 1597 | - $thr_error->set_data( $data ); | |
| 2138 | + $thr_error->set_data( $hidden_data ); // to compatible. | |
| 1598 | 2139 | throw $thr_error; |
| 1599 | 2140 | } |
| 1600 | 2141 | } |
| 1601 | 2142 | |
| @@ -1683,9 +2224,9 @@ | ||
| 1683 | 2224 | $lastRequest = MainWP_DB_Common::instance()->get_last_request_timestamp( $ip ); |
| 1684 | 2225 | if ( $lastRequest > ( ( microtime( true ) ) - $minimumDelay ) ) { |
| 1685 | 2226 | static::release( $identifier ); |
| 1686 | 2227 | $sleep = ( $minimumDelay - ( ( microtime( true ) ) - $lastRequest ) ) * 1000 * 1000; |
| 1687 | - $sleep = intval( $sleep ); | |
| 2228 | + $sleep = max( 0, intval( $sleep ) ); | |
| 1688 | 2229 | usleep( $sleep ); |
| 1689 | 2230 | return true; |
| 1690 | 2231 | } |
| 1691 | 2232 | return false; |
| @@ -1744,9 +2285,9 @@ | ||
| 1744 | 2285 | $wp_filesystem->delete( $file ); |
| 1745 | 2286 | } |
| 1746 | 2287 | |
| 1747 | 2288 | if ( ! $wp_filesystem->exists( dirname( $file ) ) ) { |
| 1748 | - $wp_filesystem->mkdir( dirname( $file ), 0777 ); | |
| 2289 | + $wp_filesystem->mkdir( dirname( $file ), 0750 ); // MWP-1558: tightened from 0777; downloaded files may contain backup data. | |
| 1749 | 2290 | } |
| 1750 | 2291 | |
| 1751 | 2292 | if ( ! $wp_filesystem->exists( dirname( $file ) ) ) { |
| 1752 | 2293 | throw new MainWP_Exception( esc_html__( 'MainWP plugin could not create directory in order to download the file.', 'mainwp' ) ); |
| @@ -1791,9 +2332,9 @@ | ||
| 1791 | 2332 | $http_pass = stripslashes( $http_pass ); |
| 1792 | 2333 | curl_setopt( $ch, CURLOPT_USERPWD, "$http_user:$http_pass" ); |
| 1793 | 2334 | } |
| 1794 | 2335 | curl_exec( $ch ); |
| 1795 | - if ( 'resource' === gettype( $ch ) ) { | |
| 2336 | + if ( static::is_valid_curl_handle( $ch ) ) { | |
| 1796 | 2337 | curl_close( $ch ); |
| 1797 | 2338 | } |
| 1798 | 2339 | fclose( $fp ); |
| 1799 | 2340 | } |
| @@ -1823,9 +2364,9 @@ | ||
| 1823 | 2364 | |
| 1824 | 2365 | if ( $hasWPFileSystem && ! empty( $wp_filesystem ) ) { |
| 1825 | 2366 | |
| 1826 | 2367 | if ( ! $wp_filesystem->is_dir( $cookieDir ) ) { |
| 1827 | - $wp_filesystem->mkdir( $cookieDir, 0777 ); | |
| 2368 | + $wp_filesystem->mkdir( $cookieDir, 0750 ); // MWP-1558: tightened from 0777; cookies/ holds child wp-admin session cookies. | |
| 1828 | 2369 | } |
| 1829 | 2370 | |
| 1830 | 2371 | if ( ! file_exists( $cookieDir . '/.htaccess' ) ) { |
| 1831 | 2372 | $file_htaccess = $cookieDir . '/.htaccess'; |
| @@ -1838,9 +2379,9 @@ | ||
| 1838 | 2379 | } |
| 1839 | 2380 | } else { |
| 1840 | 2381 | |
| 1841 | 2382 | if ( ! file_exists( $cookieDir ) ) { |
| 1842 | - @mkdir( $cookieDir, 0777, true ); | |
| 2383 | + @mkdir( $cookieDir, 0750, true ); // MWP-1558: tightened from 0777; cookies/ holds child wp-admin session cookies. | |
| 1843 | 2384 | } |
| 1844 | 2385 | |
| 1845 | 2386 | if ( ! file_exists( $cookieDir . '/.htaccess' ) ) { |
| 1846 | 2387 | $file_htaccess = @fopen( $cookieDir . '/.htaccess', 'w+' ); |
| @@ -1889,9 +2430,9 @@ | ||
| 1889 | 2430 | curl_setopt( $ch, CURLOPT_ENCODING, 'none' ); |
| 1890 | 2431 | |
| 1891 | 2432 | $data = @curl_exec( $ch ); |
| 1892 | 2433 | $httpCode = @curl_getinfo( $ch, CURLINFO_HTTP_CODE ); |
| 1893 | - if ( 'resource' === gettype( $ch ) ) { | |
| 2434 | + if ( static::is_valid_curl_handle( $ch ) ) { | |
| 1894 | 2435 | curl_close( $ch ); |
| 1895 | 2436 | } |
| 1896 | 2437 | if ( 200 === (int) $httpCode ) { |
| 1897 | 2438 | return $data; |
| @@ -1897,8 +2438,22 @@ | ||
| 1897 | 2438 | return $data; |
| 1898 | 2439 | } else { |
| 1899 | 2440 | return false; |
| 1900 | 2441 | } |
| 2442 | + } | |
| 2443 | + | |
| 2444 | + /** | |
| 2445 | + * Method is_valid_curl_handle | |
| 2446 | + * | |
| 2447 | + * @param mixed $ch cURL handle to validate. | |
| 2448 | + * @return bool Valid curl handle. | |
| 2449 | + */ | |
| 2450 | + public static function is_valid_curl_handle( $ch ) { | |
| 2451 | + return is_resource( $ch ) | |
| 2452 | + || ( is_object( $ch ) | |
| 2453 | + && class_exists( 'CurlHandle', false ) | |
| 2454 | + && $ch instanceof \CurlHandle | |
| 2455 | + ); | |
| 1901 | 2456 | } |
| 1902 | 2457 | |
| 1903 | 2458 | /** |
| 1904 | 2459 | * Method get_favico_url() |