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