# patchstack/2.1.9/includes/api.php

Patchstack – WordPress &amp; Plugins Security, version 2.1.9. 337 lines.

- Page: https://pluginprobe.com/plugins/patchstack/2.1.9/code/includes/api.php
- Raw: https://pluginprobe.com/plugins/patchstack/2.1.9/raw/includes/api.php
- Modified: 2021-11-01T08:59:56+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/patchstack/2.1.9/code/includes/api.php#L10-L20`.

```php
<?php

// Do not allow the file to be called directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * This class is used as a base for communicating with the Patchstack API.
 */
class P_Api extends P_Core {

	/**
	 * @var integer The current blog id.
	 */
	public $blog_id;

	/**
	 * Add the actions required for the API.
	 *
	 * @param Patchstack $core
	 * @return void
	 */
	public function __construct( $core ) {
		parent::__construct( $core );
		$this->blog_id = get_current_blog_id();
		add_action( 'patchstack_update_license_status', array( $this, 'update_license_status' ) );
		add_action( 'patchstack_send_ping', array( $this, 'ping' ) );
	}

	/**
	 * Get the API token.
	 *
	 * @param string  $clientid The API client ID.
	 * @param string  $secretkey The API secret key.
	 * @param boolean $fresh Whether or not to get a fresh token.
	 * @return null|string
	 */
	public function get_access_token( $clientid = '', $secretkey = '', $fresh = false ) {
		// Get current access token, if it exists.
		$token_data = $this->get_blog_option( $this->blog_id, 'patchstack_api_token', false );

		// If we do not need a fresh token, get the current one if it's not expired.
		if ( ! $fresh && isset( $token_data['token'] ) && ! $this->has_expired( $token_data['expiresin'] ) ) {
			return $token_data['token'];
		}

		// Call API and get the new access token.
		$response = $this->fetch_access_token( $clientid, $secretkey );
		if ( $response && $response->result == 'success' ) {
			$this->update_blog_option(
				$this->blog_id,
				'patchstack_api_token',
				array(
					'token'     => $response->message,
					'expiresin' => $response->expiresin,
				)
			);
			return $response->message;
		}

		// If we reach this, it means we were not able to get the access token.
		$this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' );
		return null;
	}

	/**
	 * Fetch the API Token from API Server.
	 *
	 * @param string $clientid The API client ID.
	 * @param string $secretkey The API secret key.
	 * @return string|array
	 */
	public function fetch_access_token( $clientid = '', $secretkey = '' ) {
		// Skeleton for the response data.
		$response_data = (object) array(
			'result'    => '',
			'message'   => '',
			'expiresin' => '',
		);

		// Determine if the license id/key is set.
		$client_id     = $this->get_blog_option( $this->blog_id, 'patchstack_clientid', false ) ? $this->get_blog_option( $this->blog_id, 'patchstack_clientid', false ) : $clientid;
		$client_secret = $this->get_blog_option( $this->blog_id, 'patchstack_secretkey', false ) ? $this->get_blog_option( $this->blog_id, 'patchstack_secretkey', false ) : $secretkey;
		if ( empty( $client_id ) || empty( $client_secret ) ) {
			$response_data->result  = 'failed';
			$response_data->message = __( 'API keys missing! Unable to obtain an access token.', 'patchstack' );
			return $response_data;
		}

		// Send a request to our server to obtain the access token.
		$response = wp_remote_post(
			$this->plugin->auth_url . '/oauth/token',
			array(
				'method'      => 'POST',
				'timeout'     => 60,
				'redirection' => 5,
				'httpversion' => '1.0',
				'blocking'    => true,
				'headers'     => array(),
				'body'        => array(
					'client_id'     => $client_id,
					'client_secret' => $client_secret,
					'grant_type'    => 'client_credentials',
				),
				'cookies'     => array(),
			)
		);

		// Stop if we received an error from the API.
		if ( is_wp_error( $response ) ) {
			$response_data->result  = 'failed';
			$response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $response->get_error_message();
			return $response_data;
		}

		// Parse the result.
		$result = json_decode( wp_remote_retrieve_body( $response ) );
		if ( isset( $result->access_token ) ) {
			$response_data->result    = 'success';
			$response_data->message   = $result->access_token;
			$response_data->expiresin = $result->expires_in;

			// We need to know when the token expires.
			// Defer to 'expires' if it is provided instead.
			if ( isset( $result->expires_in ) ) {
				if ( ! is_numeric( $result->expires_in ) ) {
					$response_data->message = 'expires_in value must be an integer';
					return $response_data;
				}
				$response_data->expiresin = $result->expires_in != 0 ? time() + $result->expires_in : 0;
			} elseif ( ! empty( $result->expires_in ) ) {
				// Some providers supply the seconds until expiration rather than
				// the exact timestamp. Take a best guess at which we received.
				$expires = $options['expires'];
				if ( ! $this->isExpirationTimestamp( $expires ) ) {
					$expires += time();
				}
				$response_data->expiresin = $expires;
			}
			return $response_data;
		} elseif ( isset( $result->error ) ) {
			$response_data->result  = $result->error;
			$response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $result->message;
			return $response_data;
		}
	}

	/**
	 * Checks if the API token has expired.
	 *
	 * @param integer $expiresin API token expiry.
	 * @return boolean If the token has expired.
	 */
	public function has_expired( $expiresin ) {
		return ( $expiresin < ( time() + 30 ) );
	}

	/**
	 * Retrieve the status of a license.
	 *
	 * @return void|array
	 */
	public function update_license_status() {
		// Get current license status.
		$response = $this->send_request( '/api/license/verify', 'GET' );

		// Update the representing options.
		if ( isset( $response['expires_at'] ) ) {
			$this->update_blog_option( $this->blog_id, 'patchstack_license_expiry', $response['expires_at'] );
		}

		if ( isset( $response['free'] ) ) {
			$this->update_blog_option( $this->blog_id, 'patchstack_license_free', $response['free'] == false ? 0 : 1 );

			if ( $response['free'] == true ) {
				$this->update_blog_option( $this->blog_id, 'patchstack_show_settings', 0 );
			}
		}

		if ( isset( $response['active'] ) && $response['active'] == true ) {
			$this->update_blog_option( $this->blog_id, 'patchstack_license_activated', true );
		}

		return $response;
	}

	/**
	 * Send a request to the API with optionally POST data.
	 *
	 * @param string $url
	 * @param string $request
	 * @param array  $data
	 * @return void|array If successful array, otherwise void.
	 */
	public function send_request( $url, $request, $data = array() ) {
		// Attempt to get the access token.
		$token = $this->get_access_token();
		if ( empty( $token ) ) {
			return;
		}

		// Send the remote request using the WordPress built-in method.
		$response = wp_remote_request(
			$this->plugin->api_url . $url,
			array(
				'method'      => $request,
				'timeout'     => 60,
				'redirection' => 5,
				'httpversion' => '1.0',
				'blocking'    => true,
				'headers'     => array(
					'Authorization' => 'Bearer ' . $token,
					'LicenseID'     => $this->get_blog_option( $this->blog_id, 'patchstack_clientid', 0 ),
					'Source-Host'   => get_site_url(),
				),
				'body'        => $data,
				'cookies'     => array(),
			)
		);

		// Check error or status code.
		if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) {
			$this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' );
			return;
		}

		return json_decode( wp_remote_retrieve_body( $response ), true );
	}

	/**
	 * Get the firewall rules.
	 *
	 * @return array The firewall rules.
	 */
	public function post_firewall_rule_json() {
		// If the request is coming from the API, fetch fresh rules.
		if ( isset( $_POST['webarx_refresh_rules'] ) ) {
			return $this->send_request( '/api/get-rules/2?bypass=cache', 'POST' );
		}

		return $this->send_request( '/api/get-rules/2', 'POST' );
	}

	/**
	 * Get the .htaccess rules.
	 *
	 * @param array $settings The settings on which .htaccess rules to get.
	 * @return array The .htaccess rules.
	 */
	public function post_firewall_rule( $settings ) {
		return $this->send_request( '/api/rules', 'POST', $settings );
	}

	/**
	 * Get the .htaccess firewall rules.
	 *
	 * @return array The .htaccess rules.
	 */
	public function post_firewall_htaccess_rule() {
		return $this->send_request( '/api/rules/htaccess', 'POST' );
	}

	/**
	 * Send the firewall logs to the API.
	 *
	 * @param array $logs
	 * @return array
	 */
	public function upload_firewall_logs( $logs ) {
		return $this->send_request( '/api/logs/log', 'POST', $logs );
	}

	/**
	 * Send the activity logs to the server.
	 *
	 * @param array $logs
	 * @return array
	 */
	public function upload_activity_logs( $logs ) {
		return $this->send_request( '/api/activity/log', 'POST', $logs );
	}

	/**
	 * Send WordPress core, theme, plugins versions and information to the API.
	 *
	 * @param array $software
	 * @return array
	 */
	public function upload_software( $software ) {
		return $this->send_request( '/api/sw/json', 'POST', $software );
	}

	/**
	 * Update the firewall status.
	 *
	 * @param array $status
	 * @return array
	 */
	public function update_firewall_status( $status ) {
		if ( $this->get_option( 'patchstack_license_free', 0 ) == 1 ) {
			return;
		}

		return $this->send_request( '/api/firewall/update/status', 'POST', $status );
	}

	/**
	 * Update the URL on the API.
	 *
	 * @param array $url The current URL of the site.
	 * @return array
	 */
	public function update_url( $url ) {
		return $this->send_request( '/api/plugin/update/url', 'POST', $url );
	}

	/**
	 * Send list of sites and get the id and secret key in response.
	 *
	 * @param array $sites
	 * @return array
	 */
	public function get_site_licenses( $sites ) {
		return $this->send_request( '/api/multisite-keys', 'POST', $sites );
	}

	/**
	 * Send a ping to the Patchstack API every 3 hours to make sure that the plugin is still running.
	 *
	 * @return void
	 */
	public function ping() {
		$this->send_request( '/api/ping', 'POST', array( 'firewall' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ? 1 : 0 ) );
	}
}

```
