PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.2
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.2
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-premium-update.php +453 -294 5.0 → 6.2.2 View file →
@@ -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 // phpcs:disable WordPress.DB.RestrictedFunctions, WordPress.WP.AlternativeFunctions, WordPress.PHP.NoSilencedErrors -- Using cURL functions.
13 18
14 19 /**
15 20 * Class MainWP_Premium_Update
@@ -17,336 +22,490 @@
17 22 * @package MainWP\Dashboard
18 23 *
19 24 * Check for premium plugin updates.
20 25 */
21 -class MainWP_Premium_Update {
26 +class MainWP_Premium_Update { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
22 27
23 - /**
24 - * Method get_class_name()
25 - *
26 - * Get Class Name.
27 - *
28 - * @return object
29 - */
30 - public static function get_class_name() {
31 - return __CLASS__;
32 - }
28 + /**
29 + * Raw response of the most recent premium request-update GET, if any.
30 + *
31 + * Captured so fetch_url_authed() can report the child's real result instead
32 + * of fabricating success (MWP-1660). Reset per maybe_request_premium_updates() call.
33 + *
34 + * @var array|\WP_Error|null
35 + */
36 + private static $last_request_response = null;
33 37
34 - /**
35 - * Method check_premium_updates()
36 - *
37 - * Check for Premium Plugin updates.
38 - *
39 - * @param array $updates Array of updates.
40 - * @param mixed $type Type of update.
41 - *
42 - * @return boolean true|false.
43 - */
44 - public static function check_premium_updates( $updates, $type ) {
38 + /**
39 + * Method get_class_name()
40 + *
41 + * Get Class Name.
42 + *
43 + * @return object
44 + */
45 + public static function get_class_name() {
46 + return __CLASS__;
47 + }
45 48
46 - if ( ! is_array( $updates ) || empty( $updates ) ) {
47 - return false;
48 - }
49 + /**
50 + * Method check_premium_updates()
51 + *
52 + * Check for Premium Plugin updates.
53 + *
54 + * @param array $updates Array of installed items reported by the child site.
55 + * @param mixed $type Type of update.
56 + *
57 + * @return boolean true|false.
58 + */
59 + public static function check_premium_updates( $updates, $type ) { // phpcs:ignore -- NOSONAR - complex.
49 60
50 - if ( 'plugin' === $type ) {
61 + if ( ! is_array( $updates ) || empty( $updates ) ) {
62 + return false;
63 + }
51 64
52 - $premiums = array(
53 - 'ithemes-security-pro/ithemes-security-pro.php',
54 - 'monarch/monarch.php',
55 - 'cornerstone/cornerstone.php',
56 - 'updraftplus/updraftplus.php',
57 - 'wp-all-import-pro/wp-all-import-pro.php',
58 - 'bbq-pro/bbq-pro.php',
59 - 'seedprod-coming-soon-pro-5/seedprod-coming-soon-pro-5.php',
60 - 'oxygen/functions.php',
61 - 'elementor-pro/elementor-pro.php',
62 - 'bbpowerpack/bb-powerpack.php',
63 - 'bb-ultimate-addon/bb-ultimate-addon.php',
64 - 'webarx/webarx.php',
65 - 'leco-client-portal/leco-client-portal.php',
66 - 'elementor-extras/elementor-extras.php',
67 - 'wp-schema-pro/wp-schema-pro.php',
68 - 'convertpro/convertpro.php',
69 - 'astra-addon/astra-addon.php',
70 - 'astra-portfolio/astra-portfolio.php',
71 - 'astra-pro-sites/astra-pro-sites.php',
72 - 'custom-facebook-feed-pro/custom-facebook-feed.php',
73 - 'convertpro/convertpro.php',
74 - 'convertpro-addon/convertpro-addon.php',
75 - 'wp-schema-pro/wp-schema-pro.php',
76 - 'ultimate-elementor/ultimate-elementor.php',
77 - 'gp-premium/gp-premium.php',
78 - 'flying-press/flying-press.php',
79 - 'wp-rocket/wp-rocket.php',
80 - 'fluentformpro/fluentformpro.php',
81 - 'fluentform-signature/fluentform-signature.php',
82 - 'fluentcampaign-pro/fluentcampaign-pro.php',
83 - 'fluent-support-pro/fluent-support-pro.php',
84 - 'ninja-tables-pro/ninja-tables-pro.php',
85 - 'fluent-booking-pro/fluent-booking-pro.php',
86 - 'wp-social-ninja-pro/wp-social-ninja-pro.php',
87 - 'wp-payment-form-pro/wp-payment-form-pro.php',
88 - );
65 + if ( 'plugin' === $type ) {
89 66
90 - /**
91 - * Filter: mainwp_detect_premiums_updates
92 - *
93 - * Use mainwp_detect_premium_plugins_update instead.
94 - *
95 - * @deprecated
96 - */
97 - $premiums = apply_filters( 'mainwp_detect_premiums_updates', $premiums );
67 + $premiums = MainWP_Premium_Update_Registry::get_filter_defaults( 'plugin', 'detect' );
98 68
99 - /**
100 - * Filter: mainwp_detect_premium_plugins_update
101 - *
102 - * Filters supported premium plugins to fix compatiblity issues with detecting premium plugins updates.
103 - *
104 - * @since Unknown
105 - */
106 - $premiums = apply_filters( 'mainwp_detect_premium_plugins_update', $premiums );
69 + /**
70 + * Filter: mainwp_detect_premiums_updates
71 + *
72 + * Use mainwp_detect_premium_plugins_update instead.
73 + *
74 + * @deprecated
75 + */
76 + $premiums = apply_filters( 'mainwp_detect_premiums_updates', $premiums );
107 77
108 - if ( is_array( $premiums ) && 0 < count( $premiums ) ) {
109 - foreach ( $updates as $info ) {
110 - if ( isset( $info['slug'] ) ) {
111 - if ( in_array( $info['slug'], $premiums ) ) {
112 - return true;
113 - } elseif ( false !== strpos( $info['slug'], 'yith-' ) ) {
114 - return true;
115 - }
116 - }
117 - }
118 - }
119 - } elseif ( 'theme' === $type ) {
78 + /**
79 + * Filter: mainwp_detect_premium_plugins_update
80 + *
81 + * Filters supported premium plugins to fix compatiblity issues with detecting premium plugin updates.
82 + *
83 + * @since Unknown
84 + */
85 + $premiums = apply_filters( 'mainwp_detect_premium_plugins_update', $premiums );
120 86
121 - $premiums = array();
87 + $prefixes = MainWP_Premium_Update_Registry::get_prefixes( 'plugin', 'detect' );
122 88
123 - /**
124 - * Filter: mainwp_detect_premium_themes_update
125 - *
126 - * Filters supported premium themes to fix compatiblity issues with detecting premium themes updates.
127 - *
128 - * @since Unknown
129 - */
130 - $premiums = apply_filters( 'mainwp_detect_premium_themes_update', $premiums );
89 + if ( ( is_array( $premiums ) && ! empty( $premiums ) ) || ! empty( $prefixes ) ) {
90 + foreach ( $updates as $inventory_key => $info ) {
91 + $identifier = MainWP_Premium_Update_Registry::get_inventory_identifier( $inventory_key, $info );
92 + if ( '' !== $identifier && MainWP_Premium_Update_Registry::slug_matches( $identifier, is_array( $premiums ) ? $premiums : array(), $prefixes ) ) {
93 + return true;
94 + }
95 + }
96 + }
97 + } elseif ( 'theme' === $type ) {
131 98
132 - if ( is_array( $premiums ) && 0 < count( $premiums ) ) {
133 - foreach ( $updates as $info ) {
134 - if ( isset( $info['slug'] ) ) {
135 - if ( in_array( $info['slug'], $premiums ) ) {
136 - return true;
137 - }
138 - }
139 - }
140 - }
141 - }
99 + $premiums = MainWP_Premium_Update_Registry::get_filter_defaults( 'theme', 'detect' );
142 100
143 - return false;
144 - }
101 + /**
102 + * Filter: mainwp_detect_premium_themes_update
103 + *
104 + * Filters supported premium themes to fix compatiblity issues with detecting premium theme updates.
105 + *
106 + * @since Unknown
107 + */
108 + $premiums = apply_filters( 'mainwp_detect_premium_themes_update', $premiums );
145 109
146 - /**
147 - * Method maybe_request_premium_updates()
148 - *
149 - * @param mixed $website Child Site info.
150 - * @param mixed $what stats|upgradeplugintheme What function to perform.
151 - * @param mixed $params plugin|theme Update Type.
152 - *
153 - * @return mixed $request_update
154 - */
155 - public static function maybe_request_premium_updates( $website, $what, $params ) { // phpcs:ignore -- Current complexity is the only way to achieve desired results, pull request solutions appreciated.
156 - $request_update = false;
157 - if ( 'stats' === $what || ( 'upgradeplugintheme' === $what && isset( $params['type'] ) ) ) {
110 + $prefixes = MainWP_Premium_Update_Registry::get_prefixes( 'theme', 'detect' );
158 111
159 - $update_type = '';
112 + if ( ( is_array( $premiums ) && ! empty( $premiums ) ) || ! empty( $prefixes ) ) {
113 + foreach ( $updates as $inventory_key => $info ) {
114 + $identifier = MainWP_Premium_Update_Registry::get_inventory_identifier( $inventory_key, $info );
115 + if ( '' === $identifier ) {
116 + continue;
117 + }
118 + // A theme updater only runs while its theme (or a child of it) is active,
119 + // so the extra checks are limited to the active theme and its parent.
120 + // Older child plugins that do not report activity flags keep the
121 + // previous match-any-installed behavior.
122 + if ( ( isset( $info['active'] ) || isset( $info['parent_active'] ) )
123 + && empty( $info['active'] ) && empty( $info['parent_active'] ) ) {
124 + continue;
125 + }
126 + if ( MainWP_Premium_Update_Registry::slug_matches( $identifier, is_array( $premiums ) ? $premiums : array(), $prefixes ) ) {
127 + return true;
128 + }
129 + }
130 + }
131 + }
160 132
161 - $check_premi_plugins = array();
162 - $check_premi_themes = array();
133 + return false;
134 + }
163 135
164 - if ( 'stats' === $what ) {
165 - if ( '' !== $website->plugins ) {
166 - $check_premi_plugins = json_decode( $website->plugins, 1 );
167 - }
168 - if ( '' !== $website->themes ) {
169 - $check_premi_themes = json_decode( $website->themes, 1 );
170 - }
171 - } elseif ( 'upgradeplugintheme' === $what ) {
172 - $update_type = ( isset( $params['type'] ) ) ? $params['type'] : '';
173 - if ( 'plugin' === $update_type ) {
174 - if ( '' !== $website->plugins ) {
175 - $check_premi_plugins = json_decode( $website->plugins, 1 );
176 - }
177 - } elseif ( 'theme' === $update_type ) {
178 - if ( '' !== $website->themes ) {
179 - $check_premi_themes = json_decode( $website->themes, 1 );
180 - }
181 - }
182 - }
136 + /**
137 + * Method maybe_request_premium_updates()
138 + *
139 + * @param mixed $website Child Site info.
140 + * @param mixed $what stats|upgradeplugintheme What function to perform.
141 + * @param mixed $params plugin|theme Update Type.
142 + * @param mixed $output Output result.
143 + *
144 + * @return mixed $request_update
145 + */
146 + public static function maybe_request_premium_updates( $website, $what, $params, &$output ) { // phpcs:ignore -- NOSONAR -Current complexity is the only way to achieve desired results, pull request solutions appreciated.
147 + self::$last_request_response = null;
183 148
184 - if ( self::check_premium_updates( $check_premi_plugins, 'plugin' ) ) {
185 - self::try_to_detect_premiums_update( $website, 'plugin' );
186 - }
149 + if ( 'stats' === $what || ( 'upgradeplugintheme' === $what && isset( $params['type'] ) ) ) {
187 150
188 - if ( self::check_premium_updates( $check_premi_themes, 'theme' ) ) {
189 - self::try_to_detect_premiums_update( $website, 'theme' );
190 - }
151 + $update_type = '';
191 152
192 - if ( 'upgradeplugintheme' === $what ) {
193 - if ( 'plugin' === $update_type || 'theme' === $update_type ) {
194 - if ( self::check_request_update_premium( $params['list'], $update_type ) ) {
195 - self::request_premiums_update( $website, $update_type, $params['list'] );
196 - $request_update = true;
197 - }
198 - }
199 - }
200 - }
153 + $check_premi_plugins = array();
154 + $check_premi_themes = array();
201 155
202 - return $request_update;
203 - }
156 + if ( 'stats' === $what ) {
157 + if ( '' !== $website->plugins ) {
158 + $check_premi_plugins = json_decode( $website->plugins, 1 );
159 + }
160 + if ( '' !== $website->themes ) {
161 + $check_premi_themes = json_decode( $website->themes, 1 );
162 + }
163 + } elseif ( 'upgradeplugintheme' === $what ) {
164 + $update_type = ( isset( $params['type'] ) ) ? $params['type'] : '';
165 + if ( 'plugin' === $update_type ) {
166 + if ( '' !== $website->plugins ) {
167 + $check_premi_plugins = json_decode( $website->plugins, 1 );
168 + }
169 + } elseif ( 'theme' === $update_type ) {
170 + if ( '' !== $website->themes ) {
171 + $check_premi_themes = json_decode( $website->themes, 1 );
172 + }
173 + }
174 + }
204 175
205 - /**
206 - * Method check_request_update_premium()
207 - *
208 - * Check if any updates are on the premiums list.
209 - *
210 - * @param array $list_items List of updates.
211 - * @param string $type Type of update. plugin|theme.
212 - *
213 - * @return bool true|false.
214 - */
215 - public static function check_request_update_premium( $list_items, $type ) {
176 + if ( static::check_premium_updates( $check_premi_plugins, 'plugin' ) ) {
177 + static::try_to_detect_premiums_update( $website, 'plugin' );
178 + }
216 179
217 - $updates = explode( ',', $list_items );
180 + if ( static::check_premium_updates( $check_premi_themes, 'theme' ) ) {
181 + static::try_to_detect_premiums_update( $website, 'theme' );
182 + }
218 183
219 - if ( ! is_array( $updates ) || empty( $updates ) ) {
220 - return false;
221 - }
184 + if ( 'upgradeplugintheme' === $what && ( 'plugin' === $update_type || 'theme' === $update_type ) && ( static::check_request_update_premium( $params['list'], $update_type ) || static::check_premium_updates( explode( ',', $params['list'] ), $update_type ) ) ) {
185 + $output = static::request_premiums_update( $website, $update_type, $params['list'] );
186 + return true;
187 + }
188 + }
222 189
223 - if ( 1 < count( $updates ) ) {
224 - return false;
225 - }
190 + return false;
191 + }
226 192
227 - if ( 'plugin' === $type ) {
193 + /**
194 + * Method check_request_update_premium()
195 + *
196 + * Check if any updates are on the premiums list.
197 + *
198 + * @param array $list_items List of updates.
199 + * @param string $type Type of update. plugin|theme.
200 + *
201 + * @return bool true|false.
202 + */
203 + public static function check_request_update_premium( $list_items, $type ) { // phpcs:ignore -- NOSONAR - complex.
228 204
229 - $update_premiums = array(
230 - 'yith-woocommerce-request-a-quote-premium/init.php',
231 - );
205 + $updates = explode( ',', $list_items );
232 206
233 - /**
234 - * Filter: mainwp_request_update_premium_plugins
235 - *
236 - * Filters supported premium plugins to fix compatibility problmes with updating premium plugins.
237 - *
238 - * @since Unknown
239 - */
240 - $update_premiums = apply_filters( 'mainwp_request_update_premium_plugins', $update_premiums );
207 + if ( ! is_array( $updates ) || empty( $updates ) ) {
208 + return false;
209 + }
241 210
242 - if ( is_array( $update_premiums ) && 0 < count( $update_premiums ) ) {
243 - foreach ( $updates as $slug ) {
244 - if ( ! empty( $slug ) ) {
245 - if ( in_array( $slug, $update_premiums ) ) {
246 - return true;
247 - }
248 - }
249 - }
250 - }
251 - } elseif ( 'theme' === $type ) {
211 + if ( 3 < count( $updates ) ) {
212 + return false;
213 + }
252 214
253 - $update_premiums = array();
215 + if ( 'plugin' === $type ) {
254 216
255 - /**
256 - * Filter: mainwp_request_update_premium_themes
257 - *
258 - * Filters supported premium themes to fix compatibility problmes with updating premium themes.
259 - *
260 - * @since Unknown
261 - */
262 - $update_premiums = apply_filters( 'mainwp_request_update_premium_themes', $update_premiums );
263 - if ( is_array( $update_premiums ) && 0 < count( $update_premiums ) ) {
264 - foreach ( $themes as $slug ) {
265 - if ( ! empty( $slug ) ) {
266 - if ( in_array( $slug, $update_premiums ) ) {
267 - return true;
268 - }
269 - }
270 - }
271 - }
272 - }
273 - return false;
274 - }
217 + $update_premiums = MainWP_Premium_Update_Registry::get_filter_defaults( 'plugin', 'request' );
275 218
276 - /**
277 - * Method redirect_request_site()
278 - *
279 - * Redirect to requested Site.
280 - *
281 - * @param mixed $website Child Site.
282 - * @param mixed $where_url page to redirerct to.
283 - *
284 - * @uses \MainWP\Dashboard\MainWP_Connect::get_get_data_authed()
285 - * @uses \MainWP\Dashboard\MainWP_Logger::debug()
286 - * @uses \MainWP\Dashboard\MainWP_System::$version
287 - */
288 - public static function redirect_request_site( $website, $where_url ) {
219 + /**
220 + * Filter: mainwp_request_update_premium_plugins
221 + *
222 + * Filters supported premium plugins to fix compatibility problmes with updating premium plugins.
223 + *
224 + * @since Unknown
225 + */
226 + $update_premiums = apply_filters( 'mainwp_request_update_premium_plugins', $update_premiums );
289 227
290 - $request_url = MainWP_Connect::get_get_data_authed( $website, $where_url );
228 + $prefixes = MainWP_Premium_Update_Registry::get_prefixes( 'plugin', 'request' );
291 229
292 - $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
293 - $args = array(
294 - 'timeout' => 25,
295 - 'httpversion' => '1.1',
296 - 'User-Agent' => $agent,
297 - );
230 + foreach ( $updates as $slug ) {
231 + if ( ! empty( $slug ) && MainWP_Premium_Update_Registry::slug_matches( $slug, is_array( $update_premiums ) ? $update_premiums : array(), $prefixes ) ) {
232 + return true;
233 + }
234 + }
235 + } elseif ( 'theme' === $type ) {
298 236
299 - if ( ! empty( $website->http_user ) && ! empty( $website->http_pass ) ) {
300 - $args['headers'] = array(
301 - 'Authorization' => 'Basic ' . base64_encode( $website->http_user . ':' . stripslashes( $website->http_pass ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
302 - );
303 - }
237 + $update_premiums = MainWP_Premium_Update_Registry::get_filter_defaults( 'theme', 'request' );
304 238
305 - MainWP_Logger::instance()->debug( ' :: tryRequest :: [website=' . $website->url . ']' );
306 - return wp_remote_get( $request_url, $args );
307 - }
239 + /**
240 + * Filter: mainwp_request_update_premium_themes
241 + *
242 + * Filters supported premium themes to fix compatibility problmes with updating premium themes.
243 + *
244 + * @since Unknown
245 + */
246 + $update_premiums = apply_filters( 'mainwp_request_update_premium_themes', $update_premiums );
308 247
309 - /**
310 - * Method request_premiums_update()
311 - *
312 - * Request to update plugin or theme.
313 - *
314 - * @param mixed $website Child Site to update.
315 - * @param mixed $type Type of update, plugin|theme.
316 - * @param mixed $list_items list of plugins & themes installed.
317 - *
318 - * @return mixed null|true.
319 - */
320 - public static function request_premiums_update( $website, $type, $list_items ) {
321 - if ( 'plugin' === $type ) {
322 - $where_url = 'plugins.php?_request_update_premiums_type=plugin&list=' . $list_items;
323 - } elseif ( 'theme' === $type ) {
324 - $where_url = 'update-core.php?_request_update_premiums_type=theme&list=' . $list_items;
325 - } else {
326 - return null;
327 - }
328 - self::redirect_request_site( $website, $where_url );
329 - return true;
330 - }
248 + $prefixes = MainWP_Premium_Update_Registry::get_prefixes( 'theme', 'request' );
331 249
332 - /**
333 - * Method try_to_detect_premiums_update()
334 - *
335 - * Try to detect if pugin and themes are premium.
336 - *
337 - * @param mixed $website Child Site.
338 - * @param mixed $type Type of update, plugin|theme.
339 - *
340 - * @return mixed false|self::redirect_request_site()
341 - */
342 - public static function try_to_detect_premiums_update( $website, $type ) {
343 - if ( 'plugin' === $type ) {
344 - $where_url = 'plugins.php?_detect_plugins_updates=yes';
345 - } elseif ( 'theme' === $type ) {
346 - $where_url = 'update-core.php?_detect_themes_updates=yes';
347 - } else {
348 - return false;
349 - }
350 - self::redirect_request_site( $website, $where_url );
351 - }
250 + foreach ( $updates as $slug ) {
251 + if ( ! empty( $slug ) && MainWP_Premium_Update_Registry::slug_matches( $slug, is_array( $update_premiums ) ? $update_premiums : array(), $prefixes ) ) {
252 + return true;
253 + }
254 + }
255 + }
256 + return false;
257 + }
258 +
259 + /**
260 + * Method redirect_request_site()
261 + *
262 + * Redirect to requested Site.
263 + *
264 + * Deprecated, see function handle_premium_update_actions().
265 + *
266 + * @param mixed $website Child Site.
267 + * @param mixed $where_url page to redirerct to.
268 + *
269 + * @uses \MainWP\Dashboard\MainWP_Connect::get_get_data_authed()
270 + * @uses \MainWP\Dashboard\MainWP_Logger::debug()
271 + * @uses \MainWP\Dashboard\MainWP_System::$version
272 + */
273 + public static function redirect_request_site( $website, $where_url ) {
274 +
275 + $request_url = MainWP_Connect::get_get_data_authed( $website, $where_url );
276 +
277 + $agent = 'Mozilla/5.0 (compatible; MainWP/' . MainWP_System::$version . '; +http://mainwp.com)';
278 + $args = array(
279 + 'timeout' => 25,
280 + 'httpversion' => '1.1',
281 + 'User-Agent' => $agent,
282 + 'sslverify' => static::get_ssl_verify( $website ),
283 + );
284 +
285 + if ( ! empty( $website->http_user ) && ! empty( $website->http_pass ) ) {
286 + // MWP-1548: decrypt before constructing the Basic Auth header.
287 + $http_user_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_user );
288 + $http_pass_plain = MainWP_Credential_Storage::decrypt_credential( $website->http_pass );
289 + if ( ! empty( $http_user_plain ) && ! empty( $http_pass_plain ) ) {
290 + $args['headers'] = array(
291 + 'Authorization' => 'Basic ' . base64_encode( $http_user_plain . ':' . stripslashes( $http_pass_plain ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
292 + );
293 + }
294 + }
295 +
296 + $ssl_version = isset( $website->ssl_version ) ? (int) $website->ssl_version : 0;
297 + $force_use_ipv4 = static::get_force_use_ipv4( $website );
298 +
299 + // MWP-1660: honor the site's connection profile, mirroring fetch_url().
300 + $curl_extra = function ( $handle ) use ( $ssl_version, $force_use_ipv4 ) {
301 + if ( ! empty( $ssl_version ) ) {
302 + curl_setopt( $handle, CURLOPT_SSLVERSION, $ssl_version );
303 + }
304 + if ( $force_use_ipv4 && defined( 'CURLOPT_IPRESOLVE' ) && defined( 'CURL_IPRESOLVE_V4' ) ) {
305 + curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
306 + }
307 + };
308 +
309 + add_action( 'http_api_curl', $curl_extra );
310 + try {
311 + MainWP_Logger::instance()->debug( ' :: tryRequest :: [website=' . $website->url . ']' );
312 + $response = wp_remote_get( $request_url, $args );
313 + } finally {
314 + remove_action( 'http_api_curl', $curl_extra );
315 + }
316 +
317 + static::log_request_outcome( $website, $where_url, $response );
318 +
319 + return $response;
320 + }
321 +
322 +
323 + /**
324 + * Method handle_premium_update_actions()
325 + *
326 + * Redirect to requested Site.
327 + *
328 + * @since 6.2
329 + *
330 + * @param mixed $website Child Site.
331 + * @param array $params Other params.
332 + *
333 + * @return mixed false|array information.
334 + */
335 + public static function handle_premium_update_actions( $website, $params = array() ) {
336 + if ( ! is_array( $params ) ) {
337 + return false;
338 + }
339 + MainWP_Logger::instance()->debug( 'Request premium update :: [siteid=' . $website->id . '] :: [type=' . $params['premium_type'] . '] :: [perform=' . $params['premium_perform'] . ']' );
340 + try {
341 + return MainWP_Connect::fetch_url_authed( $website, 'process_premium_updates', $params, false, false, true, null, true );
342 + } catch ( \Exception $e ) {
343 + // Just ignore.
344 + $err = $e->getMessage(); //phpcs:ignore -- NOSONAR -for debug.
345 + }
346 + return false;
347 + }
348 +
349 + /**
350 + * Resolve the sslverify argument for a website, mirroring fetch_url() semantics.
351 + *
352 + * @param mixed $website Child Site.
353 + *
354 + * @return bool
355 + */
356 + private static function get_ssl_verify( $website ) {
357 + $verify = isset( $website->verify_certificate ) ? (int) $website->verify_certificate : 2;
358 + if ( 1 === $verify ) {
359 + return true;
360 + }
361 + if ( 0 === $verify ) {
362 + return false;
363 + }
364 + return ( false === get_option( 'mainwp_sslVerifyCertificate' ) ) || ( 1 === (int) get_option( 'mainwp_sslVerifyCertificate' ) );
365 + }
366 +
367 + /**
368 + * Resolve the force-IPv4 flag for a website, mirroring fetch_url() semantics.
369 + *
370 + * @param mixed $website Child Site.
371 + *
372 + * @return bool
373 + */
374 + private static function get_force_use_ipv4( $website ) {
375 + $force = isset( $website->force_use_ipv4 ) && null !== $website->force_use_ipv4 ? (int) $website->force_use_ipv4 : null;
376 + if ( 1 === $force ) {
377 + return true;
378 + }
379 + if ( null === $force || 2 === $force ) {
380 + return 1 === (int) get_option( 'mainwp_forceUseIPv4' );
381 + }
382 + return false;
383 + }
384 +
385 + /**
386 + * Log the outcome of a premium detect/request GET and record failures per site.
387 + *
388 + * These requests previously discarded their responses entirely, leaving
389 + * support blind to sites where the premium checks silently fail (MWP-1660).
390 + *
391 + * @param mixed $website Child Site.
392 + * @param string $where_url Requested wp-admin location.
393 + * @param array|\WP_Error|mixed $response HTTP response.
394 + */
395 + private static function log_request_outcome( $website, $where_url, $response ) {
396 + $error = '';
397 + if ( is_wp_error( $response ) ) {
398 + $error = $response->get_error_message();
399 + } else {
400 + $code = (int) wp_remote_retrieve_response_code( $response );
401 + if ( $code < 200 || $code >= 400 ) {
402 + $error = 'HTTP ' . $code;
403 + }
404 + }
405 +
406 + if ( '' === $error ) {
407 + MainWP_Logger::instance()->debug_for_website( $website, 'premium_update', '[where=' . $where_url . '] :: ok' );
408 + return;
409 + }
410 +
411 + MainWP_Logger::instance()->warning_for_website( $website, 'premium_update', '[where=' . $where_url . '] :: ' . $error, false );
412 +
413 + $count = (int) MainWP_DB::instance()->get_website_option( $website, 'premium_updates_error_count' );
414 + MainWP_DB::instance()->update_website_option( $website, 'premium_updates_error_count', (string) ( $count + 1 ) );
415 + MainWP_DB::instance()->update_website_option(
416 + $website,
417 + 'premium_updates_last_error',
418 + wp_json_encode(
419 + array(
420 + 'time' => time(),
421 + 'where' => $where_url,
422 + 'error' => $error,
423 + )
424 + )
425 + );
426 + }
427 +
428 + /**
429 + * Method request_premiums_update()
430 + *
431 + * Request to update plugin or theme.
432 + *
433 + * @param mixed $website Child Site to update.
434 + * @param mixed $type Type of update, plugin|theme.
435 + * @param mixed $list_items list of plugins & themes installed.
436 + *
437 + * @return mixed
438 + */
439 + public static function request_premiums_update( $website, $type, $list_items ) {
440 + if ( ! in_array( $type, array( 'plugin', 'theme' ) ) ) {
441 + return false;
442 + }
443 + $params = array(
444 + 'premium_type' => $type,
445 + 'premium_perform' => 'premium_update',
446 + 'list' => $list_items,
447 + );
448 + return static::handle_premium_update_actions( $website, $params );
449 + }
450 +
451 + /**
452 + * Parse the child's `<mainwp>` result envelope out of an HTML response body.
453 + *
454 + * The premium request route runs the regular upgradeplugintheme callable
455 + * mid-render of a wp-admin page; the callable terminates via
456 + * MainWP_Helper::write(), so its result envelope is embedded in the page
457 + * output. Returns null when no valid envelope is present (e.g. timeout).
458 + *
459 + * @param string $body Response body.
460 + *
461 + * @return array|null Decoded information array or null.
462 + */
463 + public static function parse_mainwp_envelope( $body ) {
464 + if ( ! is_string( $body ) || '' === $body ) {
465 + return null;
466 + }
467 + if ( ! preg_match( '/<mainwp>(.*)<\/mainwp>/', $body, $results ) ) {
468 + return null;
469 + }
470 + $information = MainWP_System_Utility::get_child_response( base64_decode( $results[1] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for backwards compatibility.
471 + return is_array( $information ) ? $information : null;
472 + }
473 +
474 + /**
475 + * Get the parsed result of the most recent premium request-update GET.
476 + *
477 + * Used by fetch_url_authed() to report the child's real result. Returns null
478 + * when the request timed out or produced no parsable envelope; callers keep
479 + * the previous optimistic behavior in that case (MWP-1660).
480 + *
481 + * @return array|null
482 + */
483 + public static function get_last_parsed_response() {
484 + if ( null === self::$last_request_response || is_wp_error( self::$last_request_response ) ) {
485 + return null;
486 + }
487 + return static::parse_mainwp_envelope( wp_remote_retrieve_body( self::$last_request_response ) );
488 + }
489 +
490 + /**
491 + * Method try_to_detect_premiums_update()
492 + *
493 + * Try to detect if pugin and themes are premium.
494 + *
495 + * @param mixed $website Child Site.
496 + * @param mixed $type Type of update, plugin|theme.
497 + *
498 + * @return mixed false|static::redirect_request_site()
499 + */
500 + public static function try_to_detect_premiums_update( $website, $type ) {
501 +
502 + if ( ! in_array( $type, array( 'plugin', 'theme' ), true ) ) {
503 + return null;
504 + }
505 + $params = array(
506 + 'premium_type' => $type,
507 + 'premium_perform' => 'detect_update',
508 + );
509 + static::handle_premium_update_actions( $website, $params );
510 + }
352 511 }