diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43e3edcf4..b38bd4b03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,19 +8,36 @@ on: - 'v*.*' pull_request: +# Cancels all previous workflow runs for pull requests that have not completed. +# concurrency: +# group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} +# cancel-in-progress: true + jobs: - test: + check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + # Initialize Cache Composer + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: .cache + key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} + + # Initialize Node.js - uses: actions/setup-node@v3 with: - node-version: '20' + node-version: '22' check-latest: true cache: npm cache-dependency-path: package-lock.json - - run: npm ci + # Start actions + - run: mkdir -p .cache + - run: composer install + - run: composer run sniff-check + - run: npm ci - run: npm run format-check diff --git a/.gitignore b/.gitignore index 5ac2a3cc3..e656415ed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .env* .vscode node_modules +/vendor diff --git a/.prettierignore b/.prettierignore index c5ec21291..32f938100 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,12 @@ **/*.min.* **/*.ori.* **/*.tpl.php +composer.json +composer.lock +package.json +package-lock.json +vendor +.cache assets/js/css_async.js assets/js/webfontloader.js lib diff --git a/.prettierrc b/.prettierrc index 09fdb315e..369199fce 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,14 +2,20 @@ "overrides": [ { "files": ".prettierrc", - "options": { "parser": "json" } + "options": { + "parser": "json" + } }, { "files": "**/*.{yml,yaml}", - "options": { "tabWidth": 2 } + "options": { + "tabWidth": 2 + } } ], - "plugins": ["@prettier/plugin-php"], + "plugins": [ + "@prettier/plugin-php" + ], "arrowParens": "avoid", "braceStyle": "per-cs", "endOfLine": "auto", @@ -21,4 +27,4 @@ "trailingComma": "all", "trailingCommaPHP": true, "useTabs": true -} +} \ No newline at end of file diff --git a/assets/css/litespeed.css b/assets/css/litespeed.css index 0269e3c52..84703e5c6 100644 --- a/assets/css/litespeed.css +++ b/assets/css/litespeed.css @@ -1208,6 +1208,10 @@ h3 .litespeed-learn-more { position: relative; } +.rtl .litespeed-switch { + flex-direction: row-reverse; +} + .litespeed-switch input:checked:active + label { box-shadow: 0 2px 0 rgba(27, 146, 146, 0.7), @@ -2805,6 +2809,7 @@ g.litespeed-pie_info .litespeed-pie-done { .litespeed-dashboard-stats-wrapper { display: flex; + position: relative; } .litespeed-dashboard-stats-wrapper .litespeed-postbox { diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..79acec1ff --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "name": "litespeedtech/lscache_wp", + "require-dev": { + "squizlabs/php_codesniffer": "*", + "phpcompatibility/php-compatibility": "*" + }, + "prefer-stable": true, + "scripts": { + "post-install-cmd": "phpcs --config-set installed_paths vendor/phpcompatibility/php-compatibility", + "post-update-cmd": "phpcs --config-set installed_paths vendor/phpcompatibility/php-compatibility", + "sniff-check": "phpcs --standard=./phpcs.xml.dist" + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock new file mode 100644 index 000000000..39c17ed3a --- /dev/null +++ b/composer.lock @@ -0,0 +1,161 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "21330dd959c1642a4c7dbc91aa5effef", + "packages": [], + "packages-dev": [ + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.10.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-07-21T23:26:44+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/data/const.default.ini b/data/const.default.ini index c59645ecc..b7168bec2 100644 --- a/data/const.default.ini +++ b/data/const.default.ini @@ -13,9 +13,6 @@ ; O_AUTO_UPGRADE auto_upgrade = false -; O_API_KEY -api_key = '' - ; O_SERVER_IP server_ip = '' diff --git a/litespeed-cache.php b/litespeed-cache.php index e7324a99c..42e7656d5 100644 --- a/litespeed-cache.php +++ b/litespeed-cache.php @@ -4,7 +4,7 @@ * Plugin Name: LiteSpeed Cache * Plugin URI: https://www.litespeedtech.com/products/cache-plugins/wordpress-acceleration * Description: High-performance page caching and site optimization from LiteSpeed - * Version: 6.5.0.2 + * Version: 7.0-a7 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '6.5.0.2'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a7'); !defined('LSCWP_CONTENT_DIR') && define('LSCWP_CONTENT_DIR', WP_CONTENT_DIR); !defined('LSCWP_DIR') && define('LSCWP_DIR', __DIR__ . '/'); // Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU @@ -174,13 +174,13 @@ function wp_create_nonce_litespeed_esi($action = -1) if (!function_exists('run_litespeed_cache')) { function run_litespeed_cache() { - //Check minimum PHP requirements, which is 5.3 at the moment. - if (version_compare(PHP_VERSION, '5.3.0', '<')) { + //Check minimum PHP requirements, which is 7.2 at the moment. + if (version_compare(PHP_VERSION, '7.2.0', '<')) { return; } - //Check minimum WP requirements, which is 4.9 at the moment. - if (version_compare($GLOBALS['wp_version'], '4.9', '<')) { + //Check minimum WP requirements, which is 5.3 at the moment. + if (version_compare($GLOBALS['wp_version'], '5.3', '<')) { return; } diff --git a/package.json b/package.json index 85ef1dede..92a0a3532 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "description": "High-performance page caching and site optimization from LiteSpeed", "license": "GPLv3", "scripts": { - "format": "prettier --write . '**/*.php'", - "format-check": "prettier --check . '**/*.php'" + "format": "prettier --write . '**/*.{js,css,php}'", + "format-check": "prettier --check . '**/*.{js,css,php}'" }, "devDependencies": { "@prettier/plugin-php": "^0.21.0", diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 000000000..32d688516 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,62 @@ + + + Apply LiteSpeed Cache Coding Standards to all plugin files + + + + + + + + + + + + + + + + + + + + + + + + . + + + ^wordpress/* + + + /node_modules/* + /vendor/* + + + + + + + + + + + + diff --git a/readme.txt b/readme.txt index 681b7800e..dc6fa22b4 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,9 @@ === LiteSpeed Cache === Contributors: LiteSpeedTech Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cache, redis, memcached, database cleaner -Requires at least: 4.9 -Tested up to: 6.6.1 +Requires at least: 5.3 +Requires PHP: 7.2 +Tested up to: 6.6 Stable tag: 6.5.0.2 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl.html @@ -48,7 +49,7 @@ LiteSpeed Cache for WordPress is compatible with ClassicPress. * Single Site and Multisite (Network) support * Import/Export settings * Attractive, easy-to-understand interface -* WebP image format support +* AVIF/WebP image format support * Heartbeat control + This service is not provided by the LSCache plugin, nor is it guaranteed to be installed by your service provider. However, the plugin is compatible with the service if it is in use on your site. @@ -254,6 +255,14 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == += 7.0 - Nov 2024 = +* 🌱**Image Optimization** Added AVIF format. +* **Core** Minimum required PHP version escalated to PHP v7.2.0. +* **Core** Minimum required WP version escalated to WP v5.3. +* **Cloud** Dropped `Domain Key`. Used sodium encryption for authentication and validation. +* **GUI** Switch buttons rtl compatibility. (Eliza/Mehrshad Darzi #603) +* **Misc** Improved readme file by adding min supported PHP/WP versions. (Viktor Szépe) + = 6.5.0.2 - Sep 6 2024 = * **Debug** Compatibility improvement for WP installations w/o `AUTH_KEY` defined in `wp-config.php`. @@ -278,6 +287,23 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro * **3rd** Correct the integration with User Switching. (John Blackbourn #725) * **3rd** Fixed Admin Bar Missing issue on DIVI + Elementor frontend. (thyran/robertstaddon PR#727) += 6.4.2 - Sep 3 2024 = +* **Debug** Moved debug log to litespeed individual folder `/wp-content/litespeed/debug/`. +* **Debug** Disallowed visits to `/litespeed/debug/` folder log files in .htaccess. +* **Debug** Dropped const `LSCWP_DEBUG_PATH` support. +* **Debug** Renamed `debug.purge.log` to `purge.log`. +* **Debug** Added dummy `index.php` for debug folder. +* **Debug** Used random string for log filenames. +* **Debug** Removed cookies-related info. (Thanks to Rafie) +* **Debug** Dropped `Log Cookies` option. +* **Report** Escaped report content to protect it from potential XSS attack. (Islam R alsaid #505746) +* **ESI** Added nonce for Advanced Custom Fields + Advanced Forms. (David Lapointe Gilbert #439) +* **Purge** Run ACTION_PURGE_EMPTYCACHE even if cache is disabled in network admin. (Philip #453) +* **Page Optimize** Disable UCSS exclusion when UCSS is inactived. (#640) +* **3rd** Fixed undefined warning in WooCommerce Widgets. (Lolosan #719) +* **3rd** Correct the integration with User Switching. (John Blackbourn #725) +* **3rd** Fixed Admin Bar Missing issue on DIVI + Elementor frontend. (thyran/robertstaddon PR#727) + = 6.4.1 - Aug 19 2024 = * ❗**Security** This release patches a security issue that may affect previous LSCWP versions since v1.9. * 🐞**Page Optimize** Fixed HTML minification returning blank page issue. (#706) diff --git a/src/base.cls.php b/src/base.cls.php index 48b8f61f2..590f151a3 100644 --- a/src/base.cls.php +++ b/src/base.cls.php @@ -24,7 +24,7 @@ class Base extends Root const _VER = '_version'; // Not set-able const HASH = 'hash'; // Not set-able const O_AUTO_UPGRADE = 'auto_upgrade'; - const O_API_KEY = 'api_key'; + const O_API_KEY = 'api_key'; // Deprecated since v6.4. TODO: Will drop after v6.5 const O_SERVER_IP = 'server_ip'; const O_GUEST = 'guest'; const O_GUEST_OPTM = 'guest_optm'; @@ -273,8 +273,8 @@ class Base extends Root const O_CDN_CLOUDFLARE_ZONE = 'cdn-cloudflare_zone'; const O_CDN_MAPPING = 'cdn-mapping'; const O_CDN_ATTR = 'cdn-attr'; - const O_QC_TOKEN = 'qc-token'; const O_QC_NAMESERVERS = 'qc-nameservers'; + const O_QC_CNAME = 'qc-cname'; const NETWORK_O_USE_PRIMARY = 'use_primary_settings'; @@ -307,7 +307,6 @@ class Base extends Root /* Site related options (Will not overwrite other sites' config) */ protected static $SINGLE_SITE_OPTIONS = array( - self::O_API_KEY, self::O_CRAWLER, self::O_CRAWLER_SITEMAP, self::O_CRAWLER_DROP_DOMAIN, @@ -323,15 +322,14 @@ class Base extends Root self::O_CDN_CLOUDFLARE_ZONE, self::O_CDN_MAPPING, self::O_CDN_ATTR, - self::O_QC_TOKEN, self::O_QC_NAMESERVERS, + self::O_QC_CNAME, ); protected static $_default_options = array( self::_VER => '', self::HASH => '', self::O_AUTO_UPGRADE => false, - self::O_API_KEY => '', self::O_SERVER_IP => '', self::O_GUEST => false, self::O_GUEST_OPTM => false, @@ -549,8 +547,8 @@ class Base extends Root self::O_CDN_MAPPING => array(), self::O_CDN_ATTR => array(), - self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '', + self::O_QC_CNAME => '', ); protected static $_default_site_options = array( @@ -604,6 +602,7 @@ class Base extends Root protected static $_multi_switch_list = array( self::O_DEBUG => 2, self::O_OPTM_JS_DEFER => 2, + self::O_IMG_OPTM_WEBP => 2, ); /** @@ -890,7 +889,7 @@ protected function _conf_purge_all($id) */ protected function _conf_pswd($id) { - $check_ids = array(self::O_CDN_CLOUDFLARE_KEY, self::O_OBJECT_PSWD, self::O_API_KEY, self::O_QC_TOKEN); + $check_ids = array(self::O_CDN_CLOUDFLARE_KEY, self::O_OBJECT_PSWD); return in_array($id, $check_ids); } diff --git a/src/cdn-setup.cls.php b/src/cdn-setup.cls.php index 789ffd558..004e6ecb0 100644 --- a/src/cdn-setup.cls.php +++ b/src/cdn-setup.cls.php @@ -150,10 +150,10 @@ private function _process_cdn_status($result) $this->_summary['cdn_setup_done_ts'] = time(); $this->_setup_token = ''; - $this->cls('Conf')->update_confs(array(self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '')); + $this->cls('Conf')->update_confs(array(self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '', self::O_QC_CNAME => '')); } elseif (isset($result['_msg'])) { $notice = esc_html($result['_msg']); - if ($this->conf(Base::O_QC_NAMESERVERS)) { + if ($this->conf(Base::O_QC_NAMESERVERS) || $this->conf(Base::O_QC_CNAME)) { $this->_summary['cdn_verify_msg'] = $notice; $notice = array('cdn_verify_msg' => $notice); } @@ -222,7 +222,7 @@ private function _qc_reset($delete) self::save_summary($this->_summary, false, true); $this->_setup_token = ''; - $this->cls('Conf')->update_confs(array(self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '', self::O_CDN_QUIC => false)); + $this->cls('Conf')->update_confs(array(self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '', self::O_QC_CNAME => '', self::O_CDN_QUIC => false)); $msg = ''; if ($delete) { $msg = __( diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 7c3e7a9c2..f23a0776e 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -13,11 +13,13 @@ class Cloud extends Base { const LOG_TAG = '❄️'; - const CLOUD_SERVER = 'https://api.quic.cloud'; + const CLOUD_SERVER = 'https://api.preview.quic.cloud'; const CLOUD_IPS = 'https://quic.cloud/ips'; - const CLOUD_SERVER_DASH = 'https://my.quic.cloud'; + const CLOUD_SERVER_DASH = 'https://my.preview.quic.cloud'; const CLOUD_SERVER_WP = 'https://wpapi.quic.cloud'; + const SVC_U_ACTIVATE = 'u/wp3/activate'; + const SVC_U_LINK = 'u/wp3/link'; const SVC_D_NODES = 'd/nodes'; const SVC_D_SYNC_CONF = 'd/sync_conf'; const SVC_D_USAGE = 'd/usage'; @@ -33,24 +35,23 @@ class Cloud extends Base const SVC_HEALTH = 'health'; const SVC_CDN = 'cdn'; - const BM_IMG_OPTM_PRIO = 16; - const BM_IMG_OPTM_JUMBO_GROUP = 32; - const IMG_OPTM_JUMBO_GROUP = 1000; const IMG_OPTM_DEFAULT_GROUP = 200; const IMGOPTM_TAKEN = 'img_optm-taken'; const TTL_NODE = 3; // Days before node expired const EXPIRATION_REQ = 300; // Seconds of min interval between two unfinished requests - const EXPIRATION_TOKEN = 900; // Min intval to request a token 15m const TTL_IPS = 3; // Days for node ip list cache const API_REPORT = 'wp/report'; const API_NEWS = 'news'; const API_VER = 'ver_check'; const API_BETA_TEST = 'beta_test'; + const API_REST_ECHO = 'tool/wp_rest_echo'; + const API_SERVER_KEY_SIGN = 'key_sign'; private static $CENTER_SVC_SET = array( + self::SVC_U_ACTIVATE, self::SVC_D_NODES, self::SVC_D_SYNC_CONF, self::SVC_D_USAGE, @@ -62,10 +63,10 @@ class Cloud extends Base self::SVC_D_DEL_CDN_DNS, ); - private static $WP_SVC_SET = array(self::API_NEWS, self::API_VER, self::API_BETA_TEST); + private static $WP_SVC_SET = array(self::API_NEWS, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO); // No api key needed for these services - private static $_PUB_SVC_SET = array(self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST); + private static $_PUB_SVC_SET = array(self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO); private static $_QUEUE_SVC_SET = array(self::SVC_UCSS, self::SVC_VPI); @@ -92,11 +93,11 @@ class Cloud extends Base const TYPE_CLEAR_PROMO = 'clear_promo'; const TYPE_REDETECT_CLOUD = 'redetect_cloud'; const TYPE_CLEAR_CLOUD = 'clear_cloud'; - const TYPE_GEN_KEY = 'gen_key'; + const TYPE_ACTIVATE = 'activate'; const TYPE_LINK = 'link'; const TYPE_SYNC_USAGE = 'sync_usage'; + const TYPE_RESET = 'reset'; - private $_setup_token; protected $_summary; /** @@ -106,17 +107,289 @@ class Cloud extends Base */ public function __construct() { - $this->_setup_token = $this->conf(self::O_QC_TOKEN); $this->_summary = self::get_summary(); } /** - * Get api key from conf - * @since 5.3 + * Init QC setup + * + * @since 7.0 + */ + public function init_qc() + { + if (empty($this->_summary['sk_b64'])) { + $keypair = sodium_crypto_sign_keypair(); + $pk = base64_encode(sodium_crypto_sign_publickey($keypair)); + $sk = base64_encode(sodium_crypto_sign_secretkey($keypair)); + $this->_summary['pk_b64'] = $pk; + $this->_summary['sk_b64'] = $sk; + $this->save_summary(); + // ATM `qc_activated` = null + } + + // WPAPI REST echo dryrun + $req_data = array( + 'wp_pk_b64' => $this->_summary['pk_b64'], + ); + $echobox = self::post(self::API_REST_ECHO, $req_data); + if ($echobox === false) { + self::debugErr('REST Echo Failed!'); + $msg = __('Your WP REST API seems blocked our QIUC.cloud server calls.', 'litespeed-cache'); + Admin_Display::error($msg); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); + return; + } + + self::debug("echo succeeded"); + + // Load seperate thread echoed data from storage + if (empty($echobox['wpapi_ts']) || empty($echobox['wpapi_signature_b64'])) { + Admin_Display::error(__('Failed to get echo data from WPAPI', 'litespeed-cache')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); + return; + } + + $data = array( + 'wp_pk_b64' => $this->_summary['pk_b64'], + 'wpapi_ts' => $echobox['wpapi_ts'], + 'wpapi_signature_b64' => $echobox['wpapi_signature_b64'], + ); + $server_ip = $this->conf(self::O_SERVER_IP); + if ($server_ip) { + $data['server_ip'] = $server_ip; + } + + // Activation redirect + $param = array( + 'site_url' => home_url(), + 'ver' => Core::VER, + 'data' => $data, + 'ref' => get_admin_url(null, 'admin.php?page=litespeed'), + ); + wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . urlencode(Utility::arr2str($param))); + exit(); + } + + /** + * Link to QC setup + * + * @since 7.0 */ - private function _api_key() + public function link_qc() { - return $this->conf(self::O_API_KEY); + if (!$this->activated()) { + Admin_Display::error(__('You need to activate QC first.', 'litespeed-cache')); + return; + } + + + $data = array( + 'wp_ts' => time(), + ); + $data['wp_signature_b64'] = $this->_sign_b64($data['wp_ts']); + + // Activation redirect + $param = array( + 'site_url' => home_url(), + 'ver' => Core::VER, + 'data' => $data, + 'ref' => get_admin_url(null, 'admin.php?page=litespeed'), + ); + wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_LINK . '?data=' . urlencode(Utility::arr2str($param))); + exit(); + } + + /** + * Encrypt data for cloud req + * + * @since 7.0 + */ + private function _sign_b64($data) + { + if (empty($this->_summary['sk_b64'])) { + self::debugErr('No sk to sign.'); + return false; + } + $sk = base64_decode($this->_summary['sk_b64']); + if (strlen($sk) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { + self::debugErr('Invalid local sign sk length.'); + // Reset local pk/sk + unset($this->_summary['pk_b64']); + unset($this->_summary['sk_b64']); + $this->save_summary(); + self::debug('Clear local sign pk/sk pair.'); + + return false; + } + $signature = sodium_crypto_sign_detached($data, $sk); + return base64_encode($signature); + } + + /** + * Load server pk from cloud + * + * @since 7.0 + */ + private function _load_server_pk($from_wpapi = false) + { + // Load cloud pk + $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY_SIGN; + if ($from_wpapi) { + $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY_SIGN; + } + $resp = wp_remote_get($server_key_url); + if (is_wp_error($resp)) { + self::debugErr('Failed to load key: ' . $resp->get_error_message()); + return false; + } + $pk = trim($resp['body']); + self::debug('Loaded key from ' . $server_key_url . ': ' . $pk); + $cloud_pk = base64_decode($pk); + if (strlen($cloud_pk) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) { + self::debugErr('Invalid cloud public key length.'); + return false; + } + + $sk = base64_decode($this->_summary['sk_b64']); + if (strlen($sk) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { + self::debugErr('Invalid local secret key length.'); + // Reset local pk/sk + unset($this->_summary['pk_b64']); + unset($this->_summary['sk_b64']); + $this->save_summary(); + self::debug('Unset local pk/sk pair.'); + + return false; + } + + return $cloud_pk; + } + + /** + * WPAPI echo back to notify the sealed databox + * + * @since 7.0 + */ + public function wp_rest_echo() + { + self::debug('Parsing echo', $_POST); + + if (empty($_POST['wpapi_ts']) || empty($_POST['wpapi_signature_b64'])) { + return self::err('No echo data'); + } + + $is_valid = $this->_validate_signature($_POST['wpapi_signature_b64'], $_POST['wpapi_ts'], true); + if (!$is_valid) { + return self::err('Data validation from WPAPI REST Echo failed'); + } + + $diff = time() - $_POST['wpapi_ts']; + if (abs($diff) > 86400) { + self::debugErr("WPAPI echo data timeout [diff] " . $diff); + return self::err('Echo data expired'); + } + + $signature_b64 = $this->_sign_b64($_POST['wpapi_ts']); + self::debug("Response to echo [signature_b64] " . $signature_b64); + return self::ok(array('signature_b64' => $signature_b64)); + } + + /** + * Validate cloud data + * + * @since 7.0 + */ + private function _validate_signature($signature_b64, $data, $from_wpapi = false) + { + // Try validation + try { + $cloud_pk = $this->_load_server_pk($from_wpapi); + if (!$cloud_pk) { + return false; + } + $signature = base64_decode($signature_b64); + $is_valid = sodium_crypto_sign_verify_detached($signature, $data, $cloud_pk); + } catch (\SodiumException $e) { + self::debugErr("Decryption failed: " . $e->getMessage()); + return false; + } + self::debug('Signature validation result: ' . ($is_valid ? 'true' : 'false')); + return $is_valid; + } + + /** + * Finish qc activation after redirection back from QC + * + * @since 7.0 + */ + public function finish_qc_activation() + { + if (empty($_GET['qc_activated']) || empty($_GET['qc_ts']) || empty($_GET['qc_signature_b64'])) { + return; + } + + $data_to_validate_signature = array( + 'wp_pk_b64' => $this->_summary['pk_b64'], + 'qc_ts' => $_GET['qc_ts'], + ); + $is_valid = $this->_validate_signature($_GET['qc_signature_b64'], implode('', $data_to_validate_signature)); + if (!$is_valid) { + self::debugErr("Failed to validate qc activation data"); + Admin_Display::error(sprintf(__('Failed to validate %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + self::debug("QC activation status: " . $_GET['qc_activated']); + if (!in_array($_GET['qc_activated'], array('anonymous', 'linked', 'cdn'))) { + self::debugErr("Failed to parse qc activation status"); + Admin_Display::error(sprintf(__('Failed to parse %s activation status.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + $diff = time() - $_GET['qc_ts']; + if (abs($diff) > 86400) { + self::debugErr("QC activation data timeout [diff] " . $diff); + Admin_Display::error(sprintf(__('%s activation data expired.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + $this->_summary['qc_activated'] = $_GET['qc_activated']; + $this->save_summary(); + + $msg = sprintf(__('Congratulations, %s successfully set this domain up for the anonymous online services.', 'litespeed-cache'), 'QUIC.cloud'); + if ($_GET['qc_activated'] == 'linked') { + $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services.', 'litespeed-cache'), 'QUIC.cloud'); + } + if ($_GET['qc_activated'] == 'cdn') { + $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services with CDN service.', 'litespeed-cache'), 'QUIC.cloud'); + // Turn on CDN option + $this->cls('Conf')->update_confs(array(self::O_CDN_QUIC => true)); + } + Admin_Display::success('🎊 ' . $msg); + + $this->clear_cloud(); + + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); + } + + /** + * Reset QC setup + * + * @since 7.0 + */ + public function reset_qc() + { + unset($this->_summary['pk_b64']); + unset($this->_summary['sk_b64']); + unset($this->_summary['qc_activated']); + $this->save_summary(); + self::debug('Clear local QC activation.'); + + $this->clear_cloud(); + + Admin_Display::success(sprintf(__('Reset %s activation successfully.', 'litespeed-cache'), 'QUIC.cloud')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); + exit; } /** @@ -275,9 +548,6 @@ public function allowance($service, &$err = false) $allowance_max = 0; if ($service == self::SVC_IMG_OPTM) { $allowance_max = self::IMG_OPTM_DEFAULT_GROUP; - if (!empty($usage['pkgs']) && $usage['pkgs'] & self::BM_IMG_OPTM_JUMBO_GROUP) { - $allowance_max = self::IMG_OPTM_JUMBO_GROUP; - } } $allowance = $usage['quota'] - $usage['used']; @@ -540,7 +810,6 @@ private function _get($service, $data = false) $param = array( 'site_url' => home_url(), - 'domain_key' => $this->_api_key(), 'main_domain' => !empty($this->_summary['main_domain']) ? $this->_summary['main_domain'] : '', 'ver' => Core::VER, ); @@ -555,7 +824,11 @@ private function _get($service, $data = false) self::save_summary(array('curr_request.' . $service_tag => time())); - $response = wp_remote_get($url, array('timeout' => 15, 'sslverify' => true)); + $response = wp_remote_get($url, array( + 'timeout' => 15, + 'sslverify' => true, + 'headers' => array('Accept' => 'application/json'), + )); return $this->_parse_response($response, $service, $service_tag, $server); } @@ -593,7 +866,7 @@ private function _maybe_cloud($service_tag) return true; } - if ($service_tag == self::SVC_D_SYNC_CONF && $this->_setup_token && !$this->_api_key()) { + if ($service_tag == self::SVC_D_SYNC_CONF && !$this->activated()) { self::debug('Skip sync conf if API key is not available yet.'); return false; } @@ -603,12 +876,9 @@ private function _maybe_cloud($service_tag) $timestamp_tag = 'curr_request.'; if ($service_tag == self::SVC_IMG_OPTM . '-' . Img_Optm::TYPE_NEW_REQ) { $timestamp_tag = 'last_request.'; - if ($this->has_pkg(self::SVC_IMG_OPTM, self::BM_IMG_OPTM_PRIO)) { - $expiration_req /= 10; - } } else { // For all other requests, if is under debug mode, will always allow - if ($this->conf(self::O_DEBUG) && $this->_api_key()) { + if ($this->conf(self::O_DEBUG)) { return true; } } @@ -634,14 +904,37 @@ private function _maybe_cloud($service_tag) return true; } - if (!$this->_api_key()) { - Admin_Display::error(Error::msg('lack_of_api_key')); + if (!$this->activated() && $service_tag != self::SVC_U_ACTIVATE) { + Admin_Display::error(Error::msg('setup_required')); return false; } return true; } + /** + * Check if activated QUIC.cloud service or not + * + * @since 7.0 + * @access public + */ + public function activated() + { + return !empty($this->_summary['sk_b64']) && !empty($this->_summary['qc_activated']); + } + + /** + * Show my.qc quick link to the domain page + */ + public function qc_link() + { + $data = array( + 'site_url' => home_url(), + 'ver' => LSCWP_V, + ); + return self::CLOUD_SERVER_DASH . '/u/wp3/manage?data=' . urlencode(Utility::arr2str($data)); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); + } + /** * Post data to QUIC.cloud server * @@ -684,9 +977,17 @@ private function _post($service, $data = false, $time_out = false) $data['service_type'] = $service; // For queue distribution usage } + // Encrypt service as signature + $signature_ts = time(); + $sign_data = array( + 'service_tag' => $service_tag, + 'ts' => $signature_ts, + ); + $data['signature_b64'] = $this->_sign_b64(implode('', $sign_data)); + $data['signature_ts'] = $signature_ts; + $param = array( 'site_url' => home_url(), - 'domain_key' => $this->_api_key(), 'main_domain' => !empty($this->_summary['main_domain']) ? $this->_summary['main_domain'] : '', 'ver' => Core::VER, 'data' => $data, @@ -694,7 +995,12 @@ private function _post($service, $data = false, $time_out = false) self::save_summary(array('curr_request.' . $service_tag => time())); - $response = wp_remote_post($url, array('body' => $param, 'timeout' => $time_out ?: 15, 'sslverify' => true)); + $response = wp_remote_post($url, array( + 'body' => $param, + 'timeout' => $time_out ?: 15, + 'sslverify' => true, + 'headers' => array('Accept' => 'application/json'), + )); return $this->_parse_response($response, $service, $service_tag, $server); } @@ -726,13 +1032,13 @@ private function _parse_response($response, $service, $service_tag, $server) self::debug('Node error, redetecting node [svc] ' . $service); $this->detect_cloud($service, true); } - return; + return false; } $json = \json_decode($response['body'], true); if (!is_array($json)) { - self::debug('failed to decode response json: ' . $response['body']); + self::debugErr('failed to decode response json: ' . $response['body']); if ($service !== self::API_VER) { $msg = __('Failed to request via WordPress', 'litespeed-cache') . ': ' . $response['body'] . " [server] $server [service] $service"; @@ -746,43 +1052,43 @@ private function _parse_response($response, $service, $service_tag, $server) self::save_summary(); // Force redetect node - self::debug('Node error, redetecting node [svc] ' . $service); + self::debugErr('Node error, redetecting node [svc] ' . $service); $this->detect_cloud($service, true); } - return; + return false; } if (!empty($json['_code'])) { - self::debug('Hit err _code: ' . $json['_code']); + self::debugErr('Hit err _code: ' . $json['_code']); if ($json['_code'] == 'unpulled_images') { $msg = __('Cloud server refused the current request due to unpulled images. Please pull the images first.', 'litespeed-cache'); Admin_Display::error($msg); - return; + return false; } if ($json['_code'] == 'blocklisted') { $msg = __('Your domain_key has been temporarily blocklisted to prevent abuse. You may contact support at QUIC.cloud to learn more.', 'litespeed-cache'); Admin_Display::error($msg); - return; + return false; } if ($json['_code'] == 'rate_limit') { - self::debug('Cloud server rate limit exceeded.'); + self::debugErr('Cloud server rate limit exceeded.'); $msg = __('Cloud server refused the current request due to rate limiting. Please try again later.', 'litespeed-cache'); Admin_Display::error($msg); - return; + return false; } if ($json['_code'] == 'heavy_load' || $json['_code'] == 'redetect_node') { // Force redetect node - self::debug('Node redetecting node [svc] ' . $service); + self::debugErr('Node redetecting node [svc] ' . $service); Admin_Display::info(__('Redetected node', 'litespeed-cache') . ': ' . Error::msg($json['_code'])); $this->detect_cloud($service, true); } } if (!empty($json['_503'])) { - self::debug('service 503 unavailable temporarily. ' . $json['_503']); + self::debugErr('service 503 unavailable temporarily. ' . $json['_503']); $msg = __( 'We are working hard to improve your online service experience. The service will be unavailable while we work. We apologize for any inconvenience.', @@ -792,15 +1098,15 @@ private function _parse_response($response, $service, $service_tag, $server) Admin_Display::error($msg); // Force redetect node - self::debug('Node error, redetecting node [svc] ' . $service); + self::debugErr('Node error, redetecting node [svc] ' . $service); $this->detect_cloud($service, true); - return; + return false; } list($json, $return) = $this->extract_msg($json, $service, $server); if ($return) { - return; + return false; } self::save_summary(array( @@ -844,7 +1150,7 @@ public function extract_msg($json, $service, $server = false, $is_callback = fal self::debug('_success: ' . $json['_success']); $msg = __('Good news from QUIC.cloud server', 'litespeed-cache') . ': ' . $json['_success']; $msg .= $this->_parse_link($json); - Admin_Display::succeed($msg); + Admin_Display::success($msg); unset($json['_success']); } @@ -929,7 +1235,7 @@ public function extract_msg($json, $service, $server = false, $is_callback = fal // Site not on QC, delete invalid domain key if ($json_msg == 'site_not_registered' || $json_msg == 'err_key') { - $this->_clean_api_key(); + $this->_reset_qc_reg(); } return array($json, true); @@ -944,17 +1250,16 @@ public function extract_msg($json, $service, $server = false, $is_callback = fal } /** - * Clear API key and QC linked status + * Clear QC linked status * @since 5.0 */ - private function _clean_api_key() + private function _reset_qc_reg() { - $this->cls('Conf')->update_confs(array(self::O_API_KEY => '')); - $this->_summary['is_linked'] = 0; + unset($this->_summary['qc_activated']); self::save_summary(); $msg = __('Site not recognized. Domain Key has been automatically removed. Please request a new one.', 'litespeed-cache'); - $msg .= Doc::learn_more(admin_url('admin.php?page=litespeed-general'), __('Click here to set.', 'litespeed-cache'), true, false, true); + $msg .= Doc::learn_more(admin_url('admin.php?page=litespeed'), __('Click here to set.', 'litespeed-cache'), true, false, true); $msg .= Doc::learn_more('https://docs.litespeedtech.com/lscache/lscwp/general/#domain-key', false, false, false, true); Admin_Display::error($msg, false, true); } @@ -965,27 +1270,17 @@ private function _clean_api_key() */ public function rest_err_domains() { - // Validate token hash first - if (empty($_POST['hash']) || empty($_POST['main_domain']) || empty($_POST['alias'])) { + if (empty($_POST['main_domain']) || empty($_POST['alias'])) { return self::err('lack_of_param'); } - if (!$this->_api_key() || $_POST['hash'] !== md5(substr($this->_api_key(), 1, 8))) { - return self::err('wrong_hash'); - } - - list($post_data) = $this->extract_msg($_POST, 'Quic.cloud', false, true); + $this->extract_msg($_POST, 'Quic.cloud', false, true); if ($this->_is_err_domain($_POST['alias'])) { if ($_POST['alias'] == home_url()) { $this->_remove_domain_from_err_list($_POST['alias']); } - - $res_hash = substr($this->_api_key(), 2, 4); - - self::debug('__callback IP request hash: md5(' . $res_hash . ')'); - - return self::ok(array('hash' => md5($res_hash))); + return self::ok(); } return self::err('Not an alias req from here'); @@ -1023,72 +1318,6 @@ private function _is_err_domain($home_url) return true; } - public function req_rest_api($api, $body = array()) - { - $token = $this->_setup_token; - - if (empty($token)) { - Admin_Display::error(__('Cannot request REST API, no token saved.', 'litespeed-cache')); - return; - } - $req_args = array( - 'headers' => array( - 'Authorization' => 'bearer ' . $token, - 'Content-Type' => 'application/json', - ), - ); - self::debug('Req rest api to QC [api] ' . $api); - if (!empty($body)) { - $req_args['body'] = \json_encode($body); - - $response = wp_remote_post(self::CLOUD_SERVER . '/v2' . $api, $req_args); - } else { - $response = wp_remote_get(self::CLOUD_SERVER . '/v2' . $api, $req_args); - } - - return $this->_parse_rest_response($response); - } - - private function _parse_rest_response($response) - { - if (is_wp_error($response)) { - $error_message = $response->get_error_message(); - self::debug('failed to request REST API: ' . $error_message); - Admin_Display::error(__('Cloud REST Error', 'litespeed-cache') . ': ' . $error_message); - return $error_message; - } elseif (wp_remote_retrieve_response_code($response) == '401') { - Admin_Display::error(__('Unauthorized access to REST API. Your token has expired.', 'litespeed-cache')); - return 'unauthorized access to REST API.'; - } - - $json = \json_decode($response['body'], true); - self::debug('QC response', $json); - - if (!$json['success']) { - $contactSupport = false; - if (isset($json['info']['errors'])) { - $errs = array(); - foreach ($json['info']['errors'] as $err) { - $errs[] = 'Error ' . $err['code'] . ': ' . $err['message']; - if ($err['code'] == 1113) { - $contactSupport = true; - } - } - $error_message = implode('
', $errs); - } else { - $error_message = __('Unknown error.', 'litespeed-cache'); - $contactSupport = true; - } - if ($contactSupport) { - $error_message .= ' ' . __('Contact QUIC.cloud support', 'litespeed-cache') . ''; - } - Admin_Display::error(__('Cloud REST API returned error: ', 'litespeed-cache') . $error_message); - return $error_message; - } - - return $json; - } - /** * Show promo from cloud * @@ -1097,10 +1326,6 @@ private function _parse_rest_response($response) */ public function show_promo() { - // if ( ! $this->_api_key() && ! defined( 'LITESPEED_DISMISS_DOMAIN_KEY' ) ) { - // Admin_Display::error( Error::msg( 'lack_of_api_key' ), true ); - // } - if (empty($this->_summary['promo'])) { return; } @@ -1154,314 +1379,28 @@ private function _parse_link(&$json) */ public function ip_validate() { - if (empty($_POST['hash'])) { - self::debug('Lack of hash param'); - return self::err('lack_of_param'); + if (!$this->activated()) { + self::debug('Not activated QC yet'); + return self::err('setup_required'); } - // Note: Using empty here throws a fatal error in PHP v5.3 - if (!$this->_api_key()) { - self::debug('Lack of API key'); - return self::err('lack_of_api_key'); + if (empty($_POST['data_b64']) || empty($_POST['data_b64_nonce'])) { + return self::err('lack_of_params'); } - $to_validate = substr($this->_api_key(), 0, 4); - if ($_POST['hash'] !== md5($to_validate)) { - self::debug('__callback IP request hash wrong: md5(' . $to_validate . ') !== ' . $_POST['hash']); + // Decrypt the data_orig and respond + $data_orig = $this->_decrypt($_POST['data_b64'], $_POST['data_b64_nonce']); + + if (!$data_orig) { + self::debug('__callback IP request decryption failed'); return self::err('err_hash'); } Control::set_nocache('Cloud IP hash validation'); - $res_hash = substr($this->_api_key(), 2, 4); - - self::debug('__callback IP request hash: md5(' . $res_hash . ')'); - - return self::ok(array('hash' => md5($res_hash))); - } - - /** - * Can apply for a new token or not - * - * @since 3.0 - */ - public function can_token() - { - return empty($this->_summary['token_ts']) || time() - $this->_summary['token_ts'] > self::EXPIRATION_TOKEN; - } - - public function set_keygen_token($token) - { - $this->_summary['token'] = $token; - $this->_summary['token_ts'] = time(); - if (!empty($this->_summary['apikey_ts'])) { - unset($this->_summary['apikey_ts']); - } - self::save_summary(); - } - - /** - * Send request for domain key, get json [ 'token' => 'asdfasdf' ] - * - * @since 3.0 - * @access public - */ - public function gen_key() - { - $data = array( - 'site_url' => home_url(), - 'rest' => function_exists('rest_get_url_prefix') ? rest_get_url_prefix() : apply_filters('rest_url_prefix', 'wp-json'), - 'server_ip' => $this->conf(self::O_SERVER_IP), - ); - if (!empty($this->_summary['token'])) { - $data['token'] = $this->_summary['token']; - } - - $response = wp_remote_get(self::CLOUD_SERVER . '/d/req_key?data=' . Utility::arr2str($data)); - if (is_wp_error($response)) { - $error_message = $response->get_error_message(); - self::debug('failed to gen_key: ' . $error_message); - Admin_Display::error(__('Cloud Error', 'litespeed-cache') . ': ' . $error_message); - return; - } - - $json = \json_decode($response['body'], true); - - // Save token option - if (!empty($json['token'])) { - $this->set_keygen_token($json['token']); - } - - // Parse general error msg - if (empty($json['_res']) || $json['_res'] !== 'ok') { - // clear current token - unset($this->_summary['token']); - self::save_summary(); - - $json_msg = !empty($json['_msg']) ? $json['_msg'] : 'unknown'; - self::debug('❌ _err: ' . $json_msg); - - $msg = __('Failed to communicate with QUIC.cloud server', 'litespeed-cache') . ': ' . Error::msg($json_msg); - $msg .= $this->_parse_link($json); - Admin_Display::error($msg); - - return; - } - - // This is a ok msg - if (!empty($json['_msg'])) { - self::debug('_msg: ' . $json['_msg']); - - $msg = __('Message from QUIC.cloud server', 'litespeed-cache') . ': ' . Error::msg($json['_msg']); - $msg .= $this->_parse_link($json); - Admin_Display::info($msg); - return; - } - - self::debug('✅ send request for key successfully.'); - - Admin_Display::succeed(__('Applied for Domain Key successfully. Please wait for result. Domain Key will be automatically sent to your WordPress.', 'litespeed-cache')); - } - - /** - * Token callback validation from Cloud - * - * @since 3.0 - * @access public - */ - public function token_validate() - { - try { - $this->validate_hash(); - } catch (\Exception $e) { - return self::err($e->getMessage()); - } - - Control::set_nocache('Cloud token validation'); - - self::debug('✅ __callback token validation passed'); - - return self::ok(array('hash' => md5(substr($this->_summary['token'], 3, 8)))); - } - - /** - * Callback for approval of api key after validated token and gen key from QUIC.cloud - * - * @since 3.0 - * @access public - */ - public function save_apikey() - { - // Validate token hash first - if (empty($_POST['domain_key']) || !isset($_POST['is_linked'])) { - return self::err('lack_of_param'); - } - - try { - $this->validate_hash(1); - } catch (\Exception $e) { - return self::err($e->getMessage()); - } - - // This doesn't need to sync QUIC conf but need to clear nodes - $this->cls('Conf')->update_confs(array(self::O_API_KEY => $_POST['domain_key'])); - - $this->_summary['is_linked'] = $_POST['is_linked'] ? 1 : 0; - $this->_summary['apikey_ts'] = time(); - if (!empty($_POST['main_domain'])) { - $this->_summary['main_domain'] = $_POST['main_domain']; - } - // Clear token - unset($this->_summary['token']); - self::save_summary(); - - self::debug('✅ saved auth_key'); - Admin_Display::succeed('🎊 ' . __('Congratulations, your Domain Key has been approved! The setting has been updated accordingly.', 'litespeed-cache')); - - return self::ok(); - } - - /** - * Validate POST hash match local token or not - * - * @since 3.0 - */ - public function validate_hash($offset = 0) - { - if (empty($_POST['hash'])) { - self::debug('Lack of hash param'); - throw new \Exception('lack_of_param'); - } - - if (empty($this->_summary['token'])) { - self::debug('token validate failed: token not exist'); - throw new \Exception('lack_of_local_token'); - } - - if ($_POST['hash'] !== md5(substr($this->_summary['token'], $offset, 8))) { - self::debug('token validate failed: token mismatch hash !== ' . $_POST['hash']); - throw new \Exception('mismatch'); - } - } - - /** - * If can link the domain to QC user or not - * - * @since 3.0 - */ - public function can_link_qc() - { - return empty($this->_summary['is_linked']) && $this->_api_key(); - } - - /** - * Link the domain to QC user - * - * @since 3.0 - */ - private function _link_to_qc() - { - if (!$this->can_link_qc()) { - return; - } - - $data = array( - 'site_url' => home_url(), - 'domain_hash' => md5(substr($this->_api_key(), 0, 8)), - 'ref' => get_admin_url(null, 'admin.php?page=litespeed-general'), - ); - - wp_redirect(self::CLOUD_SERVER_DASH . '/u/wp?data=' . Utility::arr2str($data)); - exit(); - } - - public function qc_link() - { - $data = array( - 'site_url' => home_url(), - 'domain_hash' => md5(substr($this->_api_key(), 0, 8)), - 'ver' => LSCWP_V, - ); - return self::CLOUD_SERVER_DASH . '/u/wp?data=' . Utility::arr2str($data); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); - } - - public function set_linked() - { - $this->_summary['is_linked'] = 1; - self::save_summary(); + self::debug('__callback IP request hash: ' . $data_orig); - # Force resync qc conf - $this->cls('CDN\Quic')->try_sync_conf(true); - } - - /** - * Update is_linked status if is a redirected back from QC - * - * @since 3.0 - * @since 5.0 renamed update_is_linked_status -> parse_qc_redir, add param for additional args. Return args if exist. - */ - public function parse_qc_redir($check_token = false) - { - if (!$this->_api_key() && !empty($this->_summary['is_linked'])) { - $this->_summary['is_linked'] = 0; - self::save_summary(); - } - - if (empty($_GET['qc_res'])) { - return false; - } - - if ($_GET['qc_res'] == 'registered') { - if (!empty($_GET['qc_new'])) { - Admin_Display::succeed(__('QUIC.cloud account has been created and successfully linked.', 'litespeed-cache'), true); - } else { - Admin_Display::succeed(__('QUIC.cloud account has been successfully linked.', 'litespeed-cache'), true); - } - } - - $qsDrop = array(); - $qsDrop[] = ".replace( '&qc_res=" . sanitize_key($_GET['qc_res']) . ', \'\' )'; - - if (!empty($_GET['domain_hash'])) { - if (empty($_GET['domain_hash_nonce'])) { - Admin_Display::error(__('Domain Key hash nonce missing.', 'litespeed-cache'), true); - return false; - } - $salt = substr($this->_api_key(), 3, 8); - $tick = ceil(time() / 43200); - $nonce = md5($salt . $tick); - $nonce2 = md5($salt . ($tick - 1)); - if ($_GET['domain_hash_nonce'] != $nonce && $_GET['domain_hash_nonce'] != $nonce2) { - Admin_Display::error(__('Domain Key hash nonce mismatch. Please correct your server clock.', 'litespeed-cache'), true); - return false; - } - - if (md5(substr($this->_api_key(), 2, 8)) !== $_GET['domain_hash']) { - Admin_Display::error(__('Domain Key hash mismatch', 'litespeed-cache'), true); - return false; - } - - $this->set_linked(); - $qsDrop[] = ".replace( '&domain_hash=" . sanitize_key($_GET['domain_hash']) . ', \'\' )'; - } - - $token = ''; - if ($check_token && !empty($_GET['token'])) { - // Validate nonce `litespeed_qc_link` - if (empty($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'litespeed_qc_link')) { - Admin_Display::error(__('Failed to verify domain nonce.', 'litespeed-cache'), true); - return false; - } - - $token = preg_replace('/[^0-9a-zA-Z]/', '', $_GET['token']); - $qsDrop[] = ".replace( '&token=" . urlencode($_GET['token']) . ', \'\' )'; - } - - $replaceStr = implode('', $qsDrop); - - // Drop QS - echo "'; - return $token; + return self::ok(array('data_orig' => $data_orig)); } /** @@ -1543,6 +1482,7 @@ public static function ok($data = array()) */ public static function err($code) { + self::debug("❌ Error response code: $code"); return array('_res' => 'err', '_msg' => $code); } @@ -1600,19 +1540,23 @@ public function handler() $this->_clear_promo(); break; - case self::TYPE_GEN_KEY: - $this->gen_key(); + case self::TYPE_RESET: + $this->reset_qc(); + break; + + case self::TYPE_ACTIVATE: + $this->init_qc(); break; case self::TYPE_LINK: - $this->_link_to_qc(); + $this->link_qc(); break; case self::TYPE_SYNC_USAGE: $this->sync_usage(); $msg = __('Sync credit allowance with Cloud Server successfully.', 'litespeed-cache'); - Admin_Display::succeed($msg); + Admin_Display::success($msg); break; default: diff --git a/src/conf.cls.php b/src/conf.cls.php index 0fed67ba4..bb26c1d28 100644 --- a/src/conf.cls.php +++ b/src/conf.cls.php @@ -477,11 +477,6 @@ public function update_confs($the_matrix = false) if ($this->_updated_ids) { foreach ($this->_updated_ids as $id) { - // Special handler for QUIC.cloud domain key to clear all existing nodes - if ($id == self::O_API_KEY) { - $this->cls('Cloud')->clear_cloud(); - } - // Special handler for crawler: reset sitemap when drop_domain setting changed if ($id == self::O_CRAWLER_DROP_DOMAIN) { $this->cls('Crawler_Map')->empty_map(); diff --git a/src/crawler.cls.php b/src/crawler.cls.php index d0e752b22..35e1be931 100644 --- a/src/crawler.cls.php +++ b/src/crawler.cls.php @@ -1075,7 +1075,7 @@ public function list_crawlers() // WebP on/off if (($this->conf(Base::O_GUEST) && $this->conf(Base::O_GUEST_OPTM)) || $this->conf(Base::O_IMG_OPTM_WEBP)) { - $crawler_factors['webp'] = array(1 => 'WebP', 0 => ''); + $crawler_factors['webp'] = array(1 => 'WebP/AVIF', 0 => ''); } // Guest Mode on/off diff --git a/src/error.cls.php b/src/error.cls.php index 549abe269..d9782595a 100644 --- a/src/error.cls.php +++ b/src/error.cls.php @@ -54,9 +54,9 @@ public static function msg($code, $args = null) ); break; - case 'lack_of_api_key': + case 'qc_setup_required': $msg = - sprintf(__('You will need to set %s to use the online services.', 'litespeed-cache'), '' . Lang::title(Base::O_API_KEY) . '') . + sprintf(__('You will need to finish %s setup to use the online services.', 'litespeed-cache'), 'QUIC.cloud') . Doc::learn_more(admin_url('admin.php?page=litespeed-general'), __('Click here to set.', 'litespeed-cache'), true, false, true); break; diff --git a/src/img-optm.cls.php b/src/img-optm.cls.php index 9ae676023..0f67f256c 100644 --- a/src/img-optm.cls.php +++ b/src/img-optm.cls.php @@ -54,9 +54,6 @@ class Img_Optm extends Base const DB_NEED_PULL = 'need_pull'; - const JUMBO_REQUEST_BONUS = 10; - const PRIO_REQUEST_BONUS = 5; - private $wp_upload_dir; private $tmp_pid; private $tmp_type; @@ -656,10 +653,14 @@ private function _send_request($allowance) 'action' => self::CLOUD_ACTION_NEW_REQ, 'list' => \json_encode($list), 'optm_ori' => $this->conf(self::O_IMG_OPTM_ORI) ? 1 : 0, - 'optm_webp' => $this->conf(self::O_IMG_OPTM_WEBP) ? 1 : 0, 'optm_lossless' => $this->conf(self::O_IMG_OPTM_LOSSLESS) ? 1 : 0, 'keep_exif' => $this->conf(self::O_IMG_OPTM_EXIF) ? 1 : 0, ); + if ($this->conf(self::O_IMG_OPTM_WEBP) == 2) { + $data['optm_avif'] = 1; + } elseif ($this->conf(self::O_IMG_OPTM_WEBP) == 1) { + $data['optm_webp'] = 1; + } // Push to Cloud server $json = Cloud::post(Cloud::SVC_IMG_OPTM, $data); @@ -705,13 +706,6 @@ public function notify_img() $post_data = $_POST; } - // Validate key - if (empty($post_data['domain_key']) || $post_data['domain_key'] !== md5($this->conf(self::O_API_KEY))) { - $this->_summary['notify_ts_err'] = time(); - self::save_summary(); - return Cloud::err('wrong_key'); - } - global $wpdb; $notified_data = $post_data['data']; @@ -899,19 +893,6 @@ private function _calc_pull_threads() $imgs_per_req = ceil($images_waiting / 1000); //ie. download 5/request if 5000 images are waiting } - // Increase the request rate if the user has purchased addon packages - $has_jumbo_pkg = Cloud::cls()->has_pkg(Cloud::SVC_IMG_OPTM, Cloud::BM_IMG_OPTM_JUMBO_GROUP); - $has_prio_pkg = Cloud::cls()->has_pkg(Cloud::SVC_IMG_OPTM, Cloud::BM_IMG_OPTM_PRIO); - - if ($has_jumbo_pkg) { - self::debug('Jumbo package detected.'); - $imgs_per_req += self::JUMBO_REQUEST_BONUS; - } - if ($has_prio_pkg) { - self::debug('Priority Line package detected.'); - $imgs_per_req += self::PRIO_REQUEST_BONUS; - } - // Cap the request rate at 50 images per request $imgs_per_req = min(50, $imgs_per_req); diff --git a/src/lang.cls.php b/src/lang.cls.php index 7160dccd7..e1aa67359 100644 --- a/src/lang.cls.php +++ b/src/lang.cls.php @@ -46,12 +46,12 @@ public static function maybe_translate($raw_string) { $map = array( 'auto_alias_failed_cdn' => - __('Unable to automatically add %1$s as a Domain Alias for main %2$s domain, due to potential CDN conflict.', 'litespeed-cache') . + __('Unable to automatically add %1$s as a Domain Alias for main %2$s domain, due to potential CDN conflict.', 'litespeed-cache') . ' ' . Doc::learn_more('https://quic.cloud/docs/cdn/dns/how-to-setup-domain-alias/', false, false, false, true), 'auto_alias_failed_uid' => - __('Unable to automatically add %1$s as a Domain Alias for main %2$s domain.', 'litespeed-cache') . + __('Unable to automatically add %1$s as a Domain Alias for main %2$s domain.', 'litespeed-cache') . ' ' . __('Alias is in use by another QUIC.cloud account.', 'litespeed-cache') . ' ' . @@ -87,7 +87,6 @@ public static function title($id) { $_lang_list = array( self::O_SERVER_IP => __('Server IP', 'litespeed-cache'), - self::O_API_KEY => __('Domain Key', 'litespeed-cache'), self::O_GUEST_UAS => __('Guest Mode User Agents', 'litespeed-cache'), self::O_GUEST_IPS => __('Guest Mode IPs', 'litespeed-cache'), @@ -211,7 +210,7 @@ public static function title($id) self::O_IMG_OPTM_CRON => __('Auto Pull Cron', 'litespeed-cache'), self::O_IMG_OPTM_ORI => __('Optimize Original Images', 'litespeed-cache'), self::O_IMG_OPTM_RM_BKUP => __('Remove Original Backups', 'litespeed-cache'), - self::O_IMG_OPTM_WEBP => __('Image WebP Replacement', 'litespeed-cache'), + self::O_IMG_OPTM_WEBP => __('Next-Gen Image Format', 'litespeed-cache'), self::O_IMG_OPTM_LOSSLESS => __('Optimize Losslessly', 'litespeed-cache'), self::O_IMG_OPTM_EXIF => __('Preserve EXIF/XMP data', 'litespeed-cache'), self::O_IMG_OPTM_WEBP_ATTR => __('WebP Attribute To Replace', 'litespeed-cache'), diff --git a/src/media.cls.php b/src/media.cls.php index b80596d3b..7a152ed43 100644 --- a/src/media.cls.php +++ b/src/media.cls.php @@ -23,6 +23,7 @@ class Media extends Root private $content; private $_wp_upload_dir; private $_vpi_preload_list = array(); + private $_format = ''; /** * Init @@ -34,6 +35,12 @@ public function __construct() Debug2::debug2('[Media] init'); $this->_wp_upload_dir = wp_upload_dir(); + if ($this->conf(Base::O_IMG_OPTM_WEBP)) { + $this->_format = 'webp'; + if ($this->conf(Base::O_IMG_OPTM_WEBP) == 2) { + $this->_format = 'avif'; + } + } } /** @@ -49,7 +56,7 @@ public function init() } // Due to ajax call doesn't send correct accept header, have to limit webp to HTML only - if (defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP)) { + if (defined('LITESPEED_GUEST_OPTM') || $this->_format) { if ($this->webp_support()) { // Hook to srcset if (function_exists('wp_calculate_image_srcset')) { @@ -93,7 +100,7 @@ public function finalize_head($content) // $featured_image_url = get_the_post_thumbnail_url(); // if ($featured_image_url) { // self::debug('Append featured image to head: ' . $featured_image_url); - // if ((defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP)) && $this->webp_support()) { + // if ((defined('LITESPEED_GUEST_OPTM') || $this->_format) && $this->webp_support()) { // $featured_image_url = $this->replace_webp($featured_image_url) ?: $featured_image_url; // } // } @@ -437,8 +444,10 @@ public function get_image_sizes() */ public function webp_support() { - if (!empty($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false) { - return true; + if (!empty($_SERVER['HTTP_ACCEPT'])) { + if (strpos($_SERVER['HTTP_ACCEPT'], 'image/' . $this->_format) !== false) { + return true; + } } if (!empty($_SERVER['HTTP_USER_AGENT'])) { @@ -506,7 +515,7 @@ private function _finalize() * Use webp for optimized images * @since 1.6.2 */ - if ((defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP)) && $this->webp_support()) { + if ((defined('LITESPEED_GUEST_OPTM') || $this->_format) && $this->webp_support()) { $this->content = $this->_replace_buffer_img_webp($this->content); } diff --git a/src/optimize.cls.php b/src/optimize.cls.php index c31f73982..93a17c7f5 100644 --- a/src/optimize.cls.php +++ b/src/optimize.cls.php @@ -68,7 +68,7 @@ public function init() { $this->cfg_css_async = defined('LITESPEED_GUEST_OPTM') || $this->conf(self::O_OPTM_CSS_ASYNC); if ($this->cfg_css_async) { - if (!$this->conf(self::O_API_KEY)) { + if (!$this->cls('Cloud')->activated()) { Debug2::debug('[Optm] ❌ CCSS set to OFF due to missing domain key'); $this->cfg_css_async = false; } diff --git a/src/rest.cls.php b/src/rest.cls.php index 0a9f7cf72..9eca48825 100644 --- a/src/rest.cls.php +++ b/src/rest.cls.php @@ -55,21 +55,16 @@ public function rest_api_init() )); // IP callback validate - register_rest_route('litespeed/v1', '/ip_validate', array( + register_rest_route('litespeed/v3', '/ip_validate', array( 'methods' => 'POST', 'callback' => array($this, 'ip_validate'), 'permission_callback' => array($this, 'is_from_cloud'), )); - // Token callback validate - register_rest_route('litespeed/v1', '/token', array( + ## 1.2. WP REST Dryrun Callback + register_rest_route('litespeed/v3', '/wp_rest_echo', array( 'methods' => 'POST', - 'callback' => array($this, 'token'), - 'permission_callback' => array($this, 'is_from_cloud'), - )); - register_rest_route('litespeed/v1', '/token', array( - 'methods' => 'GET', - 'callback' => array($this, 'token_get'), + 'callback' => array($this, 'wp_rest_echo'), 'permission_callback' => array($this, 'is_from_cloud'), )); register_rest_route('litespeed/v3', '/ping', array( @@ -78,15 +73,8 @@ public function rest_api_init() 'permission_callback' => array($this, 'is_from_cloud'), )); - // API key callback notification - register_rest_route('litespeed/v1', '/apikey', array( - 'methods' => 'POST', - 'callback' => array($this, 'apikey'), - 'permission_callback' => array($this, 'is_from_cloud'), - )); - // CDN setup callback notification - register_rest_route('litespeed/v1', '/cdn_status', array( + register_rest_route('litespeed/v3', '/cdn_status', array( 'methods' => 'POST', 'callback' => array($this, 'cdn_status'), 'permission_callback' => array($this, 'is_from_cloud'), @@ -112,7 +100,7 @@ public function rest_api_init() 'permission_callback' => array($this, 'is_from_cloud'), )); - register_rest_route('litespeed/v1', '/err_domains', array( + register_rest_route('litespeed/v3', '/err_domains', array( 'methods' => 'POST', 'callback' => array($this, 'err_domains'), 'permission_callback' => array($this, 'is_from_cloud'), @@ -151,16 +139,6 @@ public function is_from_cloud() return $this->cls('Cloud')->is_from_cloud(); } - /** - * Token get for - * - * @since 3.0.4 - */ - public function token_get() - { - return Cloud::ok(); - } - /** * Ping pong * @@ -196,19 +174,9 @@ public function ip_validate() * * @since 3.0 */ - public function token() - { - return $this->cls('Cloud')->token_validate(); - } - - /** - * Launch api call - * - * @since 3.0 - */ - public function apikey() + public function wp_rest_echo() { - return $this->cls('Cloud')->save_apikey(); + return $this->cls('Cloud')->wp_rest_echo(); } /** @@ -265,12 +233,6 @@ public function err_domains() */ public function check_img() { - try { - $this->cls('Cloud')->validate_hash(4); - } catch (\Exception $e) { - return self::err($e->getMessage()); - } - return Img_Optm::cls()->check_img(); } diff --git a/src/root.cls.php b/src/root.cls.php index 9763c3614..87ad379ba 100644 --- a/src/root.cls.php +++ b/src/root.cls.php @@ -32,6 +32,17 @@ protected function _separate_mobile() return (wp_is_mobile() || apply_filters('litespeed_is_mobile', false)) && $this->conf(Base::O_CACHE_MOBILE); } + /** + * Log an error message + * + * @since 7.0 + */ + public static function debugErr($msg, $backtrace_limit = false) + { + $msg = '❌ ' . $msg; + self::debug($msg, $backtrace_limit); + } + /** * Log a debug message. * diff --git a/src/ucss.cls.php b/src/ucss.cls.php index 43789744a..5f322f16c 100644 --- a/src/ucss.cls.php +++ b/src/ucss.cls.php @@ -509,13 +509,6 @@ public function notify() $this->_queue = $this->load_queue('ucss'); - // Validate key - if (empty($post_data['domain_key']) || $post_data['domain_key'] !== md5($this->conf(self::O_API_KEY))) { - self::debug('❌ notify wrong key'); - self::save_summary(array('notify_ts_err' => time())); - return Cloud::err('wrong_key'); - } - list($post_data) = $this->cls('Cloud')->extract_msg($post_data, 'ucss'); $notified_data = $post_data['data']; diff --git a/src/vpi.cls.php b/src/vpi.cls.php index 238182416..4a7219dd3 100644 --- a/src/vpi.cls.php +++ b/src/vpi.cls.php @@ -96,13 +96,6 @@ public function notify() $this->_queue = $this->load_queue('vpi'); - // Validate key - if (empty($post_data['domain_key']) || $post_data['domain_key'] !== md5($this->conf(self::O_API_KEY))) { - self::debug('❌ notify wrong key'); - self::save_summary(array('notify_ts_err' => time())); - return Cloud::err('wrong_key'); - } - list($post_data) = $this->cls('Cloud')->extract_msg($post_data, 'vpi'); $notified_data = $post_data['data']; diff --git a/tpl/cdn/manage.tpl.php b/tpl/cdn/cf.tpl.php similarity index 56% rename from tpl/cdn/manage.tpl.php rename to tpl/cdn/cf.tpl.php index 880ec23dd..cd790ee62 100644 --- a/tpl/cdn/manage.tpl.php +++ b/tpl/cdn/cf.tpl.php @@ -4,6 +4,71 @@ defined('WPINC') || exit; +$this->form_action(); +?> + +

+ + +

+ + + + + + + + + +
+ + title($id); ?> + + build_switch($id); ?> +
+ + ' . __('CDN', 'litespeed-cache') . ' -> ', __('Manage', 'litespeed-cache') . ''); ?> +
+
+
+ + + build_input(Base::O_CDN_CLOUDFLARE_KEY); ?> +
+ + %2$s.', 'litespeed-cache'), 'href="https://dash.cloudflare.com/profile/api-tokens" target="_blank"', 'Cloudflare'); ?> + +
+
+ +
+ + + build_input(Base::O_CDN_CLOUDFLARE_EMAIL); ?> +
+ + +
+
+ +
+ + + conf(Base::O_CDN_CLOUDFLARE_ZONE); + $cls = $cf_zone ? ' litespeed-input-success' : ' litespeed-input-warning'; + $this->build_input(Base::O_CDN_CLOUDFLARE_NAME, $cls); + ?> +
+ + +
+
+
+
+ +form_end(); $cf_on = $this->conf(Base::O_CDN_CLOUDFLARE); $cf_domain = $this->conf(Base::O_CDN_CLOUDFLARE_NAME) ?: '-'; $cf_zone = $this->conf(Base::O_CDN_CLOUDFLARE_ZONE) ?: '-'; @@ -11,10 +76,6 @@ $curr_status = CDN\Cloudflare::get_option(CDN\Cloudflare::ITEM_STATUS, array()); ?> -

- -

: qc_link(); ?>

-

diff --git a/tpl/cdn/entry.tpl.php b/tpl/cdn/entry.tpl.php index 50a5dafa5..e68cfc2e7 100644 --- a/tpl/cdn/entry.tpl.php +++ b/tpl/cdn/entry.tpl.php @@ -1,48 +1,50 @@ __( 'CDN Settings', 'litespeed-cache' ), - 'auto_setup' => __( 'QUIC.cloud CDN Setup', 'litespeed-cache' ), - 'manage' => __( 'Manage', 'litespeed-cache' ), -) ; + 'qc' => __('QUIC.cloud', 'litespeed-cache'), + 'cf' => __('Cloudflare', 'litespeed-cache'), + 'other' => __('Other Static CDN', 'litespeed-cache'), +); ?>

- +

- v + v
- $val) { - echo "
" ; - require LSCWP_DIR . "tpl/cdn/$tab.tpl.php" ; - echo "
" ; + echo "
"; + require LSCWP_DIR . "tpl/cdn/$tab.tpl.php"; + echo "
"; } - ?> + ?>
-
+ \ No newline at end of file diff --git a/tpl/cdn/other.tpl.php b/tpl/cdn/other.tpl.php new file mode 100644 index 000000000..2beb72172 --- /dev/null +++ b/tpl/cdn/other.tpl.php @@ -0,0 +1,174 @@ +conf(Base::O_CDN_MAPPING); +// Special handler: Append one row if somehow the DB default preset value got deleted +if (!$cdn_mapping) { + $this->load_default_vals(); + $cdn_mapping = self::$_default_options[Base::O_CDN_MAPPING]; +} + +$this->form_action(); +?> +

+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + title($id); ?> + + build_switch($id); ?> +
+ ' . __('ON', 'litespeed-cache') . ''); ?> + +
+ +
+ ' . __('OFF', 'litespeed-cache') . ''); ?> +
+
+ enroll(Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_URL . '][]'); ?> + enroll(Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_IMG . '][]'); ?> + enroll(Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_CSS . '][]'); ?> + enroll(Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_JS . '][]'); ?> + enroll(Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_FILETYPE . '][]'); ?> + +
+ + + +
+ : + +
+ +
+ title(Base::CDN_MAPPING_INC_IMG); ?>: + <img', 'url()'); ?> + +
+ title(Base::CDN_MAPPING_INC_CSS); ?>: + + +
+ title(Base::CDN_MAPPING_INC_JS); ?>: + + +
+ title(Base::CDN_MAPPING_FILETYPE); ?>: + + + src=""', 'data-src=""', 'href=""'); ?> + + +
+ ' . __('Include File Types', 'litespeed-cache') . ''); ?> + +
+ +
+ + title($id); ?> + + + + +
+ + +
element.attribute', '.attribute'); ?> + +
+
+ + title($id); ?> + + build_textarea($id); ?> +
+ //', '' . $home_url . ''); ?> +
*', '//www.aa.com', '//aa.com', '//*aa.com'); ?> + +
+
+ + title($id); ?> + + + +
+ + +
+
+ + title($id); ?> + + build_textarea($id); ?> +
+ + +
+
+ +form_end(); diff --git a/tpl/cdn/auto_setup.tpl.php b/tpl/cdn/qc.tpl.php similarity index 79% rename from tpl/cdn/auto_setup.tpl.php rename to tpl/cdn/qc.tpl.php index 919c0a6e3..5c4a2ec30 100644 --- a/tpl/cdn/auto_setup.tpl.php +++ b/tpl/cdn/qc.tpl.php @@ -4,6 +4,44 @@ defined('WPINC') || exit; +$this->form_action(); +?> + +

+ + +

+ + + + + + + + + + +
+ + title($id); ?> + + build_switch($id); ?> +
+ + +
+
+ +form_end(); +?> +
+

+
+ : My QUIC.cloud +
+ +conf(Base::O_QC_CNAME); if (!empty($setup_summary['cdn_setup_ts'])) { $cdn_setup_ts = $setup_summary['cdn_setup_ts']; @@ -52,11 +92,16 @@ $curr_status = ' ' . __('Paused', 'litespeed-cache'); $curr_status_subline = '

' . $cdn_setup_err . '

'; } else if ($cdn_setup_ts > 0) { - if (isset($nameservers)) { + if ($nameservers) { $curr_status = ' ' . __('Verifying, waiting for nameservers to be updated.', 'litespeed-cache') . ' ' . __('Click the refresh button below to refresh status.', 'litespeed-cache'); if (isset($setup_summary['cdn_verify_msg'])) { $curr_status_subline = '

' . __('Last Verification Result', 'litespeed-cache') . ': ' . $setup_summary['cdn_verify_msg'] . '

'; } + } elseif ($cname) { + $curr_status = ' ' . __('Verifying, waiting for cname to be updated.', 'litespeed-cache') . ' ' . __('Click the refresh button below to refresh status.', 'litespeed-cache'); + if (isset($setup_summary['cdn_verify_msg'])) { + $curr_status_subline = '

' . __('Last Verification Result', 'litespeed-cache') . ': ' . $setup_summary['cdn_verify_msg'] . '

'; + } } else { $curr_status = ' ' . __('In Progress', 'litespeed-cache'); $curr_status_subline = '

' . __('You will receive an email upon status update.', 'litespeed-cache') . ' ' . __('This process may take several minutes.', 'litespeed-cache') . '

'; @@ -66,52 +111,7 @@ } ?> -

- -

-

- -

-
    -
  1. -
  2. -
  3. -
  4. - - - -
  5. -
-

- -

- -

- -

- - -

- -

@@ -141,7 +141,7 @@

- +

@@ -193,7 +193,7 @@ - +

@@ -203,28 +203,21 @@ - -

- -

- -

- - - -

- -

- -

- - +

+ +

+ +

+ + + +

@@ -267,4 +260,53 @@
- \ No newline at end of file + + + + + + + + +

+ +

+

+ +

+
    +
  1. +
  2. +
  3. +
  4. + + + +
  5. +
+ +

+ +

+ +

+ +

+ \ No newline at end of file diff --git a/tpl/cdn/settings.tpl.php b/tpl/cdn/settings.tpl.php deleted file mode 100644 index 82f169363..000000000 --- a/tpl/cdn/settings.tpl.php +++ /dev/null @@ -1,239 +0,0 @@ -conf( Base::O_CDN_MAPPING ); -// Special handler: Append one row if somehow the DB default preset value got deleted -if ( ! $cdn_mapping ) { - $this->load_default_vals(); - $cdn_mapping = self::$_default_options[ Base::O_CDN_MAPPING ]; -} - -$this->form_action(); -?> - -

- - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - title( $id ); ?> - - build_switch( $id ); ?> -
- - -
-
- - title( $id ); ?> - - build_switch( $id ); ?> -
- ' . __( 'ON', 'litespeed-cache' ) . '' ); ?> - -
- -
- ' . __( 'OFF', 'litespeed-cache' ) . '' ); ?> -
-
- enroll( Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_URL . '][]' ); ?> - enroll( Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_IMG . '][]' ); ?> - enroll( Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_CSS . '][]' ); ?> - enroll( Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_INC_JS . '][]' ); ?> - enroll( Base::O_CDN_MAPPING . '[' . Base::CDN_MAPPING_FILETYPE . '][]' ); ?> - -
- - - -
- : - -
- -
- title( Base::CDN_MAPPING_INC_IMG ); ?>: - <img', 'url()' ); ?> - -
- title( Base::CDN_MAPPING_INC_CSS ); ?>: - - -
- title( Base::CDN_MAPPING_INC_JS ); ?>: - - -
- title( Base::CDN_MAPPING_FILETYPE ); ?>: - - - src=""', 'data-src=""', 'href=""' ); ?> - - -
- ' . __( 'Include File Types', 'litespeed-cache' ) . '' ); ?> - -
- -
- - title( $id ); ?> - - - - -
- - -
element.attribute', '.attribute' ); ?> - -
-
- - title( $id ); ?> - - build_textarea( $id ); ?> -
- //', '' . $home_url . '' ); ?> -
*', '//www.aa.com', '//aa.com', '//*aa.com' ); ?> - -
-
- - title( $id ); ?> - - - -
- - -
-
- - title( $id ); ?> - - build_textarea( $id ); ?> -
- - -
-
- - title( $id ); ?> - - build_switch( $id ); ?> -
- - ' . __( 'CDN', 'litespeed-cache' ) . ' -> ', __( 'Manage', 'litespeed-cache' ) .'' ); ?> -
-
-
- - - build_input( Base::O_CDN_CLOUDFLARE_KEY ); ?> -
- - %2$s.', 'litespeed-cache' ), 'href="https://dash.cloudflare.com/profile/api-tokens" target="_blank"', 'Cloudflare' ); ?> - -
-
- -
- - - build_input( Base::O_CDN_CLOUDFLARE_EMAIL ); ?> -
- - -
-
- -
- - - conf( Base::O_CDN_CLOUDFLARE_ZONE ); - $cls = $cf_zone ? ' litespeed-input-success' : ' litespeed-input-warning'; - $this->build_input( Base::O_CDN_CLOUDFLARE_NAME, $cls ); - ?> -
- - -
-
-
-
- -form_end(); - diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index d1c14e10d..1a6d0d15e 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -18,6 +18,7 @@ } $__cloud = Cloud::cls(); +$__cloud->finish_qc_activation(); $cloud_summary = Cloud::get_summary(); $css_summary = CSS::get_summary(); @@ -163,6 +164,13 @@ + +
+ Click here to enable QC service + +
+ +
@@ -190,18 +198,39 @@
diff --git a/tpl/general/settings.tpl.php b/tpl/general/settings.tpl.php index 49c64ff5f..339cf9435 100644 --- a/tpl/general/settings.tpl.php +++ b/tpl/general/settings.tpl.php @@ -6,37 +6,8 @@ $__cloud = Cloud::cls(); -// This will drop QS param `qc_res` and `domain_hash` also -$__cloud->parse_qc_redir(); - $cloud_summary = Cloud::get_summary(); -$can_token = $__cloud->can_token(); - -$is_requesting = !empty($cloud_summary['token_ts']) && (empty($cloud_summary['apikey_ts']) || $cloud_summary['token_ts'] > $cloud_summary['apikey_ts']); - -$apply_btn_txt = __('Request Domain Key', 'litespeed-cache'); -if ($this->conf(Base::O_API_KEY)) { - $apply_btn_txt = __('Refresh Domain Key', 'litespeed-cache'); - if ($is_requesting) { - $apply_btn_txt = __('Waiting for Refresh', 'litespeed-cache'); - } -} elseif ($is_requesting) { - $apply_btn_txt = __('Waiting for Approval', 'litespeed-cache'); -} - -$apply_ts_txt = ''; -if (!empty($cloud_summary['token_ts'])) { - $apply_ts_txt .= ' ' . __('Requested', 'litespeed-cache') . ': ' . Utility::readable_time($cloud_summary['token_ts']) . ''; -} -if (!empty($cloud_summary['apikey_ts'])) { - $apply_ts_txt .= ' ' . __('Approved', 'litespeed-cache') . ': ' . Utility::readable_time($cloud_summary['apikey_ts']) . ''; -} -if (!$can_token) { - $next_available_req = $cloud_summary['token_ts'] + Cloud::EXPIRATION_TOKEN - time(); - $apply_ts_txt .= ' ' . sprintf(__('Next available request time: After %s', 'litespeed-cache'), Utility::readable_time($next_available_req, 0, true)); -} - $this->form_action(); ?> @@ -45,109 +16,32 @@ +
+

+ ' data-litespeed-cfm=""> +

+

+ Service: ' . $svc . ' Node: ' . $cloud_summary['server.' . $svc] . ' Connected Date: ' . Utility::readable_time($cloud_summary['server_date.' . $svc]) . '

'; + } + } + if (!$has_service) { + echo __('No cloud services currently in use', 'litespeed-cache'); + } + ?> +

+
+ _is_multisite) : ?> - - - - - _is_multisite) : ?> diff --git a/tpl/img_optm/settings.media_webp.tpl.php b/tpl/img_optm/settings.media_webp.tpl.php index 07e243a60..93b65c067 100644 --- a/tpl/img_optm/settings.media_webp.tpl.php +++ b/tpl/img_optm/settings.media_webp.tpl.php @@ -11,7 +11,7 @@ title($id); ?>
- - title($id); ?> - - - build_input($id); ?> - - build_input($id, null, null, 'text', true); ?> - - - - - - - - - - - - - qc_link(), __('Visit My Dashboard on QUIC.cloud', 'litespeed-cache'), false, 'button litespeed-btn-success litespeed-right'); ?> - can_link_qc()) : ?> - - - - - - -
-

:

-

' . $apply_btn_txt . ''); ?>

-

:

-

1) ' . home_url() . '/' . (function_exists('rest_get_url_prefix') ? rest_get_url_prefix() : apply_filters('rest_url_prefix', 'wp-json')) . '/litespeed/v1/token'); ?>

-

2)

-

:

-
- - - -
-

:

-

-
- - - conf(Base::O_API_KEY)) : ?> -
-

:

-

' . Lang::title(Base::O_API_KEY) . '') . ' See Terms.'; ?>

-
- -
-

:

-

' . __('Link to QUIC.cloud', 'litespeed-cache') . ''); ?>

-

-
- - -
- - -
- - : - - - ⚠️ - - - -
- -
-

- ' data-litespeed-cfm=""> -

-

- Service: ' . $svc . ' Node: ' . $cloud_summary['server.' . $svc] . ' Connected Date: ' . Utility::readable_time($cloud_summary['server_date.' . $svc]) . '

'; - } - } - if (!$has_service) { - echo __('No cloud services currently in use', 'litespeed-cache'); - } - ?> -

-
-
-
- build_switch($id); ?> + build_switch($id, array(__('OFF', 'litespeed-cache'), __('WebP', 'litespeed-cache'), __('AVIF', 'litespeed-cache'))); ?>
diff --git a/tpl/inc/api_key.php b/tpl/inc/api_key.php deleted file mode 100644 index c9c05f000..000000000 --- a/tpl/inc/api_key.php +++ /dev/null @@ -1,14 +0,0 @@ -conf( Base::O_API_KEY ) ; - -?> - - -

- -

- - diff --git a/tpl/page_optm/settings_css.tpl.php b/tpl/page_optm/settings_css.tpl.php index cb401112e..f34aabd28 100644 --- a/tpl/page_optm/settings_css.tpl.php +++ b/tpl/page_optm/settings_css.tpl.php @@ -62,10 +62,10 @@ build_switch($id); ?>
- conf(Base::O_API_KEY)) : ?> + cls('Cloud')->activated()) : ?>

- +
@@ -175,10 +175,10 @@ build_switch($id); ?>
- conf(Base::O_API_KEY)) : ?> + cls('Cloud')->activated()) : ?>

- +
diff --git a/tpl/toolbox/report.tpl.php b/tpl/toolbox/report.tpl.php index 180d4e493..46bcb0e64 100644 --- a/tpl/toolbox/report.tpl.php +++ b/tpl/toolbox/report.tpl.php @@ -103,5 +103,3 @@

- - \ No newline at end of file