PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 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 All 155 releases
← All changes | class/class-mainwp-connect.php +516 -146 6.1.5 → 6.2.1 View file →
@@ -412,20 +412,33 @@
412 412 *
413 413 * @param mixed $website Array of Child Site Info.
414 414 * @param mixed $what What we are posting.
415 415 * @param null $params Post parameters.
416 + * @param array $others Other data.
416 417 *
417 418 * @return mixed null|http_build_query()
418 419 */
419 - 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 +
420 430 if ( $website && '' !== $what ) {
431 + // The nonce must never repeat. The Child stores every legacy signature it accepts, forever,
432 + // and refuses a repeat as "This request has already been used"; signing is deterministic,
433 + // so the nonce is the only thing that makes two requests for the same action differ.
421 434 $data = array();
422 435 $data['user'] = $website->adminname;
423 436 $data['function'] = $what;
424 - $data['nonce'] = wp_rand( 0, 9999 );
437 + $data['nonce'] = bin2hex( random_bytes( 16 ) );
425 438 $data['mainwpver'] = MainWP_System::$version;
426 439
427 - $params_filter = apply_filters( 'mainwp_pre_fetch_authed_data', false, $params, $what, $website );
440 + $params_filter = apply_filters( 'mainwp_pre_fetch_authed_data', false, $params, $what, $website, $verify_signature );
428 441 if ( is_array( $params_filter ) && ! empty( $params_filter ) ) {
429 442 $data = array_merge( $data, $params_filter );
430 443 }
431 444
@@ -436,48 +449,12 @@
436 449 $alg = false;
437 450 $sign_success = null;
438 451 $use_seclib = false;
439 452
440 - $data = apply_filters( 'mainwp_get_post_data_authed', $data, $website, $what, $params );
441 - if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
442 - $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.
443 - $use_seclib = true;
444 - } elseif ( function_exists( 'openssl_verify' ) ) {
445 - $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
446 - $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.
447 - if ( false !== $alg ) {
448 - $data['sign_algo'] = $alg;
449 - }
450 - }
453 + $data = apply_filters( 'mainwp_get_post_data_authed', $data, $website, $what, $params, $verify_signature );
451 454
452 - if ( $use_seclib ) {
453 - $data['verifylib'] = 1;
454 - }
455 + $alt_user = '';
455 456
456 - if ( null !== $sign_success && empty( $sign_success ) ) {
457 - $sign_error = '';
458 - while ( $msg = openssl_error_string() ) {
459 - if ( is_string( $msg ) ) {
460 - $sign_error .= $msg;
461 - }
462 - }
463 - $pk_info = ! empty( $website->privkey ) ? substr( $website->privkey, 0, 10 ) : '';
464 - 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 );
465 - }
466 -
467 - $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
468 -
469 - /** This filter is documented in ../widgets/widget-mainwp-recent-posts.php */
470 - $recent_number = apply_filters( 'mainwp_recent_posts_pages_number', 5 );
471 - if ( 5 !== $recent_number ) {
472 - $data['recent_number'] = $recent_number;
473 - }
474 -
475 - $scan_dir = apply_filters( 'mainwp_stats_scan_dir', false, $website );
476 - if ( ! empty( $scan_dir ) ) {
477 - $data['scan_dir'] = 1;
478 - }
479 -
480 457 /**
481 458 * Current user global.
482 459 *
483 460 * @global string
@@ -494,14 +471,125 @@
494 471 * @param int $current_user->ID User ID.
495 472 *
496 473 * @since Unknown
497 474 */
498 - $alter_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
499 - if ( ! empty( $alter_user ) ) {
500 - $data['alt_user'] = rawurlencode( $alter_user );
475 + $alt_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
476 +
477 + }
478 +
479 + $child_support_adv_sign = 1 === (int) MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' );
480 +
481 + $alg = false;
482 + $sign_value = $what . $data['nonce']; // Legacy signature data.
483 +
484 + if ( ! $verify_signature || ! $child_support_adv_sign ) {
485 + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
486 + $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.
487 + $use_seclib = true;
488 + } elseif ( function_exists( 'openssl_verify' ) ) {
489 + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
490 + $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.
491 + if ( false !== $alg ) {
492 + $data['sign_algo'] = $alg;
493 + }
501 494 }
495 +
496 + if ( null !== $sign_success && empty( $sign_success ) ) {
497 + $sign_error = '';
498 + while ( $msg = openssl_error_string() ) {
499 + if ( is_string( $msg ) ) {
500 + $sign_error .= $msg;
501 + }
502 + }
503 + $pk_info = ! empty( $website->privkey ) ? substr( $website->privkey, 0, 10 ) : '';
504 + 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 );
505 + }
506 + } else {
507 + $signature = 'useadvancedmainwpsignature';
502 508 }
503 509
510 + $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
511 +
512 + if ( $verify_signature ) {
513 + $ts = time();
514 + $data_sign_v2 = array(
515 + 'base_function' => $what,
516 + 'nonce' => $data['nonce'],
517 + 'expires' => $ts + 60,
518 + 'user' => $website->adminname,
519 + 'req_id' => wp_generate_uuid4(),
520 + );
521 +
522 + if ( ! empty( $alt_user ) ) {
523 + $data_sign_v2['alt_user'] = rawurlencode( $alt_user );
524 + }
525 +
526 + if ( 'process_premium_updates' === $what ) {
527 + $add_sign_params = array( 'premium_perform', 'premium_type', 'list' );
528 + foreach ( $add_sign_params as $_name ) {
529 + if ( isset( $params[ $_name ] ) ) {
530 + $data_sign_v2[ $_name ] = $params[ $_name ];
531 + }
532 + }
533 + }
534 +
535 + $sign_success_v2 = null;
536 +
537 + $sign_value_v2 = wp_json_encode( $data_sign_v2 );
538 +
539 + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
540 + $use_seclib = true;
541 + $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.
542 + } elseif ( function_exists( 'openssl_verify' ) ) {
543 + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
544 + $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.
545 + if ( false !== $alg ) {
546 + $data['sign_algo'] = $alg;
547 + }
548 + }
549 +
550 + $data['data_signature'] = $sign_value_v2;
551 + $data['mainwpsignature_adv'] = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
552 +
553 + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) {
554 + $sign_error = '';
555 + while ( $msg = openssl_error_string() ) {
556 + if ( is_string( $msg ) ) {
557 + $sign_error .= $msg;
558 + }
559 + }
560 + $pk_info = ! empty( $website->privkey ) ? substr( $website->privkey, 0, 10 ) : '';
561 + 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 );
562 + }
563 + }
564 +
565 + if ( $use_seclib ) {
566 + $data['verifylib'] = 1;
567 + }
568 +
569 + if ( ! empty( $alt_user ) ) {
570 + $data['alt_user'] = rawurlencode( $alt_user );
571 + }
572 +
573 + /** This filter is documented in ../widgets/widget-mainwp-recent-posts.php */
574 + $recent_number = apply_filters( 'mainwp_recent_posts_pages_number', 5 );
575 + if ( 5 !== $recent_number ) {
576 + $data['recent_number'] = $recent_number;
577 + }
578 +
579 + $scan_dir = apply_filters( 'mainwp_stats_scan_dir', false, $website );
580 + if ( ! empty( $scan_dir ) ) {
581 + $data['scan_dir'] = 1;
582 + }
583 +
584 + if ( 'process_premium_updates' === $what ) {
585 + if ( ! empty( $http_user_plain ) && ! empty( $http_pass_plain ) ) {
586 + // For post data.
587 + $data['wp_http_user'] = $http_user_plain;
588 + $data['wp_http_pass'] = $http_pass_plain;
589 + }
590 + $data['sslVerify'] = $website->verify_certificate ? 1 : 0;
591 + }
504 592 return http_build_query( $data, '', '&' );
505 593 }
506 594
507 595 return null;
@@ -513,20 +601,29 @@
513 601 * Get authorized $_POST data & build query for renew connection action only.
514 602 *
515 603 * @param mixed $website Array of Child Site Info.
516 604 * @param mixed $what What we are posting.
605 + * @param array $others Other data.
517 606 *
518 607 * @return mixed null|http_build_query()
519 608 */
520 - private static function get_renew_post_data_authed( &$website, $what ) { // phpcs:ignore -- NOSONAR - complex.
609 + private static function get_renew_post_data_authed( &$website, $what, $others = array() ) { // phpcs:ignore -- NOSONAR - complex.
521 610
611 + if ( ! is_array( $others ) ) {
612 + $others = array();
613 + }
614 +
615 + $verify_signature = ! empty( $others['verify_signature'] ) ? true : false;
616 +
522 617 if ( $website && '' !== $what ) {
523 618 $compat_what = 'disconnect'; // to compatible, renew will call disconnect.
524 619 $data = array();
525 620 $data['user'] = $website->adminname;
526 621 $data['function'] = $compat_what;
527 - $data['nonce'] = wp_rand( 0, 9999 );
622 + $data['nonce'] = bin2hex( random_bytes( 16 ) );
528 623
624 + $sign_value = $compat_what . $data['nonce']; // compatible format.
625 +
529 626 $alg = false;
530 627 $sign_success = null;
531 628 $use_seclib = false;
532 629
@@ -531,17 +628,17 @@
531 628 $use_seclib = false;
532 629
533 630 if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
534 631 // to disconnect.
535 - $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.
632 + $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.
536 633 $use_seclib = true;
537 634 } elseif ( function_exists( 'openssl_verify' ) ) {
538 635 $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
539 - $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.
636 + $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.
540 637 if ( empty( $sign_success ) ) { // error from openssl, openssl_sign().
541 638 $alg = defined( 'OPENSSL_ALGO_SHA1' ) ? OPENSSL_ALGO_SHA1 : false; // to set default SHA1, to disconnect.
542 639 MainWP_Logger::instance()->debug_for_website( $website, 'get_renew_post_data_authed', '[' . $website->url . '] :: [openssl_sign:failed] :: Set sign_algo=SHA1' );
543 - $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.
640 + $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.
544 641 }
545 642
546 643 if ( false !== $alg ) {
547 644 $data['sign_algo'] = $alg;
@@ -547,11 +644,9 @@
547 644 $data['sign_algo'] = $alg;
548 645 }
549 646 }
550 647
551 - if ( $use_seclib ) {
552 - $data['verifylib'] = 1;
553 - }
648 + $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
554 649
555 650 if ( null !== $sign_success && empty( $sign_success ) ) {
556 651 $sign_error = '';
557 652 while ( $msg = openssl_error_string() ) {
@@ -561,10 +656,55 @@
561 656 }
562 657 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 );
563 658 }
564 659
565 - $data['mainwpsignature'] = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
660 + if ( $verify_signature ) {
661 + $ts = time();
662 + $data_sign_v2 = array(
663 + 'base_function' => $compat_what,
664 + 'nonce' => $data['nonce'],
665 + 'expires' => $ts + 60,
666 + 'user' => $website->adminname,
667 + 'req_id' => wp_generate_uuid4(),
668 + );
669 + $sign_value_v2 = wp_json_encode( $data_sign_v2 );
566 670
671 + $sign_success_v2 = null;
672 +
673 + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
674 + // to disconnect.
675 + $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.
676 + } elseif ( function_exists( 'openssl_verify' ) ) {
677 + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
678 + $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.
679 + if ( empty( $sign_success_v2 ) ) { // error from openssl, openssl_sign().
680 + $alg = defined( 'OPENSSL_ALGO_SHA1' ) ? OPENSSL_ALGO_SHA1 : false; // to set default SHA1, to disconnect.
681 + MainWP_Logger::instance()->debug_for_website( $website, 'get_renew_post_data_authed', '[' . $website->url . '] :: [openssl_sign:failed] :: Set sign_algo=SHA1' );
682 + $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.
683 + }
684 + if ( false !== $alg ) {
685 + $data['sign_algo'] = $alg;
686 + }
687 + }
688 +
689 + $data['data_signature'] = $sign_value_v2;
690 + $data['mainwpsignature_adv'] = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
691 +
692 + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) {
693 + $sign_error = '';
694 + while ( $msg = openssl_error_string() ) {
695 + if ( is_string( $msg ) ) {
696 + $sign_error .= $msg;
697 + }
698 + }
699 + 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 );
700 + }
701 + }
702 +
703 + if ( $use_seclib ) {
704 + $data['verifylib'] = 1;
705 + }
706 +
567 707 return http_build_query( $data, '', '&' );
568 708 }
569 709 return null;
570 710 }
@@ -579,12 +719,16 @@
579 719 * @param mixed $paramValue OpenSSL parameter.
580 720 * @param string $paramName Parameter name.
581 721 * @param bool $asArray true|false Default is false.
582 722 * @param array $other_params other params.
723 + * @param string $custom_url Optional. Override the target base URL for browser-bound
724 + * requests ( see MainWP_Site_Url_Corrector::browser_target_url() ).
725 + * Default null keeps the stored URL � server-side callers
726 + * ( backups, premium updates ) must not pass this.
583 727 *
584 728 * @return string $url
585 729 */
586 - public static function get_get_data_authed( $website, $paramValue, $paramName = 'where', $asArray = false, $other_params = array() ) { //phpcs:ignore -- NOSONAR - complex method.
730 + public static function get_get_data_authed( $website, $paramValue, $paramName = 'where', $asArray = false, $other_params = array(), $custom_url = null ) { //phpcs:ignore -- NOSONAR - complex method.
587 731 $params = array();
588 732 if ( $website && '' !== $paramValue ) {
589 733
590 734 $sign_success = null;
@@ -589,27 +733,51 @@
589 733
590 734 $sign_success = null;
591 735 $alg = false;
592 736 $use_seclib = false;
593 - $nonce = wp_rand( 0, 9999 );
594 - if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
595 - $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.
596 - $use_seclib = true;
597 - } elseif ( function_exists( 'openssl_verify' ) ) {
598 - $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
599 - $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.
737 + $nonce = bin2hex( random_bytes( 16 ) );
738 +
739 + /**
740 + * Current user global.
741 + *
742 + * @global string
743 + */
744 + global $current_user;
745 +
746 + $alt_user = '';
747 + if ( ( ( ! defined( 'DOING_CRON' ) || false === DOING_CRON ) && ( ! defined( 'WP_CLI' ) || false === WP_CLI ) ) && $current_user && $current_user->ID ) {
748 + /** This filter is documented in ../class/class-mainwp-connect.php */
749 + $alt_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
600 750 }
601 751
602 - $signature = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
752 + $verify_signature = is_array( $other_params ) && ! empty( $other_params['verify_signature'] );
603 753
604 - if ( null !== $sign_success && empty( $sign_success ) ) {
605 - $sign_error = '';
606 - while ( $msg = openssl_error_string() ) {
607 - if ( is_string( $msg ) ) {
608 - $sign_error .= $msg;
754 + $child_support_adv_sign = 1 === (int) MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' );
755 +
756 + if ( ! $verify_signature || ! $child_support_adv_sign ) {
757 + $sign_value = $paramValue . $nonce; // compatible format.
758 +
759 + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
760 + $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.
761 + $use_seclib = true;
762 + } elseif ( function_exists( 'openssl_verify' ) ) {
763 + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
764 + $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.
765 + }
766 +
767 + $signature = ! empty( $signature ) ? base64_encode( $signature ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
768 +
769 + if ( null !== $sign_success && empty( $sign_success ) ) {
770 + $sign_error = '';
771 + while ( $msg = openssl_error_string() ) {
772 + if ( is_string( $msg ) ) {
773 + $sign_error .= $msg;
774 + }
609 775 }
776 + 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 );
610 777 }
611 - 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 );
778 + } else {
779 + $signature = base64_encode( 'useadvancedmainwpsignature' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
612 780 }
613 781
614 782 $params = array(
615 783 'login_required' => 1,
@@ -618,8 +786,62 @@
618 786 'nonce' => $nonce,
619 787 $paramName => rawurlencode( $paramValue ),
620 788 );
621 789
790 + if ( false !== $alg ) {
791 + $params['sign_algo'] = $alg;
792 + }
793 +
794 + if ( $verify_signature ) {
795 + $ts = time();
796 + $data_sign_v2 = array(
797 + 'base_function' => $paramValue,
798 + 'where' => rawurlencode( $paramName ),
799 + 'nonce' => $nonce,
800 + 'expires' => $ts + 60,
801 + 'user' => $website->adminname,
802 + 'req_id' => wp_generate_uuid4(),
803 + );
804 +
805 + if ( ! empty( $alt_user ) ) {
806 + $data_sign_v2['alt_user'] = rawurlencode( $alt_user );
807 + }
808 +
809 + $sign_success_v2 = null;
810 +
811 + $sign_value_v2 = wp_json_encode( $data_sign_v2 );
812 +
813 + if ( MainWP_Connect_Lib::is_use_fallback_sec_lib( $website ) ) {
814 + $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.
815 + $use_seclib = true;
816 + } elseif ( function_exists( 'openssl_verify' ) ) {
817 + $alg = MainWP_System_Utility::get_connect_sign_algorithm( $website );
818 + $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.
819 + if ( false !== $alg ) {
820 + $params['sign_algo'] = $alg;
821 + }
822 + }
823 +
824 + $signature_v2 = ! empty( $signature_v2 ) ? base64_encode( $signature_v2 ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
825 +
826 + $params['data_signature'] = rawurlencode( $sign_value_v2 );
827 + $params['mainwpsignature_adv'] = rawurlencode( $signature_v2 );
828 +
829 + if ( null !== $sign_success_v2 && empty( $sign_success_v2 ) ) {
830 + $sign_error = '';
831 + while ( $msg = openssl_error_string() ) {
832 + if ( is_string( $msg ) ) {
833 + $sign_error .= $msg;
834 + }
835 + }
836 + 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 );
837 + }
838 + }
839 +
840 + if ( ! empty( $alt_user ) ) {
841 + $params['alt_user'] = rawurlencode( $alt_user );
842 + }
843 +
622 844 if ( is_array( $other_params ) ) {
623 845 foreach ( $other_params as $name => $value ) {
624 846 if ( is_string( $name ) && ! empty( $name ) && is_scalar( $value ) ) {
625 847 $params[ sanitize_text_field( wp_unslash( $name ) ) ] = rawurlencode( sanitize_text_field( wp_unslash( $value ) ) );
@@ -639,30 +861,11 @@
639 861 }
640 862 }
641 863 }
642 864
643 - if ( false !== $alg ) {
644 - $params['sign_algo'] = $alg;
645 - }
646 -
647 865 if ( ! empty( $use_seclib ) ) {
648 866 $params['verifylib'] = 1;
649 867 }
650 -
651 - /**
652 - * Current user global.
653 - *
654 - * @global string
655 - */
656 - global $current_user;
657 -
658 - if ( ( ( ! defined( 'DOING_CRON' ) || false === DOING_CRON ) && ( ! defined( 'WP_CLI' ) || false === WP_CLI ) ) && $current_user && $current_user->ID ) {
659 - /** This filter is documented in ../class/class-mainwp-connect.php */
660 - $alter_user = apply_filters( 'mainwp_alter_login_user', false, $website->id, $current_user->ID );
661 - if ( ! empty( $alter_user ) ) {
662 - $params['alt_user'] = rawurlencode( $alter_user );
663 - }
664 - }
665 868 }
666 869
667 870 if ( $asArray ) {
668 871 return $params;
@@ -667,9 +870,13 @@
667 870 if ( $asArray ) {
668 871 return $params;
669 872 }
670 873
671 - $url = ( isset( $website->url ) && '' !== $website->url ? $website->url : $website->siteurl );
874 + if ( null !== $custom_url && '' !== $custom_url ) {
875 + $url = $custom_url;
876 + } else {
877 + $url = ( isset( $website->url ) && '' !== $website->url ? $website->url : $website->siteurl );
878 + }
672 879 $url .= ( substr( $url, - 1 ) !== '/' ? '/' : '' );
673 880 $url .= '?';
674 881
675 882 foreach ( $params as $key => $value ) {
@@ -737,8 +944,27 @@
737 944 return null;
738 945 }
739 946
740 947 /**
948 + * Format a privacy-safe diagnostic for an unexpected Child response.
949 + *
950 + * @param mixed $website Website information.
951 + * @param mixed $data Raw response data.
952 + *
953 + * @return string Redacted diagnostic message.
954 + */
955 + private static function format_unexpected_response_log( $website, $data ) {
956 + $site_id = 0;
957 + if ( is_object( $website ) && property_exists( $website, 'id' ) && 0 < (int) $website->id ) {
958 + $site_id = (int) $website->id;
959 + }
960 +
961 + $response_bytes = is_string( $data ) ? strlen( $data ) : 0;
962 +
963 + return 'curl_multi_getcontent :: unexpected response :: [siteid=' . $site_id . '] :: [response_bytes=' . $response_bytes . ']';
964 + }
965 +
966 + /**
741 967 * Method fetch_urls_authed()
742 968 *
743 969 * Fetches data from child sites if authenticated.
744 970 *
@@ -783,9 +1009,26 @@
783 1009
784 1010 $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
785 1011 $mh = curl_multi_init();
786 1012
787 - $timeout = 20 * 60 * 60;
1013 + /**
1014 + * Filter: mainwp_fetch_url_site_timeout
1015 + *
1016 + * Filters the request timeout ( CURLOPT_TIMEOUT + PHP time limit ) used
1017 + * for child site requests. Defaults to 20 hours to accommodate the
1018 + * longest operations ( upgrades, backups ); short-lived callers such as
1019 + * the URL-correction verify probe bound it much tighter.
1020 + *
1021 + * @param int $timeout Timeout in seconds. Default 72000 ( 20 hours ).
1022 + * Values below 1 are ignored ( 0 would disable the
1023 + * cURL timeout entirely ) and fall back to the default.
1024 + *
1025 + * @since 6.2
1026 + */
1027 + $timeout = (int) apply_filters( 'mainwp_fetch_url_site_timeout', 20 * 60 * 60 );
1028 + if ( $timeout <= 0 ) {
1029 + $timeout = 20 * 60 * 60;
1030 + }
788 1031
789 1032 $disabled_functions = ini_get( 'disable_functions' );
790 1033 $handleToWebsite = array();
791 1034 $requestUrls = array();
@@ -1012,18 +1255,10 @@
1012 1255 }
1013 1256 }
1014 1257
1015 1258 if ( ! $contains ) {
1016 - // Add useful debug log for unexpected response.
1017 - $log_data = (string) $data;
1018 - if ( is_string( $log_data ) && strlen( $log_data ) > 2000 ) {
1019 - $log_data = substr( $log_data, 0, 2000 ) . '...[truncated]';
1020 - }
1021 - $log_url = isset( $requestUrls[ $rid ] ) ? $requestUrls[ $rid ] : '';
1022 - if ( empty( $log_url ) ) {
1023 - $log_url = ( isset( $handleToWebsite[ $rid ] ) && is_object( $handleToWebsite[ $rid ] ) && property_exists( $handleToWebsite[ $rid ], 'url' ) ) ? $handleToWebsite[ $rid ]->url : 'Unknown';
1024 - }
1025 - MainWP_Logger::instance()->debug( 'curl_multi_getcontent :: unexpected response :: [data=' . $log_data . '] :: [url=' . $log_url . ']' );
1259 + $log_website = isset( $handleToWebsite[ $rid ] ) ? $handleToWebsite[ $rid ] : null;
1260 + MainWP_Logger::instance()->debug( static::format_unexpected_response_log( $log_website, $data ) );
1026 1261 }
1027 1262
1028 1263 if ( null !== $handler ) {
1029 1264 $site = &$handleToWebsite[ static::get_resource_id( $info['handle'] ) ];
@@ -1199,8 +1434,9 @@
1199 1434 * @param bool $checkConstraints Whether or not to check constraints.
1200 1435 * @param bool $pForceFetch Whether or not to force the fetch.
1201 1436 * @param bool $pRetryFailed Whether or not to retry the fetch process.
1202 1437 * @param null $rawResponse Raw response.
1438 + * @param bool $verify_signature Wether verify signature data.
1203 1439 *
1204 1440 * @return mixed $information
1205 1441 *
1206 1442 * @uses \MainWP\Dashboard\MainWP_Monitoring_Handler::handle_check_website()
@@ -1213,9 +1449,10 @@
1213 1449 $params = null,
1214 1450 $checkConstraints = false,
1215 1451 $pForceFetch = false,
1216 1452 $pRetryFailed = true,
1217 - $rawResponse = null
1453 + $rawResponse = null,
1454 + $verify_signature = false
1218 1455 ) {
1219 1456 unset( $pForceFetch );
1220 1457
1221 1458 // to support demo data.
@@ -1250,9 +1487,9 @@
1250 1487 * @return mixed Array to return early, false to proceed normally.
1251 1488 */
1252 1489 $is_phpunit_env = defined( 'WP_TESTS_DOMAIN' ) || defined( 'PHPUNIT_COMPOSER_INSTALL' ) || ( defined( 'WP_TESTS_DIR' ) && WP_TESTS_DIR );
1253 1490 if ( defined( 'MAINWP_TESTING_MODE' ) && MAINWP_TESTING_MODE && $is_phpunit_env ) {
1254 - $pre_result = apply_filters( 'mainwp_fetch_url_authed_pre', false, $website, $what, $params );
1491 + $pre_result = apply_filters( 'mainwp_fetch_url_authed_pre', false, $website, $what, $params, $verify_signature );
1255 1492 if ( false !== $pre_result ) {
1256 1493 return $pre_result;
1257 1494 }
1258 1495 }
@@ -1265,10 +1502,8 @@
1265 1502 'force_use_ipv4' => $website->force_use_ipv4,
1266 1503 'upgrade' => ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ),
1267 1504 );
1268 1505
1269 - $request_update = MainWP_Premium_Update::maybe_request_premium_updates( $website, $what, $params );
1270 -
1271 1506 if ( isset( $rawResponse ) && $rawResponse ) {
1272 1507 $others['raw_response'] = 'yes';
1273 1508 }
1274 1509
@@ -1276,11 +1511,17 @@
1276 1511
1277 1512 $updating_website = false;
1278 1513 $type = '';
1279 1514 $list = '';
1280 - if ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what ) {
1515 +
1516 + $premium_update = 'process_premium_updates' === $what && ! empty( $params['premium_perform'] ) && 'premium_update' === $params['premium_perform'] ? true : false;
1517 +
1518 + if ( 'upgradeplugintheme' === $what || 'upgrade' === $what || 'upgradetranslation' === $what || $premium_update ) {
1281 1519 $updating_website = true;
1282 - if ( 'upgradeplugintheme' === $what || 'upgradetranslation' === $what ) {
1520 + if ( $premium_update ) {
1521 + $type = $params['premium_type'];
1522 + $list = $params['list'];
1523 + } elseif ( 'upgradeplugintheme' === $what || 'upgradetranslation' === $what ) {
1283 1524 $type = $params['type'];
1284 1525 $list = $params['list'];
1285 1526 } else {
1286 1527 $type = 'wp';
@@ -1302,52 +1543,80 @@
1302 1543 */
1303 1544 do_action( 'mainwp_website_before_updated', $website, $type, $list );
1304 1545 }
1305 1546
1547 + $information = array();
1548 + $output = array();
1549 +
1550 + if ( 'stats' === $what || ( 'upgradeplugintheme' === $what && isset( $params['type'] ) ) ) {
1551 + $request_update = MainWP_Premium_Update::maybe_request_premium_updates( $website, $what, $params, $output_result );
1552 + if ( $request_update ) {
1553 + // Return the information here.
1554 + return $output_result;
1555 + }
1556 + }
1557 +
1558 + // MWP-1548: decrypt http_user / http_pass before they hit the
1559 + // outbound HTTP Basic Auth header. Legacy plaintext rows pass
1560 + // through unchanged.
1561 + $http_user_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_user );
1562 + $http_pass_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_pass );
1563 +
1306 1564 if ( 'renew' === $what ) {
1307 - $postdata = static::get_renew_post_data_authed( $website, $what );
1565 + $postdata = static::get_renew_post_data_authed(
1566 + $website,
1567 + $what,
1568 + array(
1569 + 'verify_signature' => $verify_signature,
1570 + )
1571 + );
1308 1572 } else {
1309 - $postdata = static::get_post_data_authed( $website, $what, $params );
1573 + $postdata = static::get_post_data_authed(
1574 + $website,
1575 + $what,
1576 + $params,
1577 + array(
1578 + 'verify_signature' => $verify_signature,
1579 + 'http_user_plain' => $http_user_plain,
1580 + 'http_pass_plain' => $http_pass_plain,
1581 + )
1582 + );
1310 1583
1311 1584 }
1585 +
1312 1586 $others['function'] = $what;
1313 1587
1314 - $information = array();
1315 - $output = array();
1588 + $information = static::fetch_url( $website, $website->url, $postdata, $checkConstraints, $website->verify_certificate, $pRetryFailed, $http_user_plain, $http_pass_plain, $website->ssl_version, $others, $output );
1316 1589
1317 - if ( ! $request_update ) {
1318 - // MWP-1548: decrypt http_user / http_pass before they hit the
1319 - // outbound HTTP Basic Auth header. Legacy plaintext rows pass
1320 - // through unchanged.
1321 - $http_user_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_user );
1322 - $http_pass_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_pass );
1323 - $information = static::fetch_url( $website, $website->url, $postdata, $checkConstraints, $website->verify_certificate, $pRetryFailed, $http_user_plain, $http_pass_plain, $website->ssl_version, $others, $output );
1324 - if ( ! empty( $output ) ) {
1325 - if ( ! is_array( $information ) ) {
1326 - $information = array();
1327 - }
1328 - $information['fetch_url_output'] = $output;
1590 + if ( ! empty( $output ) ) {
1591 + if ( ! is_array( $information ) ) {
1592 + $information = array();
1329 1593 }
1330 - /**
1331 - * Fires immediately after fetch url action.
1332 - *
1333 - * @param object $website website.
1334 - * @param array $information information result data.
1335 - * @param string $what action.
1336 - * @param array $params params input array.
1337 - * @param array $others others input array.
1338 - *
1339 - * @since 4.5.1.1
1340 - */
1341 - do_action( 'mainwp_fetch_url_authed', $website, $information, $what, $params, $others );
1342 - } else {
1343 - $slug = $params['list'];
1344 - $information['upgrades'] = array( $slug => 1 );
1594 + $information['fetch_url_output'] = $output;
1345 1595 }
1596 + /**
1597 + * Fires immediately after fetch url action.
1598 + *
1599 + * @param object $website website.
1600 + * @param array $information information result data.
1601 + * @param string $what action.
1602 + * @param array $params params input array.
1603 + * @param array $others others input array.
1604 + *
1605 + * @since 4.5.1.1
1606 + */
1607 + do_action( 'mainwp_fetch_url_authed', $website, $information, $what, $params, $others );
1346 1608
1347 - if ( is_array( $information ) && isset( $information['sync'] ) && ! empty( $information['sync'] ) ) {
1348 - MainWP_Sync::sync_information_array( $website, $information['sync'] );
1349 - unset( $information['sync'] );
1609 + if ( is_array( $information ) ) {
1610 + if ( isset( $information['sync'] ) && ! empty( $information['sync'] ) ) {
1611 + MainWP_Sync::sync_information_array( $website, $information['sync'] );
1612 + unset( $information['sync'] );
1613 + }
1614 +
1615 + if ( isset( $information['sync_partial'] ) && is_array( $information['sync_partial'] ) && ! empty( $information['sync_partial'] ) ) {
1616 + MainWP_Sync::sync_partial_information( $website, $information['sync_partial'] );
1617 + unset( $information['sync_partial'] );
1618 + }
1350 1619 }
1351 1620
1352 1621 if ( $updating_website ) {
1353 1622 /**
@@ -1402,8 +1671,72 @@
1402 1671 $sslVersion = 0,
1403 1672 $others = array(),
1404 1673 &$output = array()
1405 1674 ) {
1675 +
1676 + /**
1677 + * Filter to mock the fetch_url_not_authed response before any HTTP request occurs.
1678 + *
1679 + * This filter fires early, before the HTTP request is performed, allowing
1680 + * tests to bypass child site communication entirely.
1681 + *
1682 + * SECURITY WARNING - TEST ONLY:
1683 + * This filter ONLY fires when ALL of the following conditions are met:
1684 + * 1. MAINWP_TESTING_MODE constant is defined and true.
1685 + * 2. A PHPUnit test harness constant is present (WP_TESTS_DOMAIN,
1686 + * PHPUNIT_COMPOSER_INSTALL, or WP_TESTS_DIR).
1687 + *
1688 + * These checks prevent malicious code from defining MAINWP_TESTING_MODE
1689 + * in production to spoof child site responses.
1690 + *
1691 + * IMPORTANT: MAINWP_TESTING_MODE must ONLY be defined in the PHPUnit bootstrap
1692 + * file (tests/bootstrap.php). Defining it in production code, wp-config.php,
1693 + * or plugin files would create a security vulnerability allowing response
1694 + * spoofing.
1695 + *
1696 + * @since 6.2.1
1697 + *
1698 + * @param mixed $pre_result Return non-false to short-circuit the request and return this value.
1699 + * @param string $url URL to fetch.
1700 + * @param string $admin Admin username or identifier.
1701 + * @param string $what Action being performed.
1702 + * @param array|null $params Request parameters.
1703 + * @param bool $pForceFetch Whether to force the fetch.
1704 + * @param bool|null $verifyCertificate Whether to verify the SSL certificate.
1705 + * @param string|null $http_user HTTP authentication username.
1706 + * @param string|null $http_pass HTTP authentication password.
1707 + * @param int $sslVersion SSL version.
1708 + * @param array $others Additional request options.
1709 + * @param array $output Output data passed by reference.
1710 + *
1711 + * @return mixed Array or other value to return early, false to proceed normally.
1712 + */
1713 + $is_phpunit_env = defined( 'WP_TESTS_DOMAIN' ) || defined( 'PHPUNIT_COMPOSER_INSTALL' ) || ( defined( 'WP_TESTS_DIR' ) && WP_TESTS_DIR );
1714 +
1715 + if ( defined( 'MAINWP_TESTING_MODE' ) && MAINWP_TESTING_MODE && $is_phpunit_env ) {
1716 + $pre_result = apply_filters_ref_array(
1717 + 'mainwp_fetch_url_not_authed_pre',
1718 + array(
1719 + false,
1720 + $url,
1721 + $admin,
1722 + $what,
1723 + $params,
1724 + $pForceFetch,
1725 + $verifyCertificate,
1726 + $http_user,
1727 + $http_pass,
1728 + $sslVersion,
1729 + $others,
1730 + &$output,
1731 + )
1732 + );
1733 +
1734 + if ( false !== $pre_result ) {
1735 + return $pre_result;
1736 + }
1737 + }
1738 +
1406 1739 unset( $pForceFetch );
1407 1740
1408 1741 if ( empty( $params ) ) {
1409 1742 $params = array();
@@ -1695,9 +2028,25 @@
1695 2028 if ( $force_use_ipv4 && defined( 'CURLOPT_IPRESOLVE' ) && defined( 'CURL_IPRESOLVE_V4' ) ) {
1696 2029 curl_setopt( $ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
1697 2030 }
1698 2031
1699 - $timeout = 20 * 60 * 60;
2032 + $what = '';
2033 + if ( is_array( $others ) && isset( $others['function'] ) ) {
2034 + $what = $others['function'];
2035 + }
2036 +
2037 + if ( 'deactivate' === $what ) {
2038 + $timeout = 120; // 2 minutes.
2039 + } else {
2040 + $timeout = 20 * 60 * 60;
2041 + }
2042 +
2043 + /** This filter is documented in class/class-mainwp-connect.php */
2044 + $timeout = (int) apply_filters( 'mainwp_fetch_url_site_timeout', $timeout, $what );
2045 + if ( $timeout <= 0 ) {
2046 + $timeout = 20 * 60 * 60; // values below 1 would disable the cURL timeout entirely.
2047 + }
2048 +
1700 2049 curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
1701 2050 MainWP_System_Utility::set_time_limit( $timeout );
1702 2051
1703 2052 MainWP_Utility::end_session();
@@ -1783,16 +2132,17 @@
1783 2132 MainWP_Logger::instance()->log_execution_time( 'fetch_url_site :: [url=' . $url . ']' );
1784 2133
1785 2134 $thr_error = null;
1786 2135
1787 - if ( isset( $others['function'] ) ) {
1788 - $what = $others['function'];
1789 - if ( in_array( $what, array( 'installplugintheme', 'upgradeplugintheme', 'upgradetranslation', 'upgrade', 'stats', 'renew', 'reconnect' ), true ) ) {
1790 - MainWP_Cache_Helper::invalidate_cache_group( MainWP_Cache_Helper::CGR_UPDATES );
1791 - MainWP_Cache_Warm_Helper::invalidate_pages_by_site_actions( $what );
1792 - }
2136 + if ( in_array( $what, array( 'installplugintheme', 'upgradeplugintheme', 'upgradetranslation', 'process_premium_updates', 'upgrade', 'stats', 'renew', 'reconnect' ), true ) ) {
2137 + MainWP_Cache_Helper::invalidate_cache_group( MainWP_Cache_Helper::CGR_UPDATES );
2138 + MainWP_Cache_Warm_Helper::invalidate_pages_by_site_actions( $what );
1793 2139 }
1794 2140
2141 + if ( 'process_premium_updates' === $what ) {
2142 + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'function: [process_premium_updates] response data: [' . MainWP_Utility::value_to_string( $data ) . ']' );
2143 + }
2144 +
1795 2145 if ( ( false === $data ) && empty( $http_status ) ) {
1796 2146 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url', '[' . $url . '] HTTP Error: [status=0][' . $err . ']' );
1797 2147 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1798 2148 $output['error_category'] = 'http_error';
@@ -1814,8 +2164,27 @@
1814 2164 $error_code = is_array( $information ) && isset( $information['error_code'] ) ? sanitize_text_field( wp_unslash( $information['error_code'] ) ) : '';
1815 2165 if ( ! empty( $error_code ) ) {
1816 2166 $output['child_error_code'] = $error_code;
1817 2167 }
2168 +
2169 + if ( is_array( $information ) && $website ) {
2170 + // Process to ensure compatibility with old and new Child versions.
2171 + $new_sync = isset( $information['support_advanced_sign'] );
2172 + $old_sync = ! $new_sync && ( isset( $information['mainwpdir'], $information['uniqueId'] ) || isset( $information['version'], $information['wpversion'], $information['wpe'] ) );
2173 + if ( $new_sync || $old_sync ) {
2174 + $adv_sign_support = MainWP_DB::instance()->get_website_option( $website, 'support_advanced_sign' );
2175 + if ( ! empty( $information['support_advanced_sign'] ) && 0 === (int) $adv_sign_support ) {
2176 + MainWP_DB::instance()->update_website_option( $website, 'support_advanced_sign', 1 );
2177 + } elseif ( empty( $information['support_advanced_sign'] ) && 1 === (int) $adv_sign_support ) {
2178 + MainWP_DB::instance()->update_website_option( $website, 'support_advanced_sign', 0 );
2179 + }
2180 + }
2181 + }
2182 +
2183 + if ( 'process_premium_updates' === $what ) {
2184 + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'function: [process_premium_updates] decoded data: [' . MainWP_Utility::value_to_string( $information ) . ']' );
2185 + }
2186 +
1818 2187 return $information;
1819 2188 } elseif ( 200 === (int) $http_status && ! empty( $err ) ) {
1820 2189 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1821 2190 $output['error_category'] = 'http_error';
@@ -1820,8 +2189,9 @@
1820 2189 $thr_error = new MainWP_Exception( 'HTTPERROR', $err ); //phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
1821 2190 $output['error_category'] = 'http_error';
1822 2191 $output['error_code'] = 'http_request_failed';
1823 2192 $output['error_message'] = $err;
2193 + MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', '[' . $url . '] error [' . $err . ']' ); //phpcs:ignore -- ok.
1824 2194 } elseif ( $raw_response ) {
1825 2195 MainWP_Logger::instance()->debug_for_website( $website, 'fetch_url_site', 'Response: [RAW]' );
1826 2196 return $data;
1827 2197 } else {