PluginProbe
BlockSpare – Gutenberg Blocks, AI Content Generator & Site Builder for News, Magazine & Blogs / 2.6.6
BlockSpare – Gutenberg Blocks, AI Content Generator & Site Builder for News, Magazine & Blogs v2.6.6
5.0.1 4.1.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 2.1.2 2.5.0 2.5.3 All 39 releases
← All changes | freemius/includes/sdk/FreemiusWordPress.php +741 -745 5.0.1 → 2.6.6 View file →
@@ -1,745 +1,741 @@
1 -<?php
2 - /**
3 - * Copyright 2016 Freemius, Inc.
4 - *
5 - * Licensed under the GPL v2 (the "License"); you may
6 - * not use this file except in compliance with the License. You may obtain
7 - * a copy of the License at
8 - *
9 - * http://choosealicense.com/licenses/gpl-v2/
10 - *
11 - * Unless required by applicable law or agreed to in writing, software
12 - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 - * License for the specific language governing permissions and limitations
15 - * under the License.
16 - */
17 - if ( ! defined( 'ABSPATH' ) ) {
18 - exit;
19 - }
20 -
21 - require_once dirname( __FILE__ ) . '/FreemiusBase.php';
22 -
23 - if ( ! defined( 'FS_SDK__USER_AGENT' ) ) {
24 - define( 'FS_SDK__USER_AGENT', 'fs-php-' . Freemius_Api_Base::VERSION );
25 - }
26 -
27 - if ( ! defined( 'FS_SDK__SIMULATE_NO_CURL' ) ) {
28 - define( 'FS_SDK__SIMULATE_NO_CURL', false );
29 - }
30 -
31 - if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
32 - define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
33 - }
34 -
35 - if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
36 - define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
37 - }
38 -
39 - if ( ! defined( 'FS_SDK__HAS_CURL' ) ) {
40 - if ( FS_SDK__SIMULATE_NO_CURL ) {
41 - define( 'FS_SDK__HAS_CURL', false );
42 - } else {
43 - $curl_required_methods = array(
44 - 'curl_version',
45 - 'curl_exec',
46 - 'curl_init',
47 - 'curl_close',
48 - 'curl_setopt',
49 - 'curl_setopt_array',
50 - 'curl_error',
51 - );
52 -
53 - $has_curl = true;
54 - foreach ( $curl_required_methods as $m ) {
55 - if ( ! function_exists( $m ) ) {
56 - $has_curl = false;
57 - break;
58 - }
59 - }
60 -
61 - define( 'FS_SDK__HAS_CURL', $has_curl );
62 - }
63 - }
64 -
65 - if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
66 - define( 'FS_SDK__SSLVERIFY', false );
67 - }
68 -
69 - $curl_version = FS_SDK__HAS_CURL ?
70 - curl_version() :
71 - array( 'version' => '7.37' );
72 -
73 - if ( ! defined( 'FS_API__PROTOCOL' ) ) {
74 - define( 'FS_API__PROTOCOL', version_compare( $curl_version['version'], '7.37', '>=' ) ? 'https' : 'http' );
75 - }
76 -
77 - if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
78 - define( 'FS_API__LOGGER_ON', false );
79 - }
80 -
81 - if ( ! defined( 'FS_API__ADDRESS' ) ) {
82 - define( 'FS_API__ADDRESS', '://api.freemius.com' );
83 - }
84 - if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
85 - define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
86 - }
87 -
88 - if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
89 - class Freemius_Api_WordPress extends Freemius_Api_Base {
90 - private static $_logger = array();
91 -
92 - /**
93 - * @param string $pScope 'app', 'developer', 'user' or 'install'.
94 - * @param number $pID Element's id.
95 - * @param string $pPublic Public key.
96 - * @param string|bool $pSecret Element's secret key.
97 - * @param bool $pSandbox Whether or not to run API in sandbox mode.
98 - */
99 - public function __construct( $pScope, $pID, $pPublic, $pSecret = false, $pSandbox = false ) {
100 - // If secret key not provided, use public key encryption.
101 - if ( is_bool( $pSecret ) ) {
102 - $pSecret = $pPublic;
103 - }
104 -
105 - parent::Init( $pScope, $pID, $pPublic, $pSecret, $pSandbox );
106 - }
107 -
108 - public static function GetUrl( $pCanonizedPath = '', $pIsSandbox = false ) {
109 - $address = ( $pIsSandbox ? FS_API__SANDBOX_ADDRESS : FS_API__ADDRESS );
110 -
111 - if ( ':' === $address[0] ) {
112 - $address = self::$_protocol . $address;
113 - }
114 -
115 - return $address . $pCanonizedPath;
116 - }
117 -
118 - #----------------------------------------------------------------------------------
119 - #region Servers Clock Diff
120 - #----------------------------------------------------------------------------------
121 -
122 - /**
123 - * @var int Clock diff in seconds between current server to API server.
124 - */
125 - private static $_clock_diff = 0;
126 -
127 - /**
128 - * Set clock diff for all API calls.
129 - *
130 - * @since 1.0.3
131 - *
132 - * @param $pSeconds
133 - */
134 - public static function SetClockDiff( $pSeconds ) {
135 - self::$_clock_diff = $pSeconds;
136 - }
137 -
138 - /**
139 - * Find clock diff between current server to API server.
140 - *
141 - * @since 1.0.2
142 - * @return int Clock diff in seconds.
143 - */
144 - public static function FindClockDiff() {
145 - $time = time();
146 - $pong = self::Ping();
147 -
148 - return ( $time - strtotime( $pong->timestamp ) );
149 - }
150 -
151 - #endregion
152 -
153 - /**
154 - * @var string http or https
155 - */
156 - private static $_protocol = FS_API__PROTOCOL;
157 -
158 - /**
159 - * Set API connection protocol.
160 - *
161 - * @since 1.0.4
162 - */
163 - public static function SetHttp() {
164 - self::$_protocol = 'http';
165 - }
166 -
167 - /**
168 - * Sets API connection protocol to HTTPS.
169 - *
170 - * @since 2.5.4
171 - */
172 - public static function SetHttps() {
173 - self::$_protocol = 'https';
174 - }
175 -
176 - /**
177 - * @since 1.0.4
178 - *
179 - * @return bool
180 - */
181 - public static function IsHttps() {
182 - return ( 'https' === self::$_protocol );
183 - }
184 -
185 - /**
186 - * Sign request with the following HTTP headers:
187 - * Content-MD5: MD5(HTTP Request body)
188 - * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
189 - * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
190 - * {scope_entity_secret_key}))
191 - *
192 - * @param string $pResourceUrl
193 - * @param array $pWPRemoteArgs
194 - *
195 - * @return array
196 - */
197 - function SignRequest( $pResourceUrl, $pWPRemoteArgs ) {
198 - $auth = $this->GenerateAuthorizationParams(
199 - $pResourceUrl,
200 - $pWPRemoteArgs['method'],
201 - ! empty( $pWPRemoteArgs['body'] ) ? $pWPRemoteArgs['body'] : ''
202 - );
203 -
204 - $pWPRemoteArgs['headers']['Date'] = $auth['date'];
205 - $pWPRemoteArgs['headers']['Authorization'] = $auth['authorization'];
206 -
207 - if ( ! empty( $auth['content_md5'] ) ) {
208 - $pWPRemoteArgs['headers']['Content-MD5'] = $auth['content_md5'];
209 - }
210 -
211 - return $pWPRemoteArgs;
212 - }
213 -
214 - /**
215 - * Generate Authorization request headers:
216 - *
217 - * Content-MD5: MD5(HTTP Request body)
218 - * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
219 - * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
220 - * {scope_entity_secret_key}))
221 - *
222 - * @author Vova Feldman
223 - *
224 - * @param string $pResourceUrl
225 - * @param string $pMethod
226 - * @param string $pPostParams
227 - *
228 - * @return array
229 - * @throws Freemius_Exception
230 - */
231 - function GenerateAuthorizationParams(
232 - $pResourceUrl,
233 - $pMethod = 'GET',
234 - $pPostParams = ''
235 - ) {
236 - $pMethod = strtoupper( $pMethod );
237 -
238 - $eol = "\n";
239 - $content_md5 = '';
240 - $content_type = '';
241 - $now = ( time() - self::$_clock_diff );
242 - $date = date( 'r', $now );
243 -
244 - if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
245 - $content_type = 'application/json';
246 -
247 - if ( ! empty( $pPostParams ) ) {
248 - $content_md5 = md5( $pPostParams );
249 - }
250 - }
251 -
252 - $string_to_sign = implode( $eol, array(
253 - $pMethod,
254 - $content_md5,
255 - $content_type,
256 - $date,
257 - $pResourceUrl
258 - ) );
259 -
260 - // If secret and public keys are identical, it means that
261 - // the signature uses public key hash encoding.
262 - $auth_type = ( $this->_secret !== $this->_public ) ? 'FS' : 'FSP';
263 -
264 - $auth = array(
265 - 'date' => $date,
266 - 'authorization' => $auth_type . ' ' . $this->_id . ':' .
267 - $this->_public . ':' .
268 - self::Base64UrlEncode( hash_hmac(
269 - 'sha256', $string_to_sign, $this->_secret
270 - ) )
271 - );
272 -
273 - if ( ! empty( $content_md5 ) ) {
274 - $auth['content_md5'] = $content_md5;
275 - }
276 -
277 - return $auth;
278 - }
279 -
280 - /**
281 - * Get API request URL signed via query string.
282 - *
283 - * @since 1.2.3 Stopped using http_build_query(). Instead, use urlencode(). In some environments the encoding of http_build_query() can generate a URL that once used with a redirect, the `&` querystring separator is escaped to `&amp;` which breaks the URL (Added by @svovaf).
284 - *
285 - * @param string $pPath
286 - *
287 - * @throws Freemius_Exception
288 - *
289 - * @return string
290 - */
291 - function GetSignedUrl( $pPath ) {
292 - $resource = explode( '?', $this->CanonizePath( $pPath ) );
293 - $pResourceUrl = $resource[0];
294 -
295 - $auth = $this->GenerateAuthorizationParams( $pResourceUrl );
296 -
297 - return Freemius_Api_WordPress::GetUrl(
298 - $pResourceUrl . '?' .
299 - ( 1 < count( $resource ) && ! empty( $resource[1] ) ? $resource[1] . '&' : '' ) .
300 - 'authorization=' . urlencode( $auth['authorization'] ) .
301 - '&auth_date=' . urlencode( $auth['date'] )
302 - , $this->_isSandbox );
303 - }
304 -
305 - /**
306 - * @author Vova Feldman
307 - *
308 - * @param string $pUrl
309 - * @param array $pWPRemoteArgs
310 - *
311 - * @return mixed
312 - */
313 - private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
314 - $bt = debug_backtrace();
315 -
316 - $start = microtime( true );
317 -
318 - $response = self::RemoteRequest( $pUrl, $pWPRemoteArgs );
319 -
320 - if ( FS_API__LOGGER_ON ) {
321 - $end = microtime( true );
322 -
323 - $has_body = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) );
324 - $is_http_error = is_wp_error( $response );
325 -
326 - self::$_logger[] = array(
327 - 'id' => count( self::$_logger ),
328 - 'start' => $start,
329 - 'end' => $end,
330 - 'total' => ( $end - $start ),
331 - 'method' => $pWPRemoteArgs['method'],
332 - 'path' => $pUrl,
333 - 'body' => $has_body ? $pWPRemoteArgs['body'] : null,
334 - 'result' => ! $is_http_error ?
335 - $response['body'] :
336 - json_encode( $response->get_error_messages() ),
337 - 'code' => ! $is_http_error ? $response['response']['code'] : null,
338 - 'backtrace' => $bt,
339 - );
340 - }
341 -
342 - return $response;
343 - }
344 -
345 - /**
346 - * @author Leo Fajardo (@leorw)
347 - *
348 - * @param string $pUrl
349 - * @param array $pWPRemoteArgs
350 - *
351 - * @return array|WP_Error The response array or a WP_Error on failure.
352 - */
353 - static function RemoteRequest( $pUrl, $pWPRemoteArgs ) {
354 - $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
355 -
356 - if (
357 - is_array( $response ) &&
358 - (
359 - empty( $response['headers'] ) ||
360 - empty( $response['headers']['x-api-server'] )
361 - )
362 - ) {
363 - // API is considered blocked if the response doesn't include the `x-api-server` header. When there's no error but this header doesn't exist, the response is usually not in the expected form (e.g., cannot be JSON-decoded).
364 - $response = new WP_Error( 'api_blocked', htmlentities( $response['body'] ) );
365 - }
366 -
367 - return $response;
368 - }
369 -
370 - /**
371 - * @return array
372 - */
373 - static function GetLogger() {
374 - return self::$_logger;
375 - }
376 -
377 - /**
378 - * @param string $pCanonizedPath
379 - * @param string $pMethod
380 - * @param array $pParams
381 - * @param null|array $pWPRemoteArgs
382 - * @param bool $pIsSandbox
383 - * @param null|callable $pBeforeExecutionFunction
384 - *
385 - * @return object[]|object|null
386 - *
387 - * @throws \Freemius_Exception
388 - */
389 - private static function MakeStaticRequest(
390 - $pCanonizedPath,
391 - $pMethod = 'GET',
392 - $pParams = array(),
393 - $pWPRemoteArgs = null,
394 - $pIsSandbox = false,
395 - $pBeforeExecutionFunction = null
396 - ) {
397 - // Connectivity errors simulation.
398 - if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
399 - self::ThrowCloudFlareDDoSException();
400 - } else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
401 - self::ThrowSquidAclException();
402 - }
403 -
404 - if ( empty( $pWPRemoteArgs ) ) {
405 - $user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' .
406 - home_url();
407 -
408 - $pWPRemoteArgs = array(
409 - 'method' => strtoupper( $pMethod ),
410 - 'connect_timeout' => 10,
411 - 'timeout' => WP_FS__API_TIMEOUT,
412 - 'follow_redirects' => true,
413 - 'redirection' => 5,
414 - 'user-agent' => $user_agent,
415 - 'blocking' => true,
416 - );
417 - }
418 -
419 - if ( ! isset( $pWPRemoteArgs['headers'] ) ||
420 - ! is_array( $pWPRemoteArgs['headers'] )
421 - ) {
422 - $pWPRemoteArgs['headers'] = array();
423 - }
424 -
425 - if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
426 - $pWPRemoteArgs['headers']['Content-type'] = 'application/json';
427 -
428 - if ( is_array( $pParams ) && 0 < count( $pParams ) ) {
429 - $pWPRemoteArgs['body'] = json_encode( $pParams );
430 - }
431 - }
432 -
433 - $request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox );
434 -
435 - $resource = explode( '?', $pCanonizedPath );
436 -
437 - if ( FS_SDK__HAS_CURL ) {
438 - // Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait
439 - // for 2 seconds if the server does not support this header.
440 - $pWPRemoteArgs['headers']['Expect'] = '';
441 - }
442 -
443 - if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) {
444 - $pWPRemoteArgs['sslverify'] = FS_SDK__SSLVERIFY;
445 - }
446 -
447 - if ( false !== $pBeforeExecutionFunction &&
448 - is_callable( $pBeforeExecutionFunction )
449 - ) {
450 - $pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs );
451 - }
452 -
453 - $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
454 -
455 - if ( is_wp_error( $result ) ) {
456 - /**
457 - * @var WP_Error $result
458 - */
459 - if ( self::IsCurlError( $result ) ) {
460 - /**
461 - * With dual stacked DNS responses, it's possible for a server to
462 - * have IPv6 enabled but not have IPv6 connectivity. If this is
463 - * the case, cURL will try IPv4 first and if that fails, then it will
464 - * fall back to IPv6 and the error EHOSTUNREACH is returned by the
465 - * operating system.
466 - */
467 - $matches = array();
468 - $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
469 - if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) {
470 - /**
471 - * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning.
472 - * @author Vova Feldman (@svovaf)
473 - */
474 - if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) {
475 - if ( strlen( inet_pton( $matches[1] ) ) === 16 ) {
476 - /**
477 - * error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.');
478 - * Hook to an action triggered just before cURL is executed to resolve the IP version to v4.
479 - *
480 - * @phpstan-ignore-next-line
481 - */
482 - add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 );
483 -
484 - // Re-run request.
485 - $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
486 - }
487 - }
488 - }
489 - }
490 -
491 - if ( is_wp_error( $result ) ) {
492 - self::ThrowWPRemoteException( $result );
493 - }
494 - }
495 -
496 - $response_body = $result['body'];
497 -
498 - if ( empty( $response_body ) ) {
499 - return null;
500 - }
501 -
502 - $decoded = json_decode( $response_body );
503 -
504 - if ( is_null( $decoded ) ) {
505 - if ( preg_match( '/Please turn JavaScript on/i', $response_body ) &&
506 - preg_match( '/text\/javascript/', $response_body )
507 - ) {
508 - self::ThrowCloudFlareDDoSException( $response_body );
509 - } else if ( preg_match( '/Access control configuration prevents your request from being allowed at this time. Please contact your service provider if you feel this is incorrect./', $response_body ) &&
510 - preg_match( '/squid/', $response_body )
511 - ) {
512 - self::ThrowSquidAclException( $response_body );
513 - } else {
514 - $decoded = (object) array(
515 - 'error' => (object) array(
516 - 'type' => 'Unknown',
517 - 'message' => $response_body,
518 - 'code' => 'unknown',
519 - 'http' => 402
520 - )
521 - );
522 - }
523 - }
524 -
525 - return $decoded;
526 - }
527 -
528 -
529 - /**
530 - * Makes an HTTP request. This method can be overridden by subclasses if
531 - * developers want to do fancier things or use something other than wp_remote_request()
532 - * to make the request.
533 - *
534 - * @param string $pCanonizedPath The URL to make the request to
535 - * @param string $pMethod HTTP method
536 - * @param array $pParams The parameters to use for the POST body
537 - * @param null|array $pWPRemoteArgs wp_remote_request options.
538 - *
539 - * @return object[]|object|null
540 - *
541 - * @throws Freemius_Exception
542 - */
543 - public function MakeRequest(
544 - $pCanonizedPath,
545 - $pMethod = 'GET',
546 - $pParams = array(),
547 - $pWPRemoteArgs = null
548 - ) {
549 - $resource = explode( '?', $pCanonizedPath );
550 -
551 - // Only sign request if not ping.json connectivity test.
552 - $sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) );
553 -
554 - return self::MakeStaticRequest(
555 - $pCanonizedPath,
556 - $pMethod,
557 - $pParams,
558 - $pWPRemoteArgs,
559 - $this->_isSandbox,
560 - $sign_request ? array( &$this, 'SignRequest' ) : null
561 - );
562 - }
563 -
564 - /**
565 - * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter
566 - *
567 - * @param resource $handle A cURL handle returned by curl_init()
568 - *
569 - * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to
570 - * CURL_IPRESOLVE_V4
571 - *
572 - * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e
573 - */
574 - static function CurlResolveToIPv4( $handle ) {
575 - curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
576 -
577 - return $handle;
578 - }
579 -
580 - #----------------------------------------------------------------------------------
581 - #region Connectivity Test
582 - #----------------------------------------------------------------------------------
583 -
584 - /**
585 - * This method exists only for backward compatibility to prevent a fatal error from happening when called from an outdated piece of code.
586 - *
587 - * @param mixed $pPong
588 - *
589 - * @return bool
590 - */
591 - public static function Test( $pPong = null ) {
592 - return (
593 - is_object( $pPong ) &&
594 - isset( $pPong->api ) &&
595 - 'pong' === $pPong->api
596 - );
597 - }
598 -
599 - /**
600 - * Ping API to test connectivity.
601 - *
602 - * @return object
603 - */
604 - public static function Ping() {
605 - try {
606 - $result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' );
607 - } catch ( Freemius_Exception $e ) {
608 - // Map to error object.
609 - $result = (object) $e->getResult();
610 - } catch ( Exception $e ) {
611 - // Map to error object.
612 - $result = (object) array(
613 - 'error' => (object) array(
614 - 'type' => 'Unknown',
615 - 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
616 - 'code' => 'unknown',
617 - 'http' => 402
618 - )
619 - );
620 - }
621 -
622 - return $result;
623 - }
624 -
625 - #endregion
626 -
627 - #----------------------------------------------------------------------------------
628 - #region Connectivity Exceptions
629 - #----------------------------------------------------------------------------------
630 -
631 - /**
632 - * @param \WP_Error $pError
633 - *
634 - * @return bool
635 - */
636 - private static function IsCurlError( WP_Error $pError ) {
637 - $message = $pError->get_error_message( 'http_request_failed' );
638 -
639 - return ( 0 === strpos( $message, 'cURL' ) );
640 - }
641 -
642 - /**
643 - * @param WP_Error $pError
644 - *
645 - * @throws Freemius_Exception
646 - */
647 - private static function ThrowWPRemoteException( WP_Error $pError ) {
648 - if ( self::IsCurlError( $pError ) ) {
649 - $message = $pError->get_error_message( 'http_request_failed' );
650 -
651 - #region Check if there are any missing cURL methods.
652 -
653 - $curl_required_methods = array(
654 - 'curl_version',
655 - 'curl_exec',
656 - 'curl_init',
657 - 'curl_close',
658 - 'curl_setopt',
659 - 'curl_setopt_array',
660 - 'curl_error',
661 - );
662 -
663 - // Find all missing methods.
664 - $missing_methods = array();
665 - foreach ( $curl_required_methods as $m ) {
666 - if ( ! function_exists( $m ) ) {
667 - $missing_methods[] = $m;
668 - }
669 - }
670 -
671 - if ( ! empty( $missing_methods ) ) {
672 - throw new Freemius_Exception( array(
673 - 'error' => (object) array(
674 - 'type' => 'cUrlMissing',
675 - 'message' => $message,
676 - 'code' => 'curl_missing',
677 - 'http' => 402
678 - ),
679 - 'missing_methods' => $missing_methods,
680 - ) );
681 - }
682 -
683 - #endregion
684 -
685 - // cURL error - "cURL error {{errno}}: {{error}}".
686 - $parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 );
687 -
688 - $code = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed';
689 - $message = ( 1 < count( $parts ) ) ? $parts[1] : $message;
690 -
691 - $e = new Freemius_Exception( array(
692 - 'error' => (object) array(
693 - 'code' => $code,
694 - 'message' => $message,
695 - 'type' => 'CurlException',
696 - ),
697 - ) );
698 - } else {
699 - $e = new Freemius_Exception( array(
700 - 'error' => (object) array(
701 - 'code' => $pError->get_error_code(),
702 - 'message' => $pError->get_error_message(),
703 - 'type' => 'WPRemoteException',
704 - ),
705 - ) );
706 - }
707 -
708 - throw $e;
709 - }
710 -
711 - /**
712 - * @param string $pResult
713 - *
714 - * @throws Freemius_Exception
715 - */
716 - private static function ThrowCloudFlareDDoSException( $pResult = '' ) {
717 - throw new Freemius_Exception( array(
718 - 'error' => (object) array(
719 - 'type' => 'CloudFlareDDoSProtection',
720 - 'message' => $pResult,
721 - 'code' => 'cloudflare_ddos_protection',
722 - 'http' => 402
723 - )
724 - ) );
725 - }
726 -
727 - /**
728 - * @param string $pResult
729 - *
730 - * @throws Freemius_Exception
731 - */
732 - private static function ThrowSquidAclException( $pResult = '' ) {
733 - throw new Freemius_Exception( array(
734 - 'error' => (object) array(
735 - 'type' => 'SquidCacheBlock',
736 - 'message' => $pResult,
737 - 'code' => 'squid_cache_block',
738 - 'http' => 402
739 - )
740 - ) );
741 - }
742 -
743 - #endregion
744 - }
745 - }
1 +<?php
2 + /**
3 + * Copyright 2016 Freemius, Inc.
4 + *
5 + * Licensed under the GPL v2 (the "License"); you may
6 + * not use this file except in compliance with the License. You may obtain
7 + * a copy of the License at
8 + *
9 + * http://choosealicense.com/licenses/gpl-v2/
10 + *
11 + * Unless required by applicable law or agreed to in writing, software
12 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 + * License for the specific language governing permissions and limitations
15 + * under the License.
16 + */
17 + if ( ! defined( 'ABSPATH' ) ) {
18 + exit;
19 + }
20 +
21 + require_once dirname( __FILE__ ) . '/FreemiusBase.php';
22 +
23 + if ( ! defined( 'FS_SDK__USER_AGENT' ) ) {
24 + define( 'FS_SDK__USER_AGENT', 'fs-php-' . Freemius_Api_Base::VERSION );
25 + }
26 +
27 + if ( ! defined( 'FS_SDK__SIMULATE_NO_CURL' ) ) {
28 + define( 'FS_SDK__SIMULATE_NO_CURL', false );
29 + }
30 +
31 + if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
32 + define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
33 + }
34 +
35 + if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
36 + define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
37 + }
38 +
39 + if ( ! defined( 'FS_SDK__HAS_CURL' ) ) {
40 + if ( FS_SDK__SIMULATE_NO_CURL ) {
41 + define( 'FS_SDK__HAS_CURL', false );
42 + } else {
43 + $curl_required_methods = array(
44 + 'curl_version',
45 + 'curl_exec',
46 + 'curl_init',
47 + 'curl_close',
48 + 'curl_setopt',
49 + 'curl_setopt_array',
50 + 'curl_error',
51 + );
52 +
53 + $has_curl = true;
54 + foreach ( $curl_required_methods as $m ) {
55 + if ( ! function_exists( $m ) ) {
56 + $has_curl = false;
57 + break;
58 + }
59 + }
60 +
61 + define( 'FS_SDK__HAS_CURL', $has_curl );
62 + }
63 + }
64 +
65 + if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
66 + define( 'FS_SDK__SSLVERIFY', false );
67 + }
68 +
69 + $curl_version = FS_SDK__HAS_CURL ?
70 + curl_version() :
71 + array( 'version' => '7.37' );
72 +
73 + if ( ! defined( 'FS_API__PROTOCOL' ) ) {
74 + define( 'FS_API__PROTOCOL', version_compare( $curl_version['version'], '7.37', '>=' ) ? 'https' : 'http' );
75 + }
76 +
77 + if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
78 + define( 'FS_API__LOGGER_ON', false );
79 + }
80 +
81 + if ( ! defined( 'FS_API__ADDRESS' ) ) {
82 + define( 'FS_API__ADDRESS', '://api.freemius.com' );
83 + }
84 + if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
85 + define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
86 + }
87 +
88 + if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
89 + class Freemius_Api_WordPress extends Freemius_Api_Base {
90 + private static $_logger = array();
91 +
92 + /**
93 + * @param string $pScope 'app', 'developer', 'user' or 'install'.
94 + * @param number $pID Element's id.
95 + * @param string $pPublic Public key.
96 + * @param string|bool $pSecret Element's secret key.
97 + * @param bool $pSandbox Whether or not to run API in sandbox mode.
98 + */
99 + public function __construct( $pScope, $pID, $pPublic, $pSecret = false, $pSandbox = false ) {
100 + // If secret key not provided, use public key encryption.
101 + if ( is_bool( $pSecret ) ) {
102 + $pSecret = $pPublic;
103 + }
104 +
105 + parent::Init( $pScope, $pID, $pPublic, $pSecret, $pSandbox );
106 + }
107 +
108 + public static function GetUrl( $pCanonizedPath = '', $pIsSandbox = false ) {
109 + $address = ( $pIsSandbox ? FS_API__SANDBOX_ADDRESS : FS_API__ADDRESS );
110 +
111 + if ( ':' === $address[0] ) {
112 + $address = self::$_protocol . $address;
113 + }
114 +
115 + return $address . $pCanonizedPath;
116 + }
117 +
118 + #----------------------------------------------------------------------------------
119 + #region Servers Clock Diff
120 + #----------------------------------------------------------------------------------
121 +
122 + /**
123 + * @var int Clock diff in seconds between current server to API server.
124 + */
125 + private static $_clock_diff = 0;
126 +
127 + /**
128 + * Set clock diff for all API calls.
129 + *
130 + * @since 1.0.3
131 + *
132 + * @param $pSeconds
133 + */
134 + public static function SetClockDiff( $pSeconds ) {
135 + self::$_clock_diff = $pSeconds;
136 + }
137 +
138 + /**
139 + * Find clock diff between current server to API server.
140 + *
141 + * @since 1.0.2
142 + * @return int Clock diff in seconds.
143 + */
144 + public static function FindClockDiff() {
145 + $time = time();
146 + $pong = self::Ping();
147 +
148 + return ( $time - strtotime( $pong->timestamp ) );
149 + }
150 +
151 + #endregion
152 +
153 + /**
154 + * @var string http or https
155 + */
156 + private static $_protocol = FS_API__PROTOCOL;
157 +
158 + /**
159 + * Set API connection protocol.
160 + *
161 + * @since 1.0.4
162 + */
163 + public static function SetHttp() {
164 + self::$_protocol = 'http';
165 + }
166 +
167 + /**
168 + * Sets API connection protocol to HTTPS.
169 + *
170 + * @since 2.5.4
171 + */
172 + public static function SetHttps() {
173 + self::$_protocol = 'https';
174 + }
175 +
176 + /**
177 + * @since 1.0.4
178 + *
179 + * @return bool
180 + */
181 + public static function IsHttps() {
182 + return ( 'https' === self::$_protocol );
183 + }
184 +
185 + /**
186 + * Sign request with the following HTTP headers:
187 + * Content-MD5: MD5(HTTP Request body)
188 + * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
189 + * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
190 + * {scope_entity_secret_key}))
191 + *
192 + * @param string $pResourceUrl
193 + * @param array $pWPRemoteArgs
194 + *
195 + * @return array
196 + */
197 + function SignRequest( $pResourceUrl, $pWPRemoteArgs ) {
198 + $auth = $this->GenerateAuthorizationParams(
199 + $pResourceUrl,
200 + $pWPRemoteArgs['method'],
201 + ! empty( $pWPRemoteArgs['body'] ) ? $pWPRemoteArgs['body'] : ''
202 + );
203 +
204 + $pWPRemoteArgs['headers']['Date'] = $auth['date'];
205 + $pWPRemoteArgs['headers']['Authorization'] = $auth['authorization'];
206 +
207 + if ( ! empty( $auth['content_md5'] ) ) {
208 + $pWPRemoteArgs['headers']['Content-MD5'] = $auth['content_md5'];
209 + }
210 +
211 + return $pWPRemoteArgs;
212 + }
213 +
214 + /**
215 + * Generate Authorization request headers:
216 + *
217 + * Content-MD5: MD5(HTTP Request body)
218 + * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
219 + * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
220 + * {scope_entity_secret_key}))
221 + *
222 + * @author Vova Feldman
223 + *
224 + * @param string $pResourceUrl
225 + * @param string $pMethod
226 + * @param string $pPostParams
227 + *
228 + * @return array
229 + * @throws Freemius_Exception
230 + */
231 + function GenerateAuthorizationParams(
232 + $pResourceUrl,
233 + $pMethod = 'GET',
234 + $pPostParams = ''
235 + ) {
236 + $pMethod = strtoupper( $pMethod );
237 +
238 + $eol = "\n";
239 + $content_md5 = '';
240 + $content_type = '';
241 + $now = ( time() - self::$_clock_diff );
242 + $date = date( 'r', $now );
243 +
244 + if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
245 + $content_type = 'application/json';
246 +
247 + if ( ! empty( $pPostParams ) ) {
248 + $content_md5 = md5( $pPostParams );
249 + }
250 + }
251 +
252 + $string_to_sign = implode( $eol, array(
253 + $pMethod,
254 + $content_md5,
255 + $content_type,
256 + $date,
257 + $pResourceUrl
258 + ) );
259 +
260 + // If secret and public keys are identical, it means that
261 + // the signature uses public key hash encoding.
262 + $auth_type = ( $this->_secret !== $this->_public ) ? 'FS' : 'FSP';
263 +
264 + $auth = array(
265 + 'date' => $date,
266 + 'authorization' => $auth_type . ' ' . $this->_id . ':' .
267 + $this->_public . ':' .
268 + self::Base64UrlEncode( hash_hmac(
269 + 'sha256', $string_to_sign, $this->_secret
270 + ) )
271 + );
272 +
273 + if ( ! empty( $content_md5 ) ) {
274 + $auth['content_md5'] = $content_md5;
275 + }
276 +
277 + return $auth;
278 + }
279 +
280 + /**
281 + * Get API request URL signed via query string.
282 + *
283 + * @since 1.2.3 Stopped using http_build_query(). Instead, use urlencode(). In some environments the encoding of http_build_query() can generate a URL that once used with a redirect, the `&` querystring separator is escaped to `&amp;` which breaks the URL (Added by @svovaf).
284 + *
285 + * @param string $pPath
286 + *
287 + * @throws Freemius_Exception
288 + *
289 + * @return string
290 + */
291 + function GetSignedUrl( $pPath ) {
292 + $resource = explode( '?', $this->CanonizePath( $pPath ) );
293 + $pResourceUrl = $resource[0];
294 +
295 + $auth = $this->GenerateAuthorizationParams( $pResourceUrl );
296 +
297 + return Freemius_Api_WordPress::GetUrl(
298 + $pResourceUrl . '?' .
299 + ( 1 < count( $resource ) && ! empty( $resource[1] ) ? $resource[1] . '&' : '' ) .
300 + 'authorization=' . urlencode( $auth['authorization'] ) .
301 + '&auth_date=' . urlencode( $auth['date'] )
302 + , $this->_isSandbox );
303 + }
304 +
305 + /**
306 + * @author Vova Feldman
307 + *
308 + * @param string $pUrl
309 + * @param array $pWPRemoteArgs
310 + *
311 + * @return mixed
312 + */
313 + private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
314 + $bt = debug_backtrace();
315 +
316 + $start = microtime( true );
317 +
318 + $response = self::RemoteRequest( $pUrl, $pWPRemoteArgs );
319 +
320 + if ( FS_API__LOGGER_ON ) {
321 + $end = microtime( true );
322 +
323 + $has_body = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) );
324 + $is_http_error = is_wp_error( $response );
325 +
326 + self::$_logger[] = array(
327 + 'id' => count( self::$_logger ),
328 + 'start' => $start,
329 + 'end' => $end,
330 + 'total' => ( $end - $start ),
331 + 'method' => $pWPRemoteArgs['method'],
332 + 'path' => $pUrl,
333 + 'body' => $has_body ? $pWPRemoteArgs['body'] : null,
334 + 'result' => ! $is_http_error ?
335 + $response['body'] :
336 + json_encode( $response->get_error_messages() ),
337 + 'code' => ! $is_http_error ? $response['response']['code'] : null,
338 + 'backtrace' => $bt,
339 + );
340 + }
341 +
342 + return $response;
343 + }
344 +
345 + /**
346 + * @author Leo Fajardo (@leorw)
347 + *
348 + * @param string $pUrl
349 + * @param array $pWPRemoteArgs
350 + *
351 + * @return array|WP_Error The response array or a WP_Error on failure.
352 + */
353 + static function RemoteRequest( $pUrl, $pWPRemoteArgs ) {
354 + $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
355 +
356 + if (
357 + is_array( $response ) &&
358 + (
359 + empty( $response['headers'] ) ||
360 + empty( $response['headers']['x-api-server'] )
361 + )
362 + ) {
363 + // API is considered blocked if the response doesn't include the `x-api-server` header. When there's no error but this header doesn't exist, the response is usually not in the expected form (e.g., cannot be JSON-decoded).
364 + $response = new WP_Error( 'api_blocked', htmlentities( $response['body'] ) );
365 + }
366 +
367 + return $response;
368 + }
369 +
370 + /**
371 + * @return array
372 + */
373 + static function GetLogger() {
374 + return self::$_logger;
375 + }
376 +
377 + /**
378 + * @param string $pCanonizedPath
379 + * @param string $pMethod
380 + * @param array $pParams
381 + * @param null|array $pWPRemoteArgs
382 + * @param bool $pIsSandbox
383 + * @param null|callable $pBeforeExecutionFunction
384 + *
385 + * @return object[]|object|null
386 + *
387 + * @throws \Freemius_Exception
388 + */
389 + private static function MakeStaticRequest(
390 + $pCanonizedPath,
391 + $pMethod = 'GET',
392 + $pParams = array(),
393 + $pWPRemoteArgs = null,
394 + $pIsSandbox = false,
395 + $pBeforeExecutionFunction = null
396 + ) {
397 + // Connectivity errors simulation.
398 + if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
399 + self::ThrowCloudFlareDDoSException();
400 + } else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
401 + self::ThrowSquidAclException();
402 + }
403 +
404 + if ( empty( $pWPRemoteArgs ) ) {
405 + $user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' .
406 + home_url();
407 +
408 + $pWPRemoteArgs = array(
409 + 'method' => strtoupper( $pMethod ),
410 + 'connect_timeout' => 10,
411 + 'timeout' => 60,
412 + 'follow_redirects' => true,
413 + 'redirection' => 5,
414 + 'user-agent' => $user_agent,
415 + 'blocking' => true,
416 + );
417 + }
418 +
419 + if ( ! isset( $pWPRemoteArgs['headers'] ) ||
420 + ! is_array( $pWPRemoteArgs['headers'] )
421 + ) {
422 + $pWPRemoteArgs['headers'] = array();
423 + }
424 +
425 + if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
426 + $pWPRemoteArgs['headers']['Content-type'] = 'application/json';
427 +
428 + if ( is_array( $pParams ) && 0 < count( $pParams ) ) {
429 + $pWPRemoteArgs['body'] = json_encode( $pParams );
430 + }
431 + }
432 +
433 + $request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox );
434 +
435 + $resource = explode( '?', $pCanonizedPath );
436 +
437 + if ( FS_SDK__HAS_CURL ) {
438 + // Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait
439 + // for 2 seconds if the server does not support this header.
440 + $pWPRemoteArgs['headers']['Expect'] = '';
441 + }
442 +
443 + if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) {
444 + $pWPRemoteArgs['sslverify'] = FS_SDK__SSLVERIFY;
445 + }
446 +
447 + if ( false !== $pBeforeExecutionFunction &&
448 + is_callable( $pBeforeExecutionFunction )
449 + ) {
450 + $pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs );
451 + }
452 +
453 + $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
454 +
455 + if ( is_wp_error( $result ) ) {
456 + /**
457 + * @var WP_Error $result
458 + */
459 + if ( self::IsCurlError( $result ) ) {
460 + /**
461 + * With dual stacked DNS responses, it's possible for a server to
462 + * have IPv6 enabled but not have IPv6 connectivity. If this is
463 + * the case, cURL will try IPv4 first and if that fails, then it will
464 + * fall back to IPv6 and the error EHOSTUNREACH is returned by the
465 + * operating system.
466 + */
467 + $matches = array();
468 + $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
469 + if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) {
470 + /**
471 + * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning.
472 + * @author Vova Feldman (@svovaf)
473 + */
474 + if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) {
475 + if ( strlen( inet_pton( $matches[1] ) ) === 16 ) {
476 +// error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.');
477 + // Hook to an action triggered just before cURL is executed to resolve the IP version to v4.
478 + add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 );
479 +
480 + // Re-run request.
481 + $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
482 + }
483 + }
484 + }
485 + }
486 +
487 + if ( is_wp_error( $result ) ) {
488 + self::ThrowWPRemoteException( $result );
489 + }
490 + }
491 +
492 + $response_body = $result['body'];
493 +
494 + if ( empty( $response_body ) ) {
495 + return null;
496 + }
497 +
498 + $decoded = json_decode( $response_body );
499 +
500 + if ( is_null( $decoded ) ) {
501 + if ( preg_match( '/Please turn JavaScript on/i', $response_body ) &&
502 + preg_match( '/text\/javascript/', $response_body )
503 + ) {
504 + self::ThrowCloudFlareDDoSException( $response_body );
505 + } else if ( preg_match( '/Access control configuration prevents your request from being allowed at this time. Please contact your service provider if you feel this is incorrect./', $response_body ) &&
506 + preg_match( '/squid/', $response_body )
507 + ) {
508 + self::ThrowSquidAclException( $response_body );
509 + } else {
510 + $decoded = (object) array(
511 + 'error' => (object) array(
512 + 'type' => 'Unknown',
513 + 'message' => $response_body,
514 + 'code' => 'unknown',
515 + 'http' => 402
516 + )
517 + );
518 + }
519 + }
520 +
521 + return $decoded;
522 + }
523 +
524 +
525 + /**
526 + * Makes an HTTP request. This method can be overridden by subclasses if
527 + * developers want to do fancier things or use something other than wp_remote_request()
528 + * to make the request.
529 + *
530 + * @param string $pCanonizedPath The URL to make the request to
531 + * @param string $pMethod HTTP method
532 + * @param array $pParams The parameters to use for the POST body
533 + * @param null|array $pWPRemoteArgs wp_remote_request options.
534 + *
535 + * @return object[]|object|null
536 + *
537 + * @throws Freemius_Exception
538 + */
539 + public function MakeRequest(
540 + $pCanonizedPath,
541 + $pMethod = 'GET',
542 + $pParams = array(),
543 + $pWPRemoteArgs = null
544 + ) {
545 + $resource = explode( '?', $pCanonizedPath );
546 +
547 + // Only sign request if not ping.json connectivity test.
548 + $sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) );
549 +
550 + return self::MakeStaticRequest(
551 + $pCanonizedPath,
552 + $pMethod,
553 + $pParams,
554 + $pWPRemoteArgs,
555 + $this->_isSandbox,
556 + $sign_request ? array( &$this, 'SignRequest' ) : null
557 + );
558 + }
559 +
560 + /**
561 + * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter
562 + *
563 + * @param resource $handle A cURL handle returned by curl_init()
564 + *
565 + * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to
566 + * CURL_IPRESOLVE_V4
567 + *
568 + * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e
569 + */
570 + static function CurlResolveToIPv4( $handle ) {
571 + curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
572 +
573 + return $handle;
574 + }
575 +
576 + #----------------------------------------------------------------------------------
577 + #region Connectivity Test
578 + #----------------------------------------------------------------------------------
579 +
580 + /**
581 + * This method exists only for backward compatibility to prevent a fatal error from happening when called from an outdated piece of code.
582 + *
583 + * @param mixed $pPong
584 + *
585 + * @return bool
586 + */
587 + public static function Test( $pPong = null ) {
588 + return (
589 + is_object( $pPong ) &&
590 + isset( $pPong->api ) &&
591 + 'pong' === $pPong->api
592 + );
593 + }
594 +
595 + /**
596 + * Ping API to test connectivity.
597 + *
598 + * @return object
599 + */
600 + public static function Ping() {
601 + try {
602 + $result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' );
603 + } catch ( Freemius_Exception $e ) {
604 + // Map to error object.
605 + $result = (object) $e->getResult();
606 + } catch ( Exception $e ) {
607 + // Map to error object.
608 + $result = (object) array(
609 + 'error' => (object) array(
610 + 'type' => 'Unknown',
611 + 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
612 + 'code' => 'unknown',
613 + 'http' => 402
614 + )
615 + );
616 + }
617 +
618 + return $result;
619 + }
620 +
621 + #endregion
622 +
623 + #----------------------------------------------------------------------------------
624 + #region Connectivity Exceptions
625 + #----------------------------------------------------------------------------------
626 +
627 + /**
628 + * @param \WP_Error $pError
629 + *
630 + * @return bool
631 + */
632 + private static function IsCurlError( WP_Error $pError ) {
633 + $message = $pError->get_error_message( 'http_request_failed' );
634 +
635 + return ( 0 === strpos( $message, 'cURL' ) );
636 + }
637 +
638 + /**
639 + * @param WP_Error $pError
640 + *
641 + * @throws Freemius_Exception
642 + */
643 + private static function ThrowWPRemoteException( WP_Error $pError ) {
644 + if ( self::IsCurlError( $pError ) ) {
645 + $message = $pError->get_error_message( 'http_request_failed' );
646 +
647 + #region Check if there are any missing cURL methods.
648 +
649 + $curl_required_methods = array(
650 + 'curl_version',
651 + 'curl_exec',
652 + 'curl_init',
653 + 'curl_close',
654 + 'curl_setopt',
655 + 'curl_setopt_array',
656 + 'curl_error',
657 + );
658 +
659 + // Find all missing methods.
660 + $missing_methods = array();
661 + foreach ( $curl_required_methods as $m ) {
662 + if ( ! function_exists( $m ) ) {
663 + $missing_methods[] = $m;
664 + }
665 + }
666 +
667 + if ( ! empty( $missing_methods ) ) {
668 + throw new Freemius_Exception( array(
669 + 'error' => (object) array(
670 + 'type' => 'cUrlMissing',
671 + 'message' => $message,
672 + 'code' => 'curl_missing',
673 + 'http' => 402
674 + ),
675 + 'missing_methods' => $missing_methods,
676 + ) );
677 + }
678 +
679 + #endregion
680 +
681 + // cURL error - "cURL error {{errno}}: {{error}}".
682 + $parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 );
683 +
684 + $code = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed';
685 + $message = ( 1 < count( $parts ) ) ? $parts[1] : $message;
686 +
687 + $e = new Freemius_Exception( array(
688 + 'error' => (object) array(
689 + 'code' => $code,
690 + 'message' => $message,
691 + 'type' => 'CurlException',
692 + ),
693 + ) );
694 + } else {
695 + $e = new Freemius_Exception( array(
696 + 'error' => (object) array(
697 + 'code' => $pError->get_error_code(),
698 + 'message' => $pError->get_error_message(),
699 + 'type' => 'WPRemoteException',
700 + ),
701 + ) );
702 + }
703 +
704 + throw $e;
705 + }
706 +
707 + /**
708 + * @param string $pResult
709 + *
710 + * @throws Freemius_Exception
711 + */
712 + private static function ThrowCloudFlareDDoSException( $pResult = '' ) {
713 + throw new Freemius_Exception( array(
714 + 'error' => (object) array(
715 + 'type' => 'CloudFlareDDoSProtection',
716 + 'message' => $pResult,
717 + 'code' => 'cloudflare_ddos_protection',
718 + 'http' => 402
719 + )
720 + ) );
721 + }
722 +
723 + /**
724 + * @param string $pResult
725 + *
726 + * @throws Freemius_Exception
727 + */
728 + private static function ThrowSquidAclException( $pResult = '' ) {
729 + throw new Freemius_Exception( array(
730 + 'error' => (object) array(
731 + 'type' => 'SquidCacheBlock',
732 + 'message' => $pResult,
733 + 'code' => 'squid_cache_block',
734 + 'http' => 402
735 + )
736 + ) );
737 + }
738 +
739 + #endregion
740 + }
741 + }