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 -712 1.3.4 → 2.6.6 View file →
@@ -1,712 +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 -
18 - require_once dirname( __FILE__ ) . '/FreemiusBase.php';
19 -
20 - if ( ! defined( 'FS_SDK__USER_AGENT' ) ) {
21 - define( 'FS_SDK__USER_AGENT', 'fs-php-' . Freemius_Api_Base::VERSION );
22 - }
23 -
24 - if ( ! defined( 'FS_SDK__SIMULATE_NO_CURL' ) ) {
25 - define( 'FS_SDK__SIMULATE_NO_CURL', false );
26 - }
27 -
28 - if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
29 - define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
30 - }
31 -
32 - if ( ! defined( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
33 - define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
34 - }
35 -
36 - if ( ! defined( 'FS_SDK__HAS_CURL' ) ) {
37 - if ( FS_SDK__SIMULATE_NO_CURL ) {
38 - define( 'FS_SDK__HAS_CURL', false );
39 - } else {
40 - $curl_required_methods = array(
41 - 'curl_version',
42 - 'curl_exec',
43 - 'curl_init',
44 - 'curl_close',
45 - 'curl_setopt',
46 - 'curl_setopt_array',
47 - 'curl_error',
48 - );
49 -
50 - $has_curl = true;
51 - foreach ( $curl_required_methods as $m ) {
52 - if ( ! function_exists( $m ) ) {
53 - $has_curl = false;
54 - break;
55 - }
56 - }
57 -
58 - define( 'FS_SDK__HAS_CURL', $has_curl );
59 - }
60 - }
61 -
62 - if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
63 - define( 'FS_SDK__SSLVERIFY', false );
64 - }
65 -
66 - $curl_version = FS_SDK__HAS_CURL ?
67 - curl_version() :
68 - array( 'version' => '7.37' );
69 -
70 - if ( ! defined( 'FS_API__PROTOCOL' ) ) {
71 - define( 'FS_API__PROTOCOL', version_compare( $curl_version['version'], '7.37', '>=' ) ? 'https' : 'http' );
72 - }
73 -
74 - if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
75 - define( 'FS_API__LOGGER_ON', false );
76 - }
77 -
78 - if ( ! defined( 'FS_API__ADDRESS' ) ) {
79 - define( 'FS_API__ADDRESS', '://api.freemius.com' );
80 - }
81 - if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
82 - define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
83 - }
84 -
85 - if ( class_exists( 'Freemius_Api_WordPress' ) ) {
86 - return;
87 - }
88 -
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 - * @since 1.0.4
169 - *
170 - * @return bool
171 - */
172 - public static function IsHttps() {
173 - return ( 'https' === self::$_protocol );
174 - }
175 -
176 - /**
177 - * Sign request with the following HTTP headers:
178 - * Content-MD5: MD5(HTTP Request body)
179 - * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
180 - * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
181 - * {scope_entity_secret_key}))
182 - *
183 - * @param string $pResourceUrl
184 - * @param array $pWPRemoteArgs
185 - *
186 - * @return array
187 - */
188 - function SignRequest( $pResourceUrl, $pWPRemoteArgs ) {
189 - $auth = $this->GenerateAuthorizationParams(
190 - $pResourceUrl,
191 - $pWPRemoteArgs['method'],
192 - ! empty( $pWPRemoteArgs['body'] ) ? $pWPRemoteArgs['body'] : ''
193 - );
194 -
195 - $pWPRemoteArgs['headers']['Date'] = $auth['date'];
196 - $pWPRemoteArgs['headers']['Authorization'] = $auth['authorization'];
197 -
198 - if ( ! empty( $auth['content_md5'] ) ) {
199 - $pWPRemoteArgs['headers']['Content-MD5'] = $auth['content_md5'];
200 - }
201 -
202 - return $pWPRemoteArgs;
203 - }
204 -
205 - /**
206 - * Generate Authorization request headers:
207 - *
208 - * Content-MD5: MD5(HTTP Request body)
209 - * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000)
210 - * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign,
211 - * {scope_entity_secret_key}))
212 - *
213 - * @author Vova Feldman
214 - *
215 - * @param string $pResourceUrl
216 - * @param string $pMethod
217 - * @param string $pPostParams
218 - *
219 - * @return array
220 - * @throws Freemius_Exception
221 - */
222 - function GenerateAuthorizationParams(
223 - $pResourceUrl,
224 - $pMethod = 'GET',
225 - $pPostParams = ''
226 - ) {
227 - $pMethod = strtoupper( $pMethod );
228 -
229 - $eol = "\n";
230 - $content_md5 = '';
231 - $content_type = '';
232 - $now = ( time() - self::$_clock_diff );
233 - $date = date( 'r', $now );
234 -
235 - if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
236 - $content_type = 'application/json';
237 -
238 - if ( ! empty( $pPostParams ) ) {
239 - $content_md5 = md5( $pPostParams );
240 - }
241 - }
242 -
243 - $string_to_sign = implode( $eol, array(
244 - $pMethod,
245 - $content_md5,
246 - $content_type,
247 - $date,
248 - $pResourceUrl
249 - ) );
250 -
251 - // If secret and public keys are identical, it means that
252 - // the signature uses public key hash encoding.
253 - $auth_type = ( $this->_secret !== $this->_public ) ? 'FS' : 'FSP';
254 -
255 - $auth = array(
256 - 'date' => $date,
257 - 'authorization' => $auth_type . ' ' . $this->_id . ':' .
258 - $this->_public . ':' .
259 - self::Base64UrlEncode( hash_hmac(
260 - 'sha256', $string_to_sign, $this->_secret
261 - ) )
262 - );
263 -
264 - if ( ! empty( $content_md5 ) ) {
265 - $auth['content_md5'] = $content_md5;
266 - }
267 -
268 - return $auth;
269 - }
270 -
271 - /**
272 - * Get API request URL signed via query string.
273 - *
274 - * @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).
275 - *
276 - * @param string $pPath
277 - *
278 - * @throws Freemius_Exception
279 - *
280 - * @return string
281 - */
282 - function GetSignedUrl( $pPath ) {
283 - $resource = explode( '?', $this->CanonizePath( $pPath ) );
284 - $pResourceUrl = $resource[0];
285 -
286 - $auth = $this->GenerateAuthorizationParams( $pResourceUrl );
287 -
288 - return Freemius_Api_WordPress::GetUrl(
289 - $pResourceUrl . '?' .
290 - ( 1 < count( $resource ) && ! empty( $resource[1] ) ? $resource[1] . '&' : '' ) .
291 - 'authorization=' . urlencode( $auth['authorization'] ) .
292 - '&auth_date=' . urlencode( $auth['date'] )
293 - , $this->_isSandbox );
294 - }
295 -
296 - /**
297 - * @author Vova Feldman
298 - *
299 - * @param string $pUrl
300 - * @param array $pWPRemoteArgs
301 - *
302 - * @return mixed
303 - */
304 - private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
305 - $start = microtime( true );
306 -
307 - $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
308 -
309 - if ( FS_API__LOGGER_ON ) {
310 - $end = microtime( true );
311 -
312 - $has_body = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) );
313 - $is_http_error = is_wp_error( $response );
314 -
315 - self::$_logger[] = array(
316 - 'id' => count( self::$_logger ),
317 - 'start' => $start,
318 - 'end' => $end,
319 - 'total' => ( $end - $start ),
320 - 'method' => $pWPRemoteArgs['method'],
321 - 'path' => $pUrl,
322 - 'body' => $has_body ? $pWPRemoteArgs['body'] : null,
323 - 'result' => ! $is_http_error ?
324 - $response['body'] :
325 - json_encode( $response->get_error_messages() ),
326 - 'code' => ! $is_http_error ? $response['response']['code'] : null,
327 - 'backtrace' => debug_backtrace(),
328 - );
329 - }
330 -
331 - return $response;
332 - }
333 -
334 - /**
335 - * @return array
336 - */
337 - static function GetLogger() {
338 - return self::$_logger;
339 - }
340 -
341 - /**
342 - * @param string $pCanonizedPath
343 - * @param string $pMethod
344 - * @param array $pParams
345 - * @param null|array $pWPRemoteArgs
346 - * @param bool $pIsSandbox
347 - * @param null|callable $pBeforeExecutionFunction
348 - *
349 - * @return object[]|object|null
350 - *
351 - * @throws \Freemius_Exception
352 - */
353 - private static function MakeStaticRequest(
354 - $pCanonizedPath,
355 - $pMethod = 'GET',
356 - $pParams = array(),
357 - $pWPRemoteArgs = null,
358 - $pIsSandbox = false,
359 - $pBeforeExecutionFunction = null
360 - ) {
361 - // Connectivity errors simulation.
362 - if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
363 - self::ThrowCloudFlareDDoSException();
364 - } else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
365 - self::ThrowSquidAclException();
366 - }
367 -
368 - if ( empty( $pWPRemoteArgs ) ) {
369 - $user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' .
370 - home_url();
371 -
372 - $pWPRemoteArgs = array(
373 - 'method' => strtoupper( $pMethod ),
374 - 'connect_timeout' => 10,
375 - 'timeout' => 60,
376 - 'follow_redirects' => true,
377 - 'redirection' => 5,
378 - 'user-agent' => $user_agent,
379 - 'blocking' => true,
380 - );
381 - }
382 -
383 - if ( ! isset( $pWPRemoteArgs['headers'] ) ||
384 - ! is_array( $pWPRemoteArgs['headers'] )
385 - ) {
386 - $pWPRemoteArgs['headers'] = array();
387 - }
388 -
389 - if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
390 - $pWPRemoteArgs['headers']['Content-type'] = 'application/json';
391 -
392 - if ( is_array( $pParams ) && 0 < count( $pParams ) ) {
393 - $pWPRemoteArgs['body'] = json_encode( $pParams );
394 - }
395 - }
396 -
397 - $request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox );
398 -
399 - $resource = explode( '?', $pCanonizedPath );
400 -
401 - if ( FS_SDK__HAS_CURL ) {
402 - // Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait
403 - // for 2 seconds if the server does not support this header.
404 - $pWPRemoteArgs['headers']['Expect'] = '';
405 - }
406 -
407 - if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) {
408 - $pWPRemoteArgs['sslverify'] = FS_SDK__SSLVERIFY;
409 - }
410 -
411 - if ( false !== $pBeforeExecutionFunction &&
412 - is_callable( $pBeforeExecutionFunction )
413 - ) {
414 - $pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs );
415 - }
416 -
417 - $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
418 -
419 - if ( is_wp_error( $result ) ) {
420 - /**
421 - * @var WP_Error $result
422 - */
423 - if ( self::IsCurlError( $result ) ) {
424 - /**
425 - * With dual stacked DNS responses, it's possible for a server to
426 - * have IPv6 enabled but not have IPv6 connectivity. If this is
427 - * the case, cURL will try IPv4 first and if that fails, then it will
428 - * fall back to IPv6 and the error EHOSTUNREACH is returned by the
429 - * operating system.
430 - */
431 - $matches = array();
432 - $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
433 - if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) {
434 - /**
435 - * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning.
436 - * @author Vova Feldman (@svovaf)
437 - */
438 - if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) {
439 - if ( strlen( inet_pton( $matches[1] ) ) === 16 ) {
440 -// error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.');
441 - // Hook to an action triggered just before cURL is executed to resolve the IP version to v4.
442 - add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 );
443 -
444 - // Re-run request.
445 - $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
446 - }
447 - }
448 - }
449 - }
450 -
451 - if ( is_wp_error( $result ) ) {
452 - self::ThrowWPRemoteException( $result );
453 - }
454 - }
455 -
456 - $response_body = $result['body'];
457 -
458 - if ( empty( $response_body ) ) {
459 - return null;
460 - }
461 -
462 - $decoded = json_decode( $response_body );
463 -
464 - if ( is_null( $decoded ) ) {
465 - if ( preg_match( '/Please turn JavaScript on/i', $response_body ) &&
466 - preg_match( '/text\/javascript/', $response_body )
467 - ) {
468 - self::ThrowCloudFlareDDoSException( $response_body );
469 - } 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 ) &&
470 - preg_match( '/squid/', $response_body )
471 - ) {
472 - self::ThrowSquidAclException( $response_body );
473 - } else {
474 - $decoded = (object) array(
475 - 'error' => (object) array(
476 - 'type' => 'Unknown',
477 - 'message' => $response_body,
478 - 'code' => 'unknown',
479 - 'http' => 402
480 - )
481 - );
482 - }
483 - }
484 -
485 - return $decoded;
486 - }
487 -
488 -
489 - /**
490 - * Makes an HTTP request. This method can be overridden by subclasses if
491 - * developers want to do fancier things or use something other than wp_remote_request()
492 - * to make the request.
493 - *
494 - * @param string $pCanonizedPath The URL to make the request to
495 - * @param string $pMethod HTTP method
496 - * @param array $pParams The parameters to use for the POST body
497 - * @param null|array $pWPRemoteArgs wp_remote_request options.
498 - *
499 - * @return object[]|object|null
500 - *
501 - * @throws Freemius_Exception
502 - */
503 - public function MakeRequest(
504 - $pCanonizedPath,
505 - $pMethod = 'GET',
506 - $pParams = array(),
507 - $pWPRemoteArgs = null
508 - ) {
509 - $resource = explode( '?', $pCanonizedPath );
510 -
511 - // Only sign request if not ping.json connectivity test.
512 - $sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) );
513 -
514 - return self::MakeStaticRequest(
515 - $pCanonizedPath,
516 - $pMethod,
517 - $pParams,
518 - $pWPRemoteArgs,
519 - $this->_isSandbox,
520 - $sign_request ? array( &$this, 'SignRequest' ) : null
521 - );
522 - }
523 -
524 - /**
525 - * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter
526 - *
527 - * @param resource $handle A cURL handle returned by curl_init()
528 - *
529 - * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to
530 - * CURL_IPRESOLVE_V4
531 - *
532 - * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e
533 - */
534 - static function CurlResolveToIPv4( $handle ) {
535 - curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
536 -
537 - return $handle;
538 - }
539 -
540 - #----------------------------------------------------------------------------------
541 - #region Connectivity Test
542 - #----------------------------------------------------------------------------------
543 -
544 - /**
545 - * If successful connectivity to the API endpoint using ping.json endpoint.
546 - *
547 - * - OR -
548 - *
549 - * Validate if ping result object is valid.
550 - *
551 - * @param mixed $pPong
552 - *
553 - * @return bool
554 - */
555 - public static function Test( $pPong = null ) {
556 - $pong = is_null( $pPong ) ?
557 - self::Ping() :
558 - $pPong;
559 -
560 - return (
561 - is_object( $pong ) &&
562 - isset( $pong->api ) &&
563 - 'pong' === $pong->api
564 - );
565 - }
566 -
567 - /**
568 - * Ping API to test connectivity.
569 - *
570 - * @return object
571 - */
572 - public static function Ping() {
573 - try {
574 - $result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' );
575 - } catch ( Freemius_Exception $e ) {
576 - // Map to error object.
577 - $result = (object) $e->getResult();
578 - } catch ( Exception $e ) {
579 - // Map to error object.
580 - $result = (object) array(
581 - 'error' => (object) array(
582 - 'type' => 'Unknown',
583 - 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
584 - 'code' => 'unknown',
585 - 'http' => 402
586 - )
587 - );
588 - }
589 -
590 - return $result;
591 - }
592 -
593 - #endregion
594 -
595 - #----------------------------------------------------------------------------------
596 - #region Connectivity Exceptions
597 - #----------------------------------------------------------------------------------
598 -
599 - /**
600 - * @param \WP_Error $pError
601 - *
602 - * @return bool
603 - */
604 - private static function IsCurlError( WP_Error $pError ) {
605 - $message = $pError->get_error_message( 'http_request_failed' );
606 -
607 - return ( 0 === strpos( $message, 'cURL' ) );
608 - }
609 -
610 - /**
611 - * @param WP_Error $pError
612 - *
613 - * @throws Freemius_Exception
614 - */
615 - private static function ThrowWPRemoteException( WP_Error $pError ) {
616 - if ( self::IsCurlError( $pError ) ) {
617 - $message = $pError->get_error_message( 'http_request_failed' );
618 -
619 - #region Check if there are any missing cURL methods.
620 -
621 - $curl_required_methods = array(
622 - 'curl_version',
623 - 'curl_exec',
624 - 'curl_init',
625 - 'curl_close',
626 - 'curl_setopt',
627 - 'curl_setopt_array',
628 - 'curl_error',
629 - );
630 -
631 - // Find all missing methods.
632 - $missing_methods = array();
633 - foreach ( $curl_required_methods as $m ) {
634 - if ( ! function_exists( $m ) ) {
635 - $missing_methods[] = $m;
636 - }
637 - }
638 -
639 - if ( ! empty( $missing_methods ) ) {
640 - throw new Freemius_Exception( array(
641 - 'error' => (object) array(
642 - 'type' => 'cUrlMissing',
643 - 'message' => $message,
644 - 'code' => 'curl_missing',
645 - 'http' => 402
646 - ),
647 - 'missing_methods' => $missing_methods,
648 - ) );
649 - }
650 -
651 - #endregion
652 -
653 - // cURL error - "cURL error {{errno}}: {{error}}".
654 - $parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 );
655 -
656 - $code = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed';
657 - $message = ( 1 < count( $parts ) ) ? $parts[1] : $message;
658 -
659 - $e = new Freemius_Exception( array(
660 - 'error' => (object) array(
661 - 'code' => $code,
662 - 'message' => $message,
663 - 'type' => 'CurlException',
664 - ),
665 - ) );
666 - } else {
667 - $e = new Freemius_Exception( array(
668 - 'error' => (object) array(
669 - 'code' => $pError->get_error_code(),
670 - 'message' => $pError->get_error_message(),
671 - 'type' => 'WPRemoteException',
672 - ),
673 - ) );
674 - }
675 -
676 - throw $e;
677 - }
678 -
679 - /**
680 - * @param string $pResult
681 - *
682 - * @throws Freemius_Exception
683 - */
684 - private static function ThrowCloudFlareDDoSException( $pResult = '' ) {
685 - throw new Freemius_Exception( array(
686 - 'error' => (object) array(
687 - 'type' => 'CloudFlareDDoSProtection',
688 - 'message' => $pResult,
689 - 'code' => 'cloudflare_ddos_protection',
690 - 'http' => 402
691 - )
692 - ) );
693 - }
694 -
695 - /**
696 - * @param string $pResult
697 - *
698 - * @throws Freemius_Exception
699 - */
700 - private static function ThrowSquidAclException( $pResult = '' ) {
701 - throw new Freemius_Exception( array(
702 - 'error' => (object) array(
703 - 'type' => 'SquidCacheBlock',
704 - 'message' => $pResult,
705 - 'code' => 'squid_cache_block',
706 - 'http' => 402
707 - )
708 - ) );
709 - }
710 -
711 - #endregion
712 - }
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 + }