HEX
Server: Apache/2.4.62 (Unix) OpenSSL/1.1.1k
System: Linux box12.multicloud.host 4.18.0-553.52.1.el8_10.x86_64 #1 SMP Wed May 14 09:36:12 EDT 2025 x86_64
User: kashmira (1008)
PHP: 8.1.32
Disabled: NONE
Upload Files
File: /home/kashmira/public_html/razitahir.com/wp-content/plugins/stylo-core/inc/License.php
<?php
namespace LMFW\SDK;

use ErrorException;

if (!class_exists('LMFW\SDK\License')) {
    class License {
        private $plugin_name;
        private $api_url;
        private $customer_key;
        private $customer_secret;
        private $valid_status;
        private $product_ids;
        private $stored_license;
        private $valid_object;
        private $ttl;
        private $license_options; // Added declaration for previously dynamic property

        public function __construct(
            $plugin_name,
            $server_url,
            $customer_key,
            $customer_secret,
            $product_ids,
            $license_options,
            $valid_object,
            $ttl
        ) {
            $this->plugin_name = $plugin_name;
            $this->api_url = rtrim($server_url, '/') . '/wp-json/lmfwc/v2/';
            $this->customer_key = 'ck_62c3f848a63d1e4c500026c654f97e13219d726c';
            $this->customer_secret = 'cs_bdeb1761dc5dfdb6105f975235223af689da3b94';
            $this->product_ids = is_array($product_ids) ? $product_ids : array($product_ids);
            $this->license_options = $license_options;
            $this->license_options = $license_options; // Now properly declared
            $this->valid_object = $valid_object;
            $this->ttl = $ttl;
            $this->valid_status = get_option($valid_object, array());

            // Check license validity periodically
            add_action('init', array($this, 'check_license_validity'));
        }

        public function check_license_validity() {
            $current_time = time();
            $last_check = isset($this->valid_status['nextValidation']) ? $this->valid_status['nextValidation'] : 0;
            
            // Check if 7 days have passed since last validation
            if ($current_time >= $last_check + (7 * DAY_IN_SECONDS)) {
                $license_data = get_option($this->license_options);
                
                if (!empty($license_data['license_key'])) {
                    $response = $this->retrieve($license_data['license_key']);
                    
                    if (isset($response['success']) && $response['success']) {
                        $this->valid_status['is_valid'] = 1;
                        $this->valid_status['nextValidation'] = $current_time;
                    } else {
                        $this->valid_status['is_valid'] = 0;
                        $this->valid_status['nextValidation'] = $current_time;
                    }
                    
                    update_option($this->valid_object, $this->valid_status);
                }
            }
        }

        /**
         * Enhanced API call method specifically for Cloudflare compatibility
         * 
         * @param string $endpoint API endpoint
         * @param string $method HTTP method (GET, POST, etc.)
         * @param array|string $args Request arguments
         * @return array Response data
         */
        private function call($endpoint, $method = 'GET', $args = '') {
            try {
                // Log the request for debugging
                error_log('License API request to: ' . $this->api_url . $endpoint);
                
                // Get site information for headers
                $site_url = site_url();
                $domain = parse_url($site_url, PHP_URL_HOST);
                
                // Build base URL with authentication as query parameters
                $url = add_query_arg(
                    array(
                        'consumer_key' => $this->customer_key,
                        'consumer_secret' => $this->customer_secret
                    ),
                    $this->api_url . $endpoint
                );

                // Get the client IP for Cloudflare headers
                $client_ip = $this->get_client_ip();
                
                // Set up request arguments with extensive Cloudflare-friendly headers
                // These mimic a legitimate browser request which helps bypass Cloudflare protection
                $headers = array(
                    // Standard content headers
                    'Accept' => 'application/json, text/plain, */*',
                    'Accept-Language' => 'en-US,en;q=0.9',
                    'Content-Type' => 'application/json; charset=UTF-8',
                    
                    // Identification headers
                    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
                    'Origin' => $site_url,
                    'Referer' => $site_url,
                    'X-Requested-With' => 'XMLHttpRequest',
                    
                    // Cloudflare specific headers
                    'CF-Connecting-IP' => $client_ip,
                    'X-Forwarded-For' => $client_ip,
                    'X-Real-IP' => $client_ip,
                    
                    // Cache control
                    'Cache-Control' => 'no-cache',
                    'Pragma' => 'no-cache',
                    
                    // Connection handling
                    'Connection' => 'keep-alive',
                    
                    // Custom headers for identification
                    'X-WP-Domain' => $domain,
                    'X-StyloThemes-License' => 'License-Validation-Request-' . md5($site_url),
                    'X-License-Request-Domain' => $domain
                );

                $wp_args = array(
                    'headers' => $headers,
                    'method' => $method,
                    'timeout' => 30,    // Increased timeout
                    'sslverify' => true,
                    'httpversion' => '1.1',
                    'redirection' => 5,
                    'cookies' => array(),
                    'body' => null      // Will be set below if needed
                );

                // Handle arguments based on request method
                if (!empty($args)) {
                    if ($method === 'GET') {
                        // For GET, add args as query parameters
                        $url = add_query_arg($args, $url);
                    } else {
                        // For other methods, encode args as JSON in the body
                        $wp_args['body'] = json_encode($args);
                    }
                }

                // Log the final request for debugging
                error_log('Making license request to: ' . $url);
                error_log('Request headers: ' . json_encode($headers));
                
                // Make the request with standard WordPress HTTP API
                $response = wp_remote_request($url, $wp_args);

                // Check for initial errors
                if (is_wp_error($response)) {
                    error_log('License API request failed: ' . $response->get_error_message());
                    
                    // Fallback #1: Try with cURL directly if available
                    if (function_exists('curl_init')) {
                        error_log('Attempting direct cURL fallback...');
                        $curl_response = $this->curl_fallback_request($url, $method, $args, $headers);
                        if ($curl_response !== false) {
                            $response = $curl_response;
                        } else {
                            // Fallback #2: Try with modified args
                            error_log('Attempting with modified request args...');
                            $alternative_args = $wp_args;
                            $alternative_args['timeout'] = 45;
                            $alternative_args['sslverify'] = false; // Try without SSL verification as a fallback
                            $alternative_args['headers']['User-Agent'] = 'WordPress/' . get_bloginfo('version');
                            
                            // Try alternative request
                            $response = wp_remote_request($url, $alternative_args);
                            
                            if (is_wp_error($response)) {
                                error_log('All request methods failed: ' . $response->get_error_message());
                                return array(
                                    'success' => false,
                                    'message' => 'Connection failed: ' . $response->get_error_message()
                                );
                            }
                        }
                    } else {
                        // If cURL is not available, try the alternative args
                        error_log('cURL not available, trying alternative args...');
                        $alternative_args = $wp_args;
                        $alternative_args['timeout'] = 45;
                        $alternative_args['sslverify'] = false;
                        $alternative_args['headers']['User-Agent'] = 'WordPress/' . get_bloginfo('version');
                        
                        $response = wp_remote_request($url, $alternative_args);
                        
                        if (is_wp_error($response)) {
                            error_log('Alternative request failed: ' . $response->get_error_message());
                            return array(
                                'success' => false,
                                'message' => 'Connection failed: ' . $response->get_error_message()
                            );
                        }
                    }
                }

                // Get response code
                $response_code = wp_remote_retrieve_response_code($response);
                $response_body = wp_remote_retrieve_body($response);
                $response_headers = wp_remote_retrieve_headers($response);
                
                // Log response information
                error_log('License API response code: ' . $response_code);
                error_log('License API response headers: ' . json_encode($response_headers));
                error_log('License API response: ' . substr($response_body, 0, 500) . (strlen($response_body) > 500 ? '...' : ''));

                // Handle common HTTP errors
                if ($response_code !== 200) {
                    switch ($response_code) {
                        case 403:
                            // Try one more approach for 403 errors - make a browser-like request
                            error_log('Received 403, attempting browser-like fallback request...');
                            $browser_response = $this->browser_like_fallback($url, $method, $args);
                            if ($browser_response !== false) {
                                // Process successful browser-like response
                                return $browser_response;
                            }
                            
                            return array(
                                'success' => false,
                                'message' => 'Access denied (403). This may be due to Cloudflare protection or API restrictions.'
                            );
                        case 429:
                            return array(
                                'success' => false,
                                'message' => 'Too many requests (429). Please try again later.'
                            );
                        case 500:
                        case 502:
                        case 503:
                        case 504:
                            return array(
                                'success' => false,
                                'message' => 'Server temporarily unavailable (' . $response_code . '). Please try again later.'
                            );
                        default:
                            return array(
                                'success' => false,
                                'message' => 'HTTP Error: ' . $response_code
                            );
                    }
                }

                // Get response body
                if (empty($response_body)) {
                    return array(
                        'success' => false,
                        'message' => 'Empty response received'
                    );
                }

                // Parse JSON response
                $data = json_decode($response_body, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log('JSON parse error: ' . json_last_error_msg());
                    error_log('Raw response: ' . $response_body);
                    return array(
                        'success' => false,
                        'message' => 'Invalid response format: ' . json_last_error_msg()
                    );
                }

                return $data;

            } catch (Exception $e) {
                error_log('Exception in license API call: ' . $e->getMessage());
                return array(
                    'success' => false,
                    'message' => 'Request error: ' . $e->getMessage()
                );
            }
        }

        /**
         * Direct cURL fallback for when WP HTTP API fails
         * This bypasses WordPress and makes a direct cURL request
         */
        private function curl_fallback_request($url, $method, $args, $headers) {
            if (!function_exists('curl_init')) {
                return false;
            }
            
            try {
                error_log('Executing direct cURL request to: ' . $url);
                
                $ch = curl_init();
                
                // Format headers for cURL
                $curl_headers = array();
                foreach ($headers as $key => $value) {
                    $curl_headers[] = $key . ': ' . $value;
                }
                
                // Set basic cURL options
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                curl_setopt($ch, CURLOPT_TIMEOUT, 30);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
                curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_headers);
                
                // Set method-specific options
                if ($method === 'POST') {
                    curl_setopt($ch, CURLOPT_POST, true);
                    if (!empty($args)) {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
                    }
                } else if ($method !== 'GET') {
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
                    if (!empty($args) && is_array($args)) {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
                    }
                }
                
                // Execute request and get response
                $response_body = curl_exec($ch);
                $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                $curl_error = curl_error($ch);
                
                curl_close($ch);
                
                if (!empty($curl_error)) {
                    error_log('cURL error: ' . $curl_error);
                    return false;
                }
                
                error_log('cURL response code: ' . $response_code);
                
                // Format response like wp_remote_request
                $response = array(
                    'body' => $response_body,
                    'response' => array(
                        'code' => $response_code
                    ),
                    'headers' => array()
                );
                
                return $response;
            } catch (Exception $e) {
                error_log('cURL fallback exception: ' . $e->getMessage());
                return false;
            }
        }

        /**
         * Fallback specifically for Cloudflare 403 errors
         * Makes a request that closely mimics a real browser
         */
        private function browser_like_fallback($url, $method, $args) {
            if (!function_exists('curl_init')) {
                return false;
            }
            
            try {
                error_log('Executing browser-like fallback request to bypass Cloudflare');
                
                $ch = curl_init();
                
                // Set a very browser-like User-Agent
                $browser_headers = array(
                    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
                    'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                    'Accept-Language: en-US,en;q=0.9',
                    'Cache-Control: no-cache',
                    'Connection: keep-alive',
                    'Upgrade-Insecure-Requests: 1',
                    'DNT: 1',
                    'Content-Type: application/json; charset=UTF-8'
                );
                
                // Extract base URL for the Origin and Referer headers
                $parsed_url = parse_url($url);
                $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
                
                $browser_headers[] = 'Origin: ' . $base_url;
                $browser_headers[] = 'Referer: ' . $base_url;
                
                // Basic cURL options optimized for Cloudflare
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                curl_setopt($ch, CURLOPT_TIMEOUT, 45);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
                curl_setopt($ch, CURLOPT_HTTPHEADER, $browser_headers);
                
                // Enable cookies to handle Cloudflare session cookies
                $cookie_file = tmpfile();
                $cookie_path = stream_get_meta_data($cookie_file)['uri'];
                curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_path);
                curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_path);
                
                // Set method-specific options
                if ($method === 'POST') {
                    curl_setopt($ch, CURLOPT_POST, true);
                    if (!empty($args)) {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
                    }
                } else if ($method !== 'GET') {
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
                    if (!empty($args) && is_array($args)) {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
                    }
                }
                
                // Execute request and get response
                $response_body = curl_exec($ch);
                $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                $curl_error = curl_error($ch);
                
                curl_close($ch);
                fclose($cookie_file); // Clean up the temporary cookie file
                
                if (!empty($curl_error)) {
                    error_log('Browser-like fallback cURL error: ' . $curl_error);
                    return false;
                }
                
                error_log('Browser-like fallback response code: ' . $response_code);
                
                if ($response_code !== 200) {
                    return false;
                }
                
                // Try to parse the response as JSON
                $data = json_decode($response_body, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log('Browser-like fallback JSON parse error: ' . json_last_error_msg());
                    error_log('Raw response: ' . substr($response_body, 0, 500));
                    return false;
                }
                
                return $data;
            } catch (Exception $e) {
                error_log('Browser-like fallback exception: ' . $e->getMessage());
                return false;
            }
        }

        /**
         * Get client IP with proxy support
         */
        private function get_client_ip() {
            $ip_headers = array(
                'HTTP_CF_CONNECTING_IP', // Cloudflare
                'HTTP_X_REAL_IP',       // Nginx proxy
                'HTTP_CLIENT_IP',        // Direct client IP
                'HTTP_X_FORWARDED_FOR',  // Forward proxy
                'HTTP_X_FORWARDED',      // Forward proxy
                'HTTP_FORWARDED_FOR',    // Forward proxy
                'HTTP_FORWARDED',        // Forward proxy
                'REMOTE_ADDR'            // Fallback
            );

            foreach ($ip_headers as $header) {
                if (!empty($_SERVER[$header])) {
                    // If it's a list of IPs, take the first one
                    $ip = trim(explode(',', $_SERVER[$header])[0]);
                    if (filter_var($ip, FILTER_VALIDATE_IP)) {
                        return $ip;
                    }
                }
            }

            return '';
        }

        public function license_info($license_key) {
            if (!empty($license_key)) {
                return $this->call("licenses/{$license_key}");
            }
            return array();
        }

        public function retrieve($license_key) {
            if (!empty($license_key)) {
                $response = $this->call("licenses/validate/{$license_key}");
                return $response;
            }
            return array('success' => false);
        }

        /**
         * Enhanced license activation method with no email requirement
         * 
         * @param string $customer_email Customer email address (can be empty)
         * @param string $order_id Order ID
         * @param string $license_key License key to activate
         * @return mixed String message on success, false on failure
         */
        public function activate($customer_email, $order_id, $license_key) {
            // Log activation attempt
            error_log("License activation attempt - Order ID: {$order_id}, Key: {$license_key}");
            
            // Validate required parameters
            if (empty($order_id) || empty($license_key)) {
                return 'Missing required information (Order ID or License Key)';
            }

            // Get site information
            $site_url = site_url();
            $domain = parse_url($site_url, PHP_URL_HOST);
            
            // First check if the license key is valid
            $license_check = $this->retrieve($license_key);
            
            error_log("License validation response: " . print_r($license_check, true));
            
            // Validate the license check response
            if (empty($license_check) || !isset($license_check['success']) || !$license_check['success']) {
                $error_message = isset($license_check['message']) ? $license_check['message'] : 'Unknown error validating license';
                return 'License validation failed: ' . $error_message;
            }
            
            // Validate license matches order info
            if (!empty($license_check['data'])) {
                $license_data = $license_check['data'];
                
                // Basic validation - the license should be associated with the provided order ID
                $api_order_id = isset($license_data['orderId']) ? $license_data['orderId'] : '';
                
                if (!empty($api_order_id) && $api_order_id != $order_id) {
                    return 'Order ID does not match the license key';
                }
                
                // Verify the license key matches
                $api_license_key = isset($license_data['licenseKey']) ? $license_data['licenseKey'] : '';
                if (empty($api_license_key) || $api_license_key != $license_key) {
                    return 'Invalid license key';
                }
                
                // Check if license is already activated for this site
                $existing_activation = get_option($this->valid_object, array());
                $existing_token = isset($existing_activation['activation_data']['token']) ? 
                    $existing_activation['activation_data']['token'] : '';
                
                // Check activation data from license check
                $activation_data = isset($license_data['activationData']) ? $license_data['activationData'] : array();
                $already_activated = false;
                $instance_id = null;
                
                // Look for existing activation for this site
                if (!empty($activation_data)) {
                    foreach ($activation_data as $activation) {
                        // Check if this activation matches our site
                        if (isset($activation['instance']) && 
                            (strpos($activation['instance'], $domain) !== false || 
                            strpos($domain, $activation['instance']) !== false)) {
                            $already_activated = true;
                            $instance_id = isset($activation['token']) ? $activation['token'] : null;
                            break;
                        }
                        
                        // Also check by token if we have one
                        if (!empty($existing_token) && isset($activation['token']) && $activation['token'] === $existing_token) {
                            $already_activated = true;
                            $instance_id = $existing_token;
                            break;
                        }
                    }
                }
                
                // Prepare activation request
                $request_args = array(
                    'label' => $site_url,
                    'instance' => $domain,
                );
                
                // If we already have a token for this site, include it
                if ($already_activated && !empty($instance_id)) {
                    $request_args['token'] = $instance_id;
                    error_log("Site already activated with token: {$instance_id}");
                }
                
                // Make the activation request
                $response = $this->call("licenses/activate/{$license_key}", 'GET', $request_args);
                
                error_log("Activation response: " . print_r($response, true));
                
                // Process response
                if (isset($response['success']) && $response['success']) {
                    // Extract activation data from the response
                    $license = $response['data'];
                    $activation_info = null;
                    
                    // Find our activation in the response
                    if (!empty($license['activationData'])) {
                        // Try to find our specific activation by domain
                        foreach ($license['activationData'] as $activation) {
                            if (isset($activation['instance']) && 
                                (strpos($activation['instance'], $domain) !== false || 
                                strpos($domain, $activation['instance']) !== false)) {
                                $activation_info = $activation;
                                break;
                            }
                            
                            // If we have a token, also check by that
                            if (isset($request_args['token']) && 
                                isset($activation['token']) && 
                                $activation['token'] === $request_args['token']) {
                                $activation_info = $activation;
                                break;
                            }
                        }
                        
                        // If we couldn't find a specific match, use the last activation
                        // (assuming it's ours if we just activated it)
                        if (empty($activation_info) && count($license['activationData']) > 0) {
                            $activation_info = end($license['activationData']);
                        }
                        
                        // Store activation data
                        if (!empty($activation_info)) {
                            $this->valid_status['is_valid'] = 1;
                            $this->valid_status['activation_data'] = $activation_info;
                            $this->valid_status['nextValidation'] = time();
                            $this->valid_status['valid_until'] = isset($license['expiresAt']) ? strtotime($license['expiresAt']) : null;
                            
                            update_option($this->valid_object, $this->valid_status);
                            
                            return 'License Key Activated Successfully';
                        }
                    }
                    
                    // If we couldn't extract specific activation data but the call was successful
                    $this->valid_status['is_valid'] = 1;
                    $this->valid_status['nextValidation'] = time();
                    update_option($this->valid_object, $this->valid_status);
                    
                    return 'License Key Activated';
                }
                
                // Handle errors
                $error_message = isset($response['message']) ? $response['message'] : 'Unknown activation error';
                
                // Check for specific error conditions
                if (stripos($error_message, 'exceeded') !== false || stripos($error_message, 'limit') !== false) {
                    return 'Activation limit exceeded. Please deactivate the license on another site first.';
                }
                
                return 'Activation failed: ' . $error_message;
            }
            
            return 'Invalid License Key or Order ID';
        }

        public function deactivate($license_key) {
            if (empty($license_key)) {
                return 'License not found';
            }

            $existing_activation = get_option('stylo_license_status');
            if (!empty($existing_activation['activation_data']['token'])) {
                $token = $existing_activation['activation_data']['token'];
                $response = $this->call("licenses/deactivate/{$license_key}", 'GET', array('token' => $token));

                if (isset($response['success']) && $response['success']) {
                    $this->valid_status['is_valid'] = 0;
                    $this->valid_status['nextValidation'] = time();
                    update_option($this->valid_object, $this->valid_status);
                    return 'License Key Deactivated';
                }
            }
            return 'No active license found';
        }

        public function valid_until() {
            return isset($this->valid_status['valid_until']) ? $this->valid_status['valid_until'] : null;
        }

		/**
     * Force check license validity (for testing)
     * @return array Status of the check
     */
    public function force_check_validity() {
        $license_data = get_option($this->license_options);
        $current_time = time();
        $status = array(
            'previous_status' => $this->valid_status,
            'check_time' => date('Y-m-d H:i:s', $current_time),
            'last_check' => isset($this->valid_status['nextValidation']) ? 
                date('Y-m-d H:i:s', $this->valid_status['nextValidation']) : 'Never',
        );
        
        if (!empty($license_data['license_key'])) {
            $response = $this->retrieve($license_data['license_key']);
            
            if (isset($response['success']) && $response['success']) {
                $this->valid_status['is_valid'] = 1;
                $this->valid_status['nextValidation'] = $current_time;
                $this->valid_status['last_check_response'] = $response;
            } else {
                $this->valid_status['is_valid'] = 0;
                $this->valid_status['nextValidation'] = $current_time;
                $this->valid_status['last_check_response'] = $response;
            }
            
            update_option($this->valid_object, $this->valid_status);
            $status['new_status'] = $this->valid_status;
            $status['server_response'] = $response;
        } else {
            $status['error'] = 'No license key found';
        }
        
        return $status;
    }

    /**
     * Get the next scheduled validation time
     * @return string Formatted date and time
     */
    public function get_next_validation_time() {
        if (isset($this->valid_status['nextValidation'])) {
            $next_check = $this->valid_status['nextValidation'] + (7 * DAY_IN_SECONDS);
            return array(
                'next_check' => date('Y-m-d H:i:s', $next_check),
                'time_remaining' => $next_check - time(),
                'current_status' => !empty($this->valid_status['is_valid']) ? 'Valid' : 'Invalid'
            );
        }
        return array(
            'next_check' => 'Not set',
            'time_remaining' => 0,
            'current_status' => 'Unknown'
        );
    }
    }
}