PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 4.4.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v4.4.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 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-connect.php

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

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