PluginProbe ʕ •ᴥ•ʔ
Auto Post Cleaner / 3.3.0
Auto Post Cleaner v3.3.0
3.12.0 3.13.1 3.2.4 3.2.5 3.3.0 3.3.10 3.3.11 3.3.8 3.4.2 3.5.3 3.6.0 3.7.0 3.7.1 3.7.2 3.7.3 3.7.5 3.7.6 3.8.0 3.9.0 3.9.4 3.9.6 3.9.7 trunk 3.0.0 3.1.0 3.10.1 3.10.2 3.11.4
delete-old-posts-programmatically / freemius / includes / sdk / FreemiusWordPress.php
delete-old-posts-programmatically / freemius / includes / sdk Last commit date
Exceptions 4 years ago FreemiusBase.php 3 years ago FreemiusWordPress.php 3 years ago LICENSE.txt 5 years ago index.php 5 years ago
FreemiusWordPress.php
716 lines
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 * @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 $bt = debug_backtrace();
306
307 $start = microtime( true );
308
309 $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
310
311 if ( FS_API__LOGGER_ON ) {
312 $end = microtime( true );
313
314 $has_body = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) );
315 $is_http_error = is_wp_error( $response );
316
317 self::$_logger[] = array(
318 'id' => count( self::$_logger ),
319 'start' => $start,
320 'end' => $end,
321 'total' => ( $end - $start ),
322 'method' => $pWPRemoteArgs['method'],
323 'path' => $pUrl,
324 'body' => $has_body ? $pWPRemoteArgs['body'] : null,
325 'result' => ! $is_http_error ?
326 $response['body'] :
327 json_encode( $response->get_error_messages() ),
328 'code' => ! $is_http_error ? $response['response']['code'] : null,
329 'backtrace' => $bt,
330 );
331 }
332
333 return $response;
334 }
335
336 /**
337 * @return array
338 */
339 static function GetLogger() {
340 return self::$_logger;
341 }
342
343 /**
344 * @param string $pCanonizedPath
345 * @param string $pMethod
346 * @param array $pParams
347 * @param null|array $pWPRemoteArgs
348 * @param bool $pIsSandbox
349 * @param null|callable $pBeforeExecutionFunction
350 *
351 * @return object[]|object|null
352 *
353 * @throws \Freemius_Exception
354 */
355 private static function MakeStaticRequest(
356 $pCanonizedPath,
357 $pMethod = 'GET',
358 $pParams = array(),
359 $pWPRemoteArgs = null,
360 $pIsSandbox = false,
361 $pBeforeExecutionFunction = null
362 ) {
363 // Connectivity errors simulation.
364 if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
365 self::ThrowCloudFlareDDoSException();
366 } else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
367 self::ThrowSquidAclException();
368 }
369
370 if ( empty( $pWPRemoteArgs ) ) {
371 $user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' .
372 home_url();
373
374 $pWPRemoteArgs = array(
375 'method' => strtoupper( $pMethod ),
376 'connect_timeout' => 10,
377 'timeout' => 60,
378 'follow_redirects' => true,
379 'redirection' => 5,
380 'user-agent' => $user_agent,
381 'blocking' => true,
382 );
383 }
384
385 if ( ! isset( $pWPRemoteArgs['headers'] ) ||
386 ! is_array( $pWPRemoteArgs['headers'] )
387 ) {
388 $pWPRemoteArgs['headers'] = array();
389 }
390
391 if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) {
392 $pWPRemoteArgs['headers']['Content-type'] = 'application/json';
393
394 if ( is_array( $pParams ) && 0 < count( $pParams ) ) {
395 $pWPRemoteArgs['body'] = json_encode( $pParams );
396 }
397 }
398
399 $request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox );
400
401 $resource = explode( '?', $pCanonizedPath );
402
403 if ( FS_SDK__HAS_CURL ) {
404 // Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait
405 // for 2 seconds if the server does not support this header.
406 $pWPRemoteArgs['headers']['Expect'] = '';
407 }
408
409 if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) {
410 $pWPRemoteArgs['sslverify'] = FS_SDK__SSLVERIFY;
411 }
412
413 if ( false !== $pBeforeExecutionFunction &&
414 is_callable( $pBeforeExecutionFunction )
415 ) {
416 $pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs );
417 }
418
419 $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
420
421 if ( is_wp_error( $result ) ) {
422 /**
423 * @var WP_Error $result
424 */
425 if ( self::IsCurlError( $result ) ) {
426 /**
427 * With dual stacked DNS responses, it's possible for a server to
428 * have IPv6 enabled but not have IPv6 connectivity. If this is
429 * the case, cURL will try IPv4 first and if that fails, then it will
430 * fall back to IPv6 and the error EHOSTUNREACH is returned by the
431 * operating system.
432 */
433 $matches = array();
434 $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
435 if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) {
436 /**
437 * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning.
438 * @author Vova Feldman (@svovaf)
439 */
440 if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) {
441 if ( strlen( inet_pton( $matches[1] ) ) === 16 ) {
442 // error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.');
443 // Hook to an action triggered just before cURL is executed to resolve the IP version to v4.
444 add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 );
445
446 // Re-run request.
447 $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs );
448 }
449 }
450 }
451 }
452
453 if ( is_wp_error( $result ) ) {
454 self::ThrowWPRemoteException( $result );
455 }
456 }
457
458 $response_body = $result['body'];
459
460 if ( empty( $response_body ) ) {
461 return null;
462 }
463
464 $decoded = json_decode( $response_body );
465
466 if ( is_null( $decoded ) ) {
467 if ( preg_match( '/Please turn JavaScript on/i', $response_body ) &&
468 preg_match( '/text\/javascript/', $response_body )
469 ) {
470 self::ThrowCloudFlareDDoSException( $response_body );
471 } 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 ) &&
472 preg_match( '/squid/', $response_body )
473 ) {
474 self::ThrowSquidAclException( $response_body );
475 } else {
476 $decoded = (object) array(
477 'error' => (object) array(
478 'type' => 'Unknown',
479 'message' => $response_body,
480 'code' => 'unknown',
481 'http' => 402
482 )
483 );
484 }
485 }
486
487 return $decoded;
488 }
489
490
491 /**
492 * Makes an HTTP request. This method can be overridden by subclasses if
493 * developers want to do fancier things or use something other than wp_remote_request()
494 * to make the request.
495 *
496 * @param string $pCanonizedPath The URL to make the request to
497 * @param string $pMethod HTTP method
498 * @param array $pParams The parameters to use for the POST body
499 * @param null|array $pWPRemoteArgs wp_remote_request options.
500 *
501 * @return object[]|object|null
502 *
503 * @throws Freemius_Exception
504 */
505 public function MakeRequest(
506 $pCanonizedPath,
507 $pMethod = 'GET',
508 $pParams = array(),
509 $pWPRemoteArgs = null
510 ) {
511 $resource = explode( '?', $pCanonizedPath );
512
513 // Only sign request if not ping.json connectivity test.
514 $sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) );
515
516 return self::MakeStaticRequest(
517 $pCanonizedPath,
518 $pMethod,
519 $pParams,
520 $pWPRemoteArgs,
521 $this->_isSandbox,
522 $sign_request ? array( &$this, 'SignRequest' ) : null
523 );
524 }
525
526 /**
527 * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter
528 *
529 * @param resource $handle A cURL handle returned by curl_init()
530 *
531 * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to
532 * CURL_IPRESOLVE_V4
533 *
534 * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e
535 */
536 static function CurlResolveToIPv4( $handle ) {
537 curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
538
539 return $handle;
540 }
541
542 #----------------------------------------------------------------------------------
543 #region Connectivity Test
544 #----------------------------------------------------------------------------------
545
546 /**
547 * If successful connectivity to the API endpoint using ping.json endpoint.
548 *
549 * - OR -
550 *
551 * Validate if ping result object is valid.
552 *
553 * @param mixed $pPong
554 *
555 * @return bool
556 */
557 public static function Test( $pPong = null ) {
558 $pong = is_null( $pPong ) ?
559 self::Ping() :
560 $pPong;
561
562 return (
563 is_object( $pong ) &&
564 isset( $pong->api ) &&
565 'pong' === $pong->api
566 );
567 }
568
569 /**
570 * Ping API to test connectivity.
571 *
572 * @return object
573 */
574 public static function Ping() {
575 try {
576 $result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' );
577 } catch ( Freemius_Exception $e ) {
578 // Map to error object.
579 $result = (object) $e->getResult();
580 } catch ( Exception $e ) {
581 // Map to error object.
582 $result = (object) array(
583 'error' => (object) array(
584 'type' => 'Unknown',
585 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
586 'code' => 'unknown',
587 'http' => 402
588 )
589 );
590 }
591
592 return $result;
593 }
594
595 #endregion
596
597 #----------------------------------------------------------------------------------
598 #region Connectivity Exceptions
599 #----------------------------------------------------------------------------------
600
601 /**
602 * @param \WP_Error $pError
603 *
604 * @return bool
605 */
606 private static function IsCurlError( WP_Error $pError ) {
607 $message = $pError->get_error_message( 'http_request_failed' );
608
609 return ( 0 === strpos( $message, 'cURL' ) );
610 }
611
612 /**
613 * @param WP_Error $pError
614 *
615 * @throws Freemius_Exception
616 */
617 private static function ThrowWPRemoteException( WP_Error $pError ) {
618 if ( self::IsCurlError( $pError ) ) {
619 $message = $pError->get_error_message( 'http_request_failed' );
620
621 #region Check if there are any missing cURL methods.
622
623 $curl_required_methods = array(
624 'curl_version',
625 'curl_exec',
626 'curl_init',
627 'curl_close',
628 'curl_setopt',
629 'curl_setopt_array',
630 'curl_error',
631 );
632
633 // Find all missing methods.
634 $missing_methods = array();
635 foreach ( $curl_required_methods as $m ) {
636 if ( ! function_exists( $m ) ) {
637 $missing_methods[] = $m;
638 }
639 }
640
641 if ( ! empty( $missing_methods ) ) {
642 throw new Freemius_Exception( array(
643 'error' => (object) array(
644 'type' => 'cUrlMissing',
645 'message' => $message,
646 'code' => 'curl_missing',
647 'http' => 402
648 ),
649 'missing_methods' => $missing_methods,
650 ) );
651 }
652
653 #endregion
654
655 // cURL error - "cURL error {{errno}}: {{error}}".
656 $parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 );
657
658 $code = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed';
659 $message = ( 1 < count( $parts ) ) ? $parts[1] : $message;
660
661 $e = new Freemius_Exception( array(
662 'error' => (object) array(
663 'code' => $code,
664 'message' => $message,
665 'type' => 'CurlException',
666 ),
667 ) );
668 } else {
669 $e = new Freemius_Exception( array(
670 'error' => (object) array(
671 'code' => $pError->get_error_code(),
672 'message' => $pError->get_error_message(),
673 'type' => 'WPRemoteException',
674 ),
675 ) );
676 }
677
678 throw $e;
679 }
680
681 /**
682 * @param string $pResult
683 *
684 * @throws Freemius_Exception
685 */
686 private static function ThrowCloudFlareDDoSException( $pResult = '' ) {
687 throw new Freemius_Exception( array(
688 'error' => (object) array(
689 'type' => 'CloudFlareDDoSProtection',
690 'message' => $pResult,
691 'code' => 'cloudflare_ddos_protection',
692 'http' => 402
693 )
694 ) );
695 }
696
697 /**
698 * @param string $pResult
699 *
700 * @throws Freemius_Exception
701 */
702 private static function ThrowSquidAclException( $pResult = '' ) {
703 throw new Freemius_Exception( array(
704 'error' => (object) array(
705 'type' => 'SquidCacheBlock',
706 'message' => $pResult,
707 'code' => 'squid_cache_block',
708 'http' => 402
709 )
710 ) );
711 }
712
713 #endregion
714 }
715 }
716