PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 1.4.3
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v1.4.3
3.6.0 3.5.0 trunk 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.6.1 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 All 110 releases
buttonizer-multifunctional-button / freemius / includes / sdk / FreemiusWordPress.php

FreemiusWordPress.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 1.4.3, at freemius/includes/sdk/FreemiusWordPress.php

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