client = $client; $hash = md5( $this->client->getSlug() . $this->client->getProductId() . home_url() ); $this->option_key = 'absp_service_api_' . $hash . '_manage_license'; $this->data_key = $this->client->getSlug() . '-license'; $this->schedule_hook = $this->client->getSlug() . '_license_check_event'; // Load the license. $this->get_license(); $this->set_activation_required_message( /* translators: %s: Plugin Name */ sprintf( esc_html__( 'Active %s license to get professional support and automatic update from your WordPress dashboard.', 'absolute-addons' ), '' . esc_html( $this->client->getName() ) . '' ) ); } public function set_page_url( $url ) { $this->page_url = esc_url_raw( $url ); return $this; } public function get_page_url() { if ( null !== $this->page_url ) { return $this->page_url; } return admin_url( 'admin.php?page=' . $this->menu_args['menu_slug'] ); } public function set_activation_required_message( $message ) { $this->activation_required = $message; return $this; } public function use_custom_style() { $this->use_custom_style = true; return $this; } private function updating_license( $status ) { $status = (bool) $status; // Set initial flag. $this->updating_license = $status; if ( $status ) { set_transient( $this->option_key . '_is_updating_license', 'yes', 20 ); } else { delete_transient( $this->option_key . '_is_updating_license' ); } return $this; // Method Chain. } private function is_updating_license() { return $this->updating_license || 'yes' === get_transient( $this->option_key . '_is_updating_license' ); } /** * Initialize License. * * @return void */ public function init() { add_action( 'init', [ $this, 'handle_license_page_form' ] ); if ( null === $this->menu_args ) { $this->set_menu_args(); } // Run hook to check license status daily. add_action( $this->schedule_hook, [ $this, 'check_license_status' ] ); $this->userCapability = $this->menu_args['capability']; $this->is_license_page = isset( $_GET['page'] ) && $_GET['page'] === $this->menu_args['menu_slug']; // phpcs:ignore add_action( 'plugin_action_links_' . $this->client->getBasename(), [ $this, 'plugin_action_links' ] ); add_action( 'admin_notices', [ $this, '__admin_notices' ] ); // Activation/Deactivation hooks. $this->activation_deactivation(); // Check the validity and save the state. (check after cron scheduled) $this->is_valid(); // Set Did Init Flag $this->did_init = true; } /** * Expose the License Key. * * @return void|string */ public function get_key() { $this->get_license(); return $this->license['key']; } /** * Display Admin Notices. * * @return void */ public function __admin_notices() { if ( ! current_user_can( $this->userCapability ) ) { return; } if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL === true ) { $host = wp_parse_url( $this->client->getServiceEndpoint( 'license' ), PHP_URL_HOST ); if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) || ( defined( 'WP_ACCESSIBLE_HOSTS' ) && false === stristr( WP_ACCESSIBLE_HOSTS, $host ) ) ) { ?>

'. esc_html__( 'Warning!', 'absolute-addons' ).'', esc_html( $this->client->getName() ), '' . esc_html( $host ) . '', 'WP_ACCESSIBLE_HOSTS' ); ?>

inactive_license_notice(); if ( ! empty( $this->error ) ) { ?>
error ) ); ?>
success ) ) { ?>
success ) ); ?>
is_license_page && ! $this->is_valid() && ! $this->is_updating_license() ) { ?>

' . esc_attr( $this->client->getName() ) . '', esc_attr( $this->client->getType() ), ''. esc_html__( 'Click here', 'woo-feed' ) .'', '' . esc_attr( $this->client->getName() ) . '' ); ?>

get_page_url(); if ( ! empty( $age_url ) ) { $links[] = sprintf( '%s', esc_url( $age_url ), esc_html( $this->menu_args['menu_title'] ) ); } return $links; } /** * Check license. * * @return array */ public function check() { return $this->request( 'status', $this->license ); } /** * Check Plugin Update. * * @return array */ public function check_update() { return $this->request( 'update', $this->license ); } /** * Get Plugin data. * * @return array { * Plugin Information * @type bool $success API response status * @type string $api_call_execution_time API Man (Rest Response) Execution Time * @type array $data { * Plugin Data ( API Man.) * @type array $package { * @type int $product_id API Man Product ID * } * @type array $info { * @type string $id Plugin Id * @type string $name Plugin Name * @type string $author Author Name * @type string $author_profile Author Profile URL * @type string $slug Plugin Slug * @type string $plugin Plugin main file path * @type string $new_version New Version String * @type string $url Plugin URL * @type string $package Plugin update download URL * @type string $icons Plugin Icons * @type string $banners Plugin Banners * @type string $banner_rtl RTL Version of Plugin Banners * @type string $upgrade_notice Upgrade Notice * @type string $requires Minimum WordPress Version * @type string $requires_php Minimum PHP Version * @type string $tested Tested upto WordPress Version * @type array $compatibility Compatibility information (API Man sends string) * @type array $contributors Plugin Contributors List (if available) * @type array $ratings Plugin Rating (if available) * @type float $num_ratings Plugin Rating (if available) * @type string $last_updated Last updated Date * @type string $homepage Plugin Home Page URL * @type array $sections { * Plugin Description Sections * @type string $description Plugin Description * @type string $changelog Change LOG * } * @type mixed $author_block_count * @type mixed $author_block_rating * } * } * } */ public function get_information() { return $this->request( 'information', $this->license ); } /** * Active a license. * * @param array $license license data. * @return array */ public function activate( $license ) { return $this->request( 'activate', $license ); } /** * Deactivate current license. * * @return array */ public function deactivate() { return $this->request( 'deactivate', $this->license ); } /** * Send common request. * * @param string $action request action. * @param array $license license data. * * @return array */ protected function request( $action, $license = [] ) { // WC-AM Valid Actions and response data types. $actions = [ 'activate' => 'json', 'deactivate' => 'json', 'status' => 'json', 'information' => 'json', 'update' => 'json', 'plugininformation' => 'serialize', // serialize option doesn't provide success status. 'pluginupdatecheck' => 'serialize', ]; if ( ! in_array( $action, array_keys( $actions ) ) ) { return [ 'success' => false, 'error' => __( 'Invalid Request Action.', 'absolute-addons' ), ]; } // parse license data $license = wp_parse_args( $license, $this->get_license() ); // validate license data. if ( ! $this->validate_license_data( $license ) ) { return [ 'success' => false, 'error' => __( 'Invalid/Empty License Data.', 'absolute-addons' ), ]; } /** @noinspection HttpUrlsUsage */ $params = [ 'object' => str_ireplace( [ 'http://', 'https://' ], '', home_url() ), 'api_key' => $license['key'], 'instance' => $license['instance'], 'product_id' => $license['product_id'], 'plugin_name' => $this->client->getBasename(), 'wc_am_action' => $action, ]; $response = $this->client->request( [ 'body' => $params, 'server' => 'license', 'blocking' => true ] ); if ( ! is_wp_error( $response ) ) { $response = wp_remote_retrieve_body( $response ); if ( 'json' == $actions[ $action ] ) { $response = json_decode( $response, true ); } else { $response = maybe_unserialize( $response ); if( is_string( $response ) && 0 === strpos( $response, '{' ) || 0 === strpos( $response, '[' ) ) { // WC-AM returns error with json. $response = json_decode( $response, true ); } else { return [ 'success' => true, 'data' => $response, // For sanity. 'api_call_execution_time' => '', ]; } } if ( empty( $response ) || ! isset( $response['success'] ) || ( isset( $response['success'] ) && ! $response['success'] ) ) { return [ 'success' => false, 'code' => isset( $response['code'] ) ? $response['code'] : 'UNKNOWN ERROR', 'error' => isset( $response['error'] ) ? $response['error'] : __( 'Unknown error occurred, Please try again.', 'absolute-addons' ), 'data' => isset( $response['data'] ) ? $response['data'] : '', ]; } return $response; } else { return [ 'success' => false, 'error' => $response->get_error_message(), 'code' => $response->get_error_code(), 'data' => $response->get_error_data( $response->get_error_code() ), ]; } } public function set_menu_args( $args = [] ) { $defaults = [ 'type' => 'menu', // Can be: menu, options, submenu. 'page_title' => __( 'Manage License', 'absolute-addons' ), 'menu_title' => __( 'Manage License', 'absolute-addons' ), 'capability' => 'manage_options', 'menu_slug' => $this->client->getSlug() . '-manage-license', 'icon_url' => '', 'position' => null, 'parent_slug' => '', ]; $this->menu_args = wp_parse_args( $args, $defaults ); return $this; } /** * Add settings page for license. * * @param array $args settings for rendering the menu. * * @return $this */ public function add_settings_page() { if ( $this->did_init ) { _doing_it_wrong( __METHOD__, sprintf( /* translators: 1. Class Method. */ __( '%s Should be called before License::init()', 'absolute-addons'), '' . __METHOD__ . '' ), '1.0.0' ); return $this; } if ( ! in_array( $this->menu_args['type'], [ 'menu', 'options', 'submenu' ] ) ) { if ( empty( $this->menu_args['parent_slug'] ) ) { $this->menu_args['type'] = 'menu'; } } if ( 'submenu' == $this->menu_args['type'] && empty( $this->menu_args['parent_slug'] ) ) { $this->menu_args['type'] = 'options'; } add_action( 'admin_menu', [ $this, '__admin_menu' ], 999 ); return $this; } /** * Admin Menu hook. * * @return void */ public function __admin_menu() { switch ( $this->menu_args['type'] ) { case 'submenu': $this->add_submenu_page(); break; case 'options': $this->add_options_page(); break; case 'menu': default: $this->add_menu_page(); break; } } /** * License menu output. * * @return void */ public function render_menu_page() { ?>


render_license_page(); ?>
licenses_style(); $action = ( isset( $this->license['status'] ) && 'active' == $this->license['status'] ) ? 'deactivate' : 'activate'; ?> client->getSlug() . '_license_section' ); ?>
render_page_header(); ?>
activation_required ) { echo wp_kses_post( wpautop( $this->activation_required ) ); } ?> render_activation_form( $action ); ?> render_license_info(); ?>
client->getSlug() . '_license_section' ); ?> label information for variable product. So user can select from the dropdown. * */ public function set_product_data( $data = [] ) { if ( ! empty( $data ) ) { $this->product_data = $data; } return $this; } /** * License form submit. * * @return void */ public function handle_license_page_form() { $check_key = $this->client->getSlug() . '-check-license'; if ( isset( $_GET[ $check_key ] ) && wp_verify_nonce( sanitize_text_field( $_GET[ $check_key ] ), $this->client->getSlug() ) ) { $this->check_license_status(); wp_safe_redirect( $this->get_page_url() ); die(); } if ( isset( $_POST[ $this->data_key ], $_POST[ $this->data_key ]['_action'] ) ) { check_admin_referer( $this->data_key ); switch ( $_POST[ $this->data_key ]['_action'] ) { case 'activate': $this->activate_client_license( array_map( 'sanitize_text_field', $_POST[ $this->data_key ] ) ); break; case 'deactivate': $this->deactivate_client_license(); break; default: break; } } } /** * Check license status on schedule. * Check and update license status on db. * * @return void */ public function check_license_status() { $this->updating_license( true ); // Get current license data. $license = $this->get_license(); if ( $license ) { // check license. $response = $this->check(); if ( isset( $response['success'] ) && $response['success'] ) { // Update license status. $license = wp_parse_args( $this->parse_license_from_request( $response ), $license ); } else { // Don't reset the key. // keep it, if the user renew subscription update the status and reactivate the plugin. $license = wp_parse_args( [ //'key' => '', 'status' => 'inactive', //'instance' => '', //'product_id' => empty( $this->client->getProductId() ) ? $this->client->getProductId() : $this->client->getName(), // product id. 'remaining' => 0, 'activations' => 0, 'limit' => 0, 'unlimited' => false, 'expires' => 0, ], $license ); } // Update the license state & and save in db. $this->set_license( $license ); } $this->updating_license( false ); } /** * Check this is a valid license. * * @param array $license Optional. A license instance to check. * Default blank. * If blank it will test against license from database.. * * @return bool */ public function is_valid( $license = [] ) { if ( null !== $this->is_valid_license ) { return $this->is_valid_license; } // Load the license if already not loaded. if ( empty( $license ) ) { $license = $this->get_license(); } if ( isset( $license['key'], $license['instance'], $license['product_id'], $license['status'] ) && 'active' === $license['status'] ) { $this->is_valid_license = $this->validate_license_signature(); } else { $this->is_valid_license = false; } return $this->is_valid_license; } /** * Validate license data for request. * * @param array $license license data. * * @return bool */ public function validate_license_data( $license = [] ) { $license = $this->parse_license_data( $license ); return ( ! empty( $license['key'] ) && ! empty( $license['instance'] ) && ! empty( $license['product_id'] ) ); } /** * Read WooCommerce API Manager Data, Convert to new license format and save in db. * * @param bool $override override current settings. * @return bool */ public function migrate_license_from_wc_am( $override = false ) { // phpcs:disable /*// WC AM data structure. [ '_data' => [ 'api_key', 'activation_email', ], // api key & email '_product_id' => '', // product title or name '_instance' => '', // instance key unique id '_activated' => '', // activation status => Activated|Deactivated '_deactivate_checkbox' => '', // deactivation check box state > On|Off ];*/ // phpcs:enable // Check if already migrated || override. if ( 1 == get_option( $this->option_key . '_wc_am_migrated', false ) && ! $override ) { return false; } // Is already migrated. // WC API Manager data prefix. $wcAmPrefix = str_ireplace( [ ' ', '_', '&', '?' ], '_', strtolower( $this->client->getName() ) ); $license = [ 'key' => '', 'status' => 'deactivate', // activate. 'instance' => '', // max len 190. ]; // Get key. $data = get_option( $wcAmPrefix . '_data', false ); if ( $data && isset( $data['api_key'] ) ) { $license['key'] = $data['api_key']; } if ( $data && isset( $data['product_id'] ) ) { $license['product_id'] = $data['product_id']; } // Is found?. if ( ! isset( $license['product_id'] ) ) { $license['product_id'] = get_option( $wcAmPrefix . '_product_id', '' ); } // Iinstance id. $license['instance'] = get_option( $wcAmPrefix . '_instance', '' ); // Activation status. $data = get_option( $wcAmPrefix . '_activated', false ); if ( $data ) { $license['status'] = strtolower( $data ) === 'activated' ? 'active' : 'inactive'; // Deactivated. } // Save license. $this->set_license( $license ); $this->check_license_status(); update_option( $this->option_key . '_wc_am_migrated', 1, false ); return true; } /** * Styles for licenses page. * * @return void */ private function licenses_style() { ?> client->getProductId() ); ?>
data_key ); ?>
autocomplete="off" required>
product_data ) ) { ?>
autocomplete="off" required>
manage_license_url ) { ?>
license['status'] ? 'active' : 'inactive'; $remaining = $this->license['remaining']; ?>

license['updated_at'] ); ?>

render_license_expire_datetime(); ?>

license['unlimited'] ) { ?>

'; $unlimited .= ''; $unlimited .= ''; $unlimited .= ''; $unlimited .= ''; $unlimited .= ''; $unlimited .= '' . esc_html__( 'Unlimited', 'absolute-addons' ) . ''; printf( /* translators: 1: Remaining activation, 2: Total activation (limit) */ esc_html__( '%1$d out of %2$s', 'absolute-addons' ), esc_attr( $this->license['activations'] ), $unlimited ); ?>

license['limit'] ) ); ?>

license['status'] ) { $expires = __( 'N/A', 'absolute-addons' ); $expiring = true; } elseif ( empty( $this->license['expires'] ) ) { $expires = __( 'N/A', 'absolute-addons' ); $expiring = false; } else { $time_diff = $this->license['expires'] - current_time( 'timestamp', true ); if ( $time_diff > 0 && $time_diff < WEEK_IN_SECONDS ) { // translators: placeholder is human time diff (e.g. "3 weeks") $expires = sprintf( __( 'In %s', 'absolute-addons' ), human_time_diff( current_time( 'timestamp', true ), $this->license['expires'] ) ); $expiring = true; } elseif ( $time_diff < 0 && absint( $time_diff ) < WEEK_IN_SECONDS ) { // translators: placeholder is human time diff (e.g. "3 weeks") $expires = sprintf( __( '%s ago', 'absolute-addons' ), human_time_diff( current_time( 'timestamp', true ), $this->license['expires'] ) ); $expiring = true; } else { $zone = wp_timezone(); $expires = date_create( '@' . $this->license['expires'], $zone ); $expires = wp_date( 'F j, Y H:i (T)', $expires->getTimestamp(), $zone ); $expiring = false; } } ?>

header_icon_url = esc_url_raw( $url ); return $this; } public function remove_header() { $this->remove_header = true; return $this; } /** * Card header. * * @return void */ private function render_page_header() { if ( $this->remove_header ) { return; } ?>
header_icon_url ) { ?> <?php esc_attr_e( 'License Management', 'absolute-addons' ); ?>

client->getName() ); ?>

updating_license( true ); if ( empty( $postData['license_key'] ) ) { $this->error = __('The license key field is required.', 'absolute-addons' ); return; } $license = $this->get_license(); $updateKey = $this->validate_license_data( $license ); // Check if it's a change request. if ( $updateKey ) { $deactivate = $this->deactivate(); // deactivate first. if ( ! $deactivate['success'] ) { $check = $this->check(); // Check api status. if ( $check['success'] && isset( $check['activated'] ) && $check['activated'] ) { if ( $deactivate['error'] ) { $this->error = $deactivate['error']; } else { $this->error = __( 'Unknown error occurred.', 'absolute-addons' ); } return; } } } // Set new license info. $license['key'] = $postData['license_key']; $license['product_id'] = $postData['product_id']; if ( empty( $license['instance'] ) ) { $license['instance'] = $this->generateInstanceId(); } // Activate The License. $response = $this->activate( $license ); if ( ! $response['success'] ) { if ( $response['error'] ) { $this->error = $response['error']; } else { $this->error = __( 'Unknown error occurred.', 'absolute-addons' ); } return; } // Don't reset the key. // keep it, so if the user renew subscription update the status and reactivate the plugin. // Update license status. $license = wp_parse_args( $this->parse_license_from_request( $response ), $license ); $this->set_license( $license ); if ( ! $updateKey ) { $this->success = __( 'License activated successfully.', 'absolute-addons' ); } else { $this->success = __( 'License updated successfully.', 'absolute-addons' ); } $this->schedule_cron_event(); $this->updating_license( false ); } /** * deactivate client license. * * @return void */ private function deactivate_client_license() { if ( ! isset( $this->license['key'] ) || empty( $this->license['key'] ) ) { $this->error = __( 'License key not found.', 'absolute-addons' ); } else { $response = $this->deactivate(); if ( ! $response['success'] ) { // check api status. $check = $this->check(); if ( $check['success'] && isset( $check['activated'] ) && $check['activated'] ) { if ( $response['error'] ) { $this->error = $response['error']; } else { $this->error = __( 'Unknown error occurred.', 'absolute-addons' ); } } } } $this->clear_scheduler(); // Keep the instance key for reference. $this->set_license( [ 'instance' => $this->license['instance'] ] ); $this->success = __( 'License deactivated successfully.', 'absolute-addons' ); } /** * Add license menu page. * * @return void */ private function add_menu_page() { add_menu_page( $this->menu_args['page_title'], $this->menu_args['menu_title'], $this->menu_args['capability'], $this->menu_args['menu_slug'], [ $this, 'render_menu_page' ], $this->menu_args['icon_url'], $this->menu_args['position'] ); } /** * Add submenu page. * * @return void */ private function add_submenu_page() { add_submenu_page( $this->menu_args['parent_slug'], $this->menu_args['page_title'], $this->menu_args['menu_title'], $this->menu_args['capability'], $this->menu_args['menu_slug'], [ $this, 'render_menu_page' ] ); } /** * Add submenu page. * * @return void */ private function add_options_page() { add_options_page( $this->menu_args['page_title'], $this->menu_args['menu_title'], $this->menu_args['capability'], $this->menu_args['menu_slug'], [ $this, 'render_menu_page' ] ); } /** * Schedule daily license checker event. * * @return void */ public function schedule_cron_event() { if ( ! wp_next_scheduled( $this->schedule_hook ) ) { wp_schedule_event( time(), 'daily', $this->schedule_hook ); wp_schedule_single_event( time() + 20, $this->schedule_hook ); } } /** * Clear any scheduled hook. * * @return void */ public function clear_scheduler() { wp_clear_scheduled_hook( $this->schedule_hook ); } /** * Register Activation And Deactivation Hooks. * * @return void */ private function activation_deactivation() { switch ( $this->client->getType() ) { case 'plugin': register_activation_hook( $this->client->getBasename(), [ $this, 'schedule_cron_event' ] ); register_deactivation_hook( $this->client->getBasename(), [ $this, 'project_deactivation' ] ); add_action( 'activated_plugin', [ $this, 'redirect_to_license_page' ], 999, 2 ); break; case 'theme': add_action( 'switch_theme', [ $this, 'project_deactivation' ] ); add_action( 'after_switch_theme', [ $this, 'schedule_cron_event' ] ); add_action( 'after_switch_theme', [ $this, 'redirect_to_license_page' ], 999, 2 ); break; } } /** * Project Deactivation Callback. * * @return void */ public function project_deactivation() { $this->get_license(); $this->deactivate_client_license(); } /** * Redirect to the license activation page after plugin/theme is activated. * * @TODO make option for the plugin/theme (which is using this lib) can alter this method with their custom function. * * @param string $param1 Plugin: base file|Theme: old theme name. * @param bool|WP_Theme $param2 Plugin: network wide activation status|Theme: WP_Theme instance of the old theme. * * @return void */ public function redirect_to_license_page( $param1, $param2 = null ) { $canRedirect = false; if ( 'plugin' == $this->client->getType() ) { $canRedirect = ( $param1 == $this->client->getBasename() ); } if ( 'theme' == $this->client->getType() ) { $canRedirect = ( ! get_option( 'theme_switched_via_customizer' ) ); } if ( $canRedirect ) { wp_safe_redirect( $this->get_page_url() ); die(); } } /** * Form action URL. * * @return void */ private function formActionUrl( $echo = true ) { if ( $echo ) { echo esc_url( $this->get_page_url() ); } return $this->get_page_url(); } /** * Get input license key. * * @param array $license license data. * @return string */ private function get_input_license_value( $license ) { // phpcs:disable // if ( 'deactivate' != $action ) return ''; // $key_length = strlen( $license['key'] ); // return str_pad( substr( $license['key'], 0, $key_length / 2 ), $key_length, '*' ); // phpcs:enable return isset( $license['key'] ) ? $license['key'] : ''; } private function license_signature_payload() { /** * Once cron runs it updates the next schedule before running * the scheduled hook, so when we call the update_license_signature * method we get the new time stamp here (wp_next_scheduled). */ $payload = $this->client->getSlug() . '||' . wp_next_scheduled( $this->schedule_hook ); return $payload . '||' . implode( '||', array_values( $this->license ) ); } private function update_license_signature() { if ( $this->license && is_array( $this->license ) ) { $payload = $this->license_signature_payload(); $signature = hash_hmac( 'sha1', $payload, $this->hash( $payload ) ); update_option( $this->option_key . '_signature', $signature, false ); } } private function get_license_signature() { remove_all_filters( "pre_option_{$this->option_key}_signature" ); return get_option( $this->option_key . '_signature', false ); } private function validate_license_signature() { $license_signature = $this->get_license_signature(); if ( ! $license_signature ) { return false; } // Validate hash. $payload = $this->license_signature_payload(); $signature = hash_hmac( 'sha1', $payload, $this->hash( $payload ) ); return hash_equals( $signature, $license_signature ); } /** * Update License Data. * Call this method without license data will deactivate the license (set empty data). * * @param array $license { * Optional. License Data. * @type string $key The License Key. * @type string $status Activation Status. * @type int $remaining Remaining Activation. * @type int $activation_limit Number of activation allowed for the license key. * @type int $expires Number of day remaining before the license expires. * } * @return bool False if value was not updated and true if value was updated. */ private function set_license( $license = [] ) { // Parse & sanitize. $this->license = $this->parse_license_data( $license ); $this->license['updated_at'] = current_time( 'mysql' ); // Update license signature. $this->update_license_signature(); // Update in db. return update_option( $this->option_key, $this->license, false ); } /** * Get Plugin/Theme License. * * @return array { * Optional. License Data. * @type string $key The License Key. * @type string $status Activation Status. * @type int $remaining Remaining Activation. * @type int $activation_limit Number of activation allowed for the license key. * @type int $expires Number of day remaining before the license expires. * } */ public function get_license() { if ( null !== $this->license ) { return $this->license; } // No hooks please... remove_all_filters( "pre_option_{$this->option_key}" ); $this->license = get_option( $this->option_key, false ); // Initialize blank inactive license data. if ( false === $this->license ) { $this->set_license(); } $this->license = $this->parse_license_data( $this->license ); return $this->license; } /** * Parse License data. * * @param array $data license data. * * @return array */ private function parse_license_data( $data = [] ) { $defaults = [ 'key' => '', // License key. 'status' => 'inactive', // Current status. 'instance' => '', // Instance unique id. // product id. 'product_id' => empty( $this->client->getProductId() ) ? $this->client->getProductId() : $this->client->getName(), 'remaining' => 0, // Remaining activation. 'activations' => 0, // Total activation. 'limit' => 0, //Activation limit. 'unlimited' => false, // Is unlimited activation. 'expires' => 0, // Expires set this to a unix timestamp [GMT]. 'updated_at' => 0, // Expires set this to a unix timestamp [GMT]. ]; // Parse. $data = wp_parse_args( $data, $defaults ); $license = []; $updated_at = strtotime( $data['updated_at'] ); if ( $updated_at ) { $updated_at = gmdate( 'Y-m-d H:i:s', $updated_at ); } // Sanitize data. $license['key'] = sanitize_text_field( $data['key'] ); $license['status'] = strtolower( $data['status'] ) === 'active' ? 'active' : 'inactive'; $license['instance'] = sanitize_text_field( $data['instance'] ); $license['product_id'] = sanitize_text_field( $data['product_id'] ); // Product id can be string too. $license['remaining'] = absint( $data['remaining'] ); $license['activations'] = absint( $data['activations'] ); $license['limit'] = absint( $data['limit'] ); $license['unlimited'] = (bool) $data['unlimited']; $license['expires'] = absint( $data['expires'] ); $license['updated_at'] = $updated_at; return $license; } /** * @param array $response * * @return array */ private function parse_license_from_request( $response ) { // WC API-Manager don't send back the remaining date. We have tricked it to do so. $status = 'inactive'; if ( isset( $response['activated'] ) && $response['activated'] ) { $status = 'active'; } elseif ( // Legacy API Manager < 2.0, and API Manager PHP Library <= 1.2. isset( $response['status_check'] ) && 'active' == $response['status_check'] ) { $status = 'active'; } return [ 'status' => $status, 'remaining' => isset( $response['data'], $response['data']['activations_remaining'] ) ? $response['data']['activations_remaining'] : 0, 'activations' => isset( $response['data'], $response['data']['total_activations'] ) ? $response['data']['total_activations'] : 0, 'limit' => isset( $response['data'], $response['data']['total_activations_purchased'] ) ? $response['data']['total_activations_purchased'] : 0, 'unlimited' => isset( $response['data'], $response['data']['unlimited_activations'] ) && $response['data']['unlimited_activations'], 'expires' => isset( $response['data'], $response['data']['expires'] ) ? $response['data']['expires'] : 0, ]; } /** * Generate a random Instance ID. * * WC-AM requires an instance id, with maximum length of 190 (instance VARCHAR(190) NOT NULL) * * @return string */ private function generateInstanceId() { if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; } return sha1( wp_generate_password( 12, false ) . microtime( true ) . home_url() ); } /** * Gets a form of `wp_hash()` specific to the plugin using license service. * * We cannot use `wp_hash()` because it is defined in `pluggable.php` which is not loaded until after plugins are loaded, * which is too late to verify the recovery mode cookie. * * This tries to use the `AUTH` salts first, but if they aren't valid specific salts will be generated and stored. * * @param string $data Data to hash. * @return string|false The hashed $data, or false on failure. * * @see wp_hash() */ private function hash( $data ) { if ( ! defined( 'AUTH_KEY' ) || AUTH_KEY === 'put your unique phrase here' ) { $auth_key = get_site_option( $this->client->getSlug() . '_license_auth_key' ); if ( ! $auth_key ) { if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; } $auth_key = wp_generate_password( 64, true, true ); update_site_option( $this->client->getSlug() . '_license_auth_key', $auth_key ); } } else { $auth_key = AUTH_KEY; } if ( ! defined( 'AUTH_SALT' ) || AUTH_SALT === 'put your unique phrase here' || AUTH_SALT === $auth_key ) { $auth_salt = get_site_option( $this->client->getSlug() . '_license_auth_salt' ); if ( ! $auth_salt ) { if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; } $auth_salt = wp_generate_password( 64, true, true ); update_site_option( $this->client->getSlug() . '_license_auth_salt', $auth_salt ); } } else { $auth_salt = AUTH_SALT; } return hash_hmac( 'sha1', $data, $auth_key . $auth_salt ); } } // End of file class-license.php.