From 142f0bef9dfdb7df492d6b3e9cfa636fc3c78de5 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 16 Jul 2024 14:22:29 -0400 Subject: [PATCH 01/36] v6.4-a1: Regrouped CDN menu. --- tpl/cdn/{manage.tpl.php => cf.tpl.php} | 69 +++++- tpl/cdn/entry.tpl.php | 44 ++-- tpl/cdn/other.tpl.php | 174 +++++++++++++++ tpl/cdn/{auto_setup.tpl.php => qc.tpl.php} | 36 ++++ tpl/cdn/settings.tpl.php | 239 --------------------- 5 files changed, 298 insertions(+), 264 deletions(-) rename tpl/cdn/{manage.tpl.php => cf.tpl.php} (56%) create mode 100644 tpl/cdn/other.tpl.php rename tpl/cdn/{auto_setup.tpl.php => qc.tpl.php} (91%) delete mode 100644 tpl/cdn/settings.tpl.php 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 91% rename from tpl/cdn/auto_setup.tpl.php rename to tpl/cdn/qc.tpl.php index 919c0a6e3..72954da38 100644 --- a/tpl/cdn/auto_setup.tpl.php +++ b/tpl/cdn/qc.tpl.php @@ -4,6 +4,42 @@ defined('WPINC') || exit; +$this->form_action(); +?> + +

+ + +

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

+ +

: My QUIC.cloud

+ +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(); - From d823baac756cd17a3bda5c60fd84b468a451bfb3 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 22 Jul 2024 13:53:24 -0400 Subject: [PATCH 02/36] v6.4-a2 in dev: Auto CDN Setup for cname method --- src/base.cls.php | 3 +++ src/cdn-setup.cls.php | 10 ++++++---- tpl/cdn/qc.tpl.php | 13 ++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/base.cls.php b/src/base.cls.php index 48b8f61f2..f01d00ba8 100644 --- a/src/base.cls.php +++ b/src/base.cls.php @@ -275,6 +275,7 @@ class Base extends Root 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'; @@ -325,6 +326,7 @@ class Base extends Root self::O_CDN_ATTR, self::O_QC_TOKEN, self::O_QC_NAMESERVERS, + self::O_QC_CNAME, ); protected static $_default_options = array( @@ -551,6 +553,7 @@ class Base extends Root self::O_QC_TOKEN => '', self::O_QC_NAMESERVERS => '', + self::O_QC_CNAME => '', ); protected static $_default_site_options = array( diff --git a/src/cdn-setup.cls.php b/src/cdn-setup.cls.php index 789ffd558..39e4ca8a3 100644 --- a/src/cdn-setup.cls.php +++ b/src/cdn-setup.cls.php @@ -139,7 +139,9 @@ private function _process_cdn_status($result) $this->cls('Cloud')->set_linked(); $cname = esc_html($result['cname']); $this->cls('Conf')->update_confs(array(self::O_QC_CNAME => $cname, self::O_CDN_QUIC => true)); - Admin_Display::succeed('🎊 ' . __('Congratulations, QUIC.cloud successfully set this domain up for the CDN. Please update your cname to:', 'litespeed-cache') . $cname); + Admin_Display::succeed( + '🎊 ' . __('Congratulations, QUIC.cloud successfully set this domain up for the CDN. Please update your cname to:', 'litespeed-cache') . $cname + ); } elseif (isset($result['done'])) { if (isset($this->_summary['cdn_setup_err'])) { unset($this->_summary['cdn_setup_err']); @@ -150,10 +152,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 +224,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/tpl/cdn/qc.tpl.php b/tpl/cdn/qc.tpl.php index 72954da38..9a5cd6c0c 100644 --- a/tpl/cdn/qc.tpl.php +++ b/tpl/cdn/qc.tpl.php @@ -59,6 +59,8 @@ $cdn_setup_err = $setup_summary['cdn_setup_err']; } +$nameservers = array(); +$cname = $this->conf(Base::O_QC_CNAME); if (!empty($setup_summary['cdn_setup_ts'])) { $cdn_setup_ts = $setup_summary['cdn_setup_ts']; @@ -88,11 +90,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') . '

'; @@ -111,7 +118,7 @@
  1. -
  2. +
  3. @@ -120,7 +127,7 @@

- +

From b4e58d578399398a2e6b516a2d962196c6b593c1 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 23 Jul 2024 13:24:48 -0400 Subject: [PATCH 03/36] v6.4-a3: Corrected QC and LSADC cache hit status --- src/cdn-setup.cls.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cdn-setup.cls.php b/src/cdn-setup.cls.php index 39e4ca8a3..004e6ecb0 100644 --- a/src/cdn-setup.cls.php +++ b/src/cdn-setup.cls.php @@ -139,9 +139,7 @@ private function _process_cdn_status($result) $this->cls('Cloud')->set_linked(); $cname = esc_html($result['cname']); $this->cls('Conf')->update_confs(array(self::O_QC_CNAME => $cname, self::O_CDN_QUIC => true)); - Admin_Display::succeed( - '🎊 ' . __('Congratulations, QUIC.cloud successfully set this domain up for the CDN. Please update your cname to:', 'litespeed-cache') . $cname - ); + Admin_Display::succeed('🎊 ' . __('Congratulations, QUIC.cloud successfully set this domain up for the CDN. Please update your cname to:', 'litespeed-cache') . $cname); } elseif (isset($result['done'])) { if (isset($this->_summary['cdn_setup_err'])) { unset($this->_summary['cdn_setup_err']); From ace3a86327c6eca72855b495ee4e6992e90605b2 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 6 Aug 2024 18:17:19 -0400 Subject: [PATCH 04/36] v7.0-a1 in dev: QC activation; Removing domain key and auto cdn setup --- assets/css/litespeed.css | 1 + data/const.default.ini | 3 - src/base.cls.php | 9 +- src/cloud.cls.php | 421 ++++++++--------------------- src/conf.cls.php | 5 - src/error.cls.php | 4 +- src/img-optm.cls.php | 7 - src/lang.cls.php | 1 - src/optimize.cls.php | 2 +- src/rest.cls.php | 50 +--- src/ucss.cls.php | 7 - src/vpi.cls.php | 7 - tpl/cdn/qc.tpl.php | 145 +++++----- tpl/dash/dashboard.tpl.php | 21 +- tpl/general/settings.tpl.php | 146 ++-------- tpl/inc/api_key.php | 14 - tpl/page_optm/settings_css.tpl.php | 8 +- tpl/toolbox/report.tpl.php | 2 - 18 files changed, 230 insertions(+), 623 deletions(-) delete mode 100644 tpl/inc/api_key.php diff --git a/assets/css/litespeed.css b/assets/css/litespeed.css index 0269e3c52..787f1d0b1 100644 --- a/assets/css/litespeed.css +++ b/assets/css/litespeed.css @@ -2805,6 +2805,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/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/src/base.cls.php b/src/base.cls.php index f01d00ba8..ec6bde47b 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,7 +273,6 @@ 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'; @@ -308,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, @@ -324,7 +322,6 @@ 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, ); @@ -333,7 +330,6 @@ class Base extends Root 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, @@ -551,7 +547,6 @@ 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 => '', ); @@ -893,7 +888,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/cloud.cls.php b/src/cloud.cls.php index 7c3e7a9c2..3ccb73d85 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -18,6 +18,7 @@ class Cloud extends Base const CLOUD_SERVER_DASH = 'https://my.quic.cloud'; const CLOUD_SERVER_WP = 'https://wpapi.quic.cloud'; + const SVC_U_ACTIVATE = 'u/activate'; const SVC_D_NODES = 'd/nodes'; const SVC_D_SYNC_CONF = 'd/sync_conf'; const SVC_D_USAGE = 'd/usage'; @@ -42,15 +43,16 @@ class Cloud extends Base 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'; 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 +64,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 +94,9 @@ 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_LINK = 'link'; + const TYPE_ACTIVATE = 'activate'; const TYPE_SYNC_USAGE = 'sync_usage'; - private $_setup_token; protected $_summary; /** @@ -106,17 +106,78 @@ 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 6.4 */ - private function _api_key() + public function init_qc() { - return $this->conf(self::O_API_KEY); + if (!empty($this->_summary['sk'])) { + $keypair = sodium_crypto_box_keypair(); + $pk = sodium_crypto_box_publickey($keypair); + $sk = sodium_crypto_box_secretkey($keypair); + $this->_summary['pk'] = $pk; + $this->_summary['sk'] = $sk; + $this->save_summary(); + // ATM `qc_activated` = null + } + + // WPAPI REST echo dryrun + $req_data = array( + 'wp_pk' => $pk, + ); + $data = self::post(self::API_REST_ECHO, $req_data); + if (empty($data['sealed_encrypted']) || empty($data['sealed_encrypted_nonce'])) { + self::debug('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-general')); + return; + } + + // Activation redirect + $param = array( + 'site_url' => home_url(), + 'ver' => Core::VER, + 'data' => $data, + 'ref' => get_admin_url(null, 'admin.php?page=litespeed-general'), + ); + wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . Utility::arr2str($param)); + return; + } + + /** + * Finish qc activation after redirection back from QC + * + * @since 6.4 + */ + public function finish_qc_activation() + { + if (empty($_GET['qc_activated']) || !in_array($_GET['qc_activated'], array('anonymous', 'linked', 'cdn'))) { + 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::succeed('🎊 ' . $msg); + + $this->clear_cloud(); + + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); } /** @@ -608,7 +669,7 @@ private function _maybe_cloud($service_tag) } } 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 +695,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 6.4 + * @access public + */ + public function activated() + { + return !empty($this->_summary['sk']) && !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/wp?data=' . Utility::arr2str($data); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); + } + /** * Post data to QUIC.cloud server * @@ -686,7 +770,7 @@ private function _post($service, $data = false, $time_out = false) $param = array( 'site_url' => home_url(), - 'domain_key' => $this->_api_key(), + // 'domain_key' => $this->_api_key(), 'main_domain' => !empty($this->_summary['main_domain']) ? $this->_summary['main_domain'] : '', 'ver' => Core::VER, 'data' => $data, @@ -929,7 +1013,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,13 +1028,12 @@ 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'); @@ -1097,10 +1180,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; } @@ -1162,7 +1241,7 @@ public function ip_validate() // 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'); + return self::err('setup_required'); } $to_validate = substr($this->_api_key(), 0, 4); @@ -1180,290 +1259,6 @@ public function ip_validate() 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(); - - # 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; - } - /** * Check if this visit is from cloud or not * @@ -1600,12 +1395,8 @@ public function handler() $this->_clear_promo(); break; - case self::TYPE_GEN_KEY: - $this->gen_key(); - break; - - case self::TYPE_LINK: - $this->_link_to_qc(); + case self::TYPE_ACTIVATE: + $this->init_qc(); break; case self::TYPE_SYNC_USAGE: 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/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..65dafa538 100644 --- a/src/img-optm.cls.php +++ b/src/img-optm.cls.php @@ -705,13 +705,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']; diff --git a/src/lang.cls.php b/src/lang.cls.php index 7160dccd7..63ae44f81 100644 --- a/src/lang.cls.php +++ b/src/lang.cls.php @@ -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'), diff --git a/src/optimize.cls.php b/src/optimize.cls.php index c31f73982..79133fdbb 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')->activate()) { 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..f0023cda2 100644 --- a/src/rest.cls.php +++ b/src/rest.cls.php @@ -61,15 +61,10 @@ public function rest_api_init() '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'), @@ -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/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/qc.tpl.php b/tpl/cdn/qc.tpl.php index 9a5cd6c0c..5c4a2ec30 100644 --- a/tpl/cdn/qc.tpl.php +++ b/tpl/cdn/qc.tpl.php @@ -35,9 +35,11 @@ form_end(); ?> -

- -

: My QUIC.cloud

+
+

+
+ : My QUIC.cloud +
-

- -

-

- -

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

- -

- -

- -

- - -

- -

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

- +

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

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

- -

-
    - ' . $nameserver . ''; - } - ?> -
-

- - - -

- -

- -

- - +

+ +

+
    + ' . $nameserver . ''; + } + ?> +
+

+ + + +

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

+ +

+

+ +

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

+ +

+ +

+ +

+ \ No newline at end of file diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index d1c14e10d..1fc7ed580 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 + +
+ +
@@ -194,12 +202,15 @@ - - - can_link_qc()) : ?> - + + - +

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/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..3a2eada8d 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')->activate()) : ?>

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

- +
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 From 7db028d866765550c089d1680fc2317a082549f6 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 12 Aug 2024 14:38:31 -0400 Subject: [PATCH 05/36] Dropped img_optm prio and jumbo packages to improve code simplicity and focus on rate improvement. --- src/cloud.cls.php | 9 --------- src/img-optm.cls.php | 16 ---------------- 2 files changed, 25 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 3ccb73d85..cfe7d9e15 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -34,9 +34,6 @@ 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'; @@ -336,9 +333,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']; @@ -664,9 +658,6 @@ 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)) { diff --git a/src/img-optm.cls.php b/src/img-optm.cls.php index 65dafa538..6982d792e 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; @@ -892,19 +889,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); From 7ac9a5cfca9470eda58a949156b81791db8e492e Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Fri, 16 Aug 2024 13:21:00 -0400 Subject: [PATCH 06/36] v7.0-a1: * **Core** Minimum required PHP version escalated to PHP v7.2.0. * **Core** Minimum required WP version escalated to WP v5.3. --- litespeed-cache.php | 12 ++++++------ readme.txt | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index e7324a99c..0c8e965c5 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-a1 * 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-a1'); !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/readme.txt b/readme.txt index 681b7800e..823113640 100644 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ === LiteSpeed Cache === Contributors: LiteSpeedTech Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cache, redis, memcached, database cleaner -Requires at least: 4.9 +Requires at least: 5.3 Tested up to: 6.6.1 Stable tag: 6.5.0.2 License: GPLv3 @@ -254,6 +254,10 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == += 7.0 Nov 2024 = +* **Core** Minimum required PHP version escalated to PHP v7.2.0. +* **Core** Minimum required WP version escalated to WP v5.3. + = 6.5.0.2 - Sep 6 2024 = * **Debug** Compatibility improvement for WP installations w/o `AUTH_KEY` defined in `wp-config.php`. @@ -278,6 +282,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) From c2d5be5223052d61a4e00762496d0402e036a22e Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Fri, 16 Aug 2024 13:21:41 -0400 Subject: [PATCH 07/36] v7.0-a2 --- litespeed-cache.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index 0c8e965c5..b94daaf99 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: 7.0-a1 + * Version: 7.0-a2 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.0-a1'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a2'); !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 From 0333e4859a273accffdc8c09510fbf38caf21321 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Fri, 16 Aug 2024 17:46:19 -0400 Subject: [PATCH 08/36] v7.0-a3 in dev: QC service setup --- readme.txt | 3 +- src/cloud.cls.php | 255 ++++++++++++++++++++++++++-------------------- src/rest.cls.php | 4 +- 3 files changed, 150 insertions(+), 112 deletions(-) diff --git a/readme.txt b/readme.txt index 823113640..e3cabffb0 100644 --- a/readme.txt +++ b/readme.txt @@ -254,9 +254,10 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == -= 7.0 Nov 2024 = += 7.0 - Nov 2024 = * **Core** Minimum required PHP version escalated to PHP v7.2.0. * **Core** Minimum required WP version escalated to WP v5.3. +* **3rd** Correct the integration with User Switching (John Blackbourn #725) = 6.5.0.2 - Sep 6 2024 = * **Debug** Compatibility improvement for WP installations w/o `AUTH_KEY` defined in `wp-config.php`. diff --git a/src/cloud.cls.php b/src/cloud.cls.php index cfe7d9e15..f2dc809b0 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -13,12 +13,12 @@ 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/activate'; + const SVC_U_ACTIVATE = 'u/wp3/activate'; const SVC_D_NODES = 'd/nodes'; const SVC_D_SYNC_CONF = 'd/sync_conf'; const SVC_D_USAGE = 'd/usage'; @@ -47,6 +47,7 @@ class Cloud extends Base const API_VER = 'ver_check'; const API_BETA_TEST = 'beta_test'; const API_REST_ECHO = 'tool/wp_rest_echo'; + const API_SERVER_KEY = 'server_key'; private static $CENTER_SVC_SET = array( self::SVC_U_ACTIVATE, @@ -61,10 +62,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, self::API_REST_ECHO); + private static $WP_SVC_SET = array(self::API_NEWS, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO, self::API_SERVER_KEY); // 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, self::API_REST_ECHO); + private static $_PUB_SVC_SET = array(self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO, self::API_SERVER_KEY); private static $_QUEUE_SVC_SET = array(self::SVC_UCSS, self::SVC_VPI); @@ -109,14 +110,14 @@ public function __construct() /** * Init QC setup * - * @since 6.4 + * @since 7.0 */ public function init_qc() { - if (!empty($this->_summary['sk'])) { + if (empty($this->_summary['sk'])) { $keypair = sodium_crypto_box_keypair(); - $pk = sodium_crypto_box_publickey($keypair); - $sk = sodium_crypto_box_secretkey($keypair); + $pk = bin2hex(sodium_crypto_box_publickey($keypair)); + $sk = bin2hex(sodium_crypto_box_secretkey($keypair)); $this->_summary['pk'] = $pk; $this->_summary['sk'] = $sk; $this->save_summary(); @@ -125,17 +126,39 @@ public function init_qc() // WPAPI REST echo dryrun $req_data = array( - 'wp_pk' => $pk, + 'wp_pk' => $this->_summary['pk'], ); $data = self::post(self::API_REST_ECHO, $req_data); - if (empty($data['sealed_encrypted']) || empty($data['sealed_encrypted_nonce'])) { + if (empty($data['_res']) || $data['_res'] != 'ok') { self::debug('REST Echo Failed!'); $msg = __('Your WP REST API seems blocked our QIUC.cloud server calls.', 'litespeed-cache'); + if (!empty($data['code'])) { + $msg .= ' (' . $data['code'] . ')'; + } Admin_Display::error($msg); wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); return; } + self::debug("echo succeeded"); + + // Load seperate thread echoed data from storage + $echobox = self::get_option('echobox', array()); + if (empty($echobox['data_encrypted']) || empty($echobox['data_encrypted_nonce'])) { + Admin_Display::error(__('Failed to load sealed box data from WPAPI', 'litespeed-cache')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); + return; + } + + $data = array( + 'data_encrypted' => $echobox['data_encrypted'], + 'data_encrypted_nonce' => $echobox['data_encrypted_nonce'], + ); + $server_ip = $this->conf(self::O_SERVER_IP); + if ($server_ip) { + $data['server_ip'] = $server_ip; + } + // Activation redirect $param = array( 'site_url' => home_url(), @@ -147,10 +170,95 @@ public function init_qc() return; } + /** + * Encrypt data for cloud req + * + * @since 7.0 + */ + private function _encrypt($data, $from_wpapi = false) + { + $keypair = $this->_load_server_pk_pair($from_wpapi); + $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); + return sodium_crypto_box($data, $nonce, $keypair); + } + + /** + * Load server pk from cloud + * + * @since 7.0 + */ + private function _load_server_pk_pair($from_wpapi = false) + { + // Load cloud pk + $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY; + if ($from_wpapi) { + $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY; + } + $resp = wp_remote_get($server_key_url); + if (is_wp_error($resp)) { + self::debug('Failed to load key: ' . $resp->get_error_message()); + return false; + } + self::debug('Loaded key from ' . $server_key_url . ': ' . $resp['body']); + $cloud_pk = base64_decode($resp['body']); + $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey(hex2bin($this->_summary['sk']), $cloud_pk); + return $keypair; + } + + /** + * Decrypt cloud response encrypted box + * + * @since 7.0 + */ + private function _decrypt($data, $nonce, $from_wpapi = false) + { + // Try decryption + try { + $keypair = $this->_load_server_pk_pair($from_wpapi); + $databox_raw = sodium_crypto_box_open($data, $nonce, $keypair); + } catch (\SodiumException $e) { + return false; + } + self::debug('Decrypted info: ', $databox_raw); + return $databox_raw; + } + + /** + * WPAPI echo back to notify the sealed databox + * + * @since 7.0 + */ + public function wp_rest_echo() + { + self::debug('Parsing echo', $_POST); + + if (empty($_POST['sealed_encrypted']) || empty($_POST['sealed_encrypted_nonce'])) { + return self::err('No sealed data'); + } + + // open sealed box + try { + $databox_raw = $this->_decrypt($_POST['sealed_encrypted'], $_POST['sealed_encrypted_nonce'], true); + $databox = \json_decode($databox_raw, true); + } catch (\SodiumException $e) { + self::debug("❌ Decryption failed: " . $e->getMessage()); + return self::err('Decryption failed: ' . $e->getMessage()); + } + + self::debug("sealed box ", $databox_raw); + + if (empty($databox['data_encrypted']) || empty($databox['data_encrypted_nonce'])) { + return self::err('Missing data_encrypted or nonce'); + } + + self::update_option('echobox', $databox); + return self::err('nonoo'); + } + /** * Finish qc activation after redirection back from QC * - * @since 6.4 + * @since 7.0 */ public function finish_qc_activation() { @@ -595,7 +703,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, ); @@ -648,7 +755,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; } @@ -697,7 +804,7 @@ private function _maybe_cloud($service_tag) /** * Check if activated QUIC.cloud service or not * - * @since 6.4 + * @since 7.0 * @access public */ public function activated() @@ -759,9 +866,16 @@ private function _post($service, $data = false, $time_out = false) $data['service_type'] = $service; // For queue distribution usage } + // Encrypt service as signature + $signature = $this->_encrypt($service_tag); + $data['signature'] = array( + 'service_tag' => $service_tag, + 'ts' => time(), + 'signature' => $signature, + ); + $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, @@ -1039,27 +1153,17 @@ private function _reset_qc_reg() */ 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'); @@ -1097,72 +1201,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 * @@ -1224,30 +1262,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('setup_required'); + if (empty($_POST['data']) || empty($_POST['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'], $_POST['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 . ')'); + self::debug('__callback IP request hash: ' . $data_orig); - return self::ok(array('hash' => md5($res_hash))); + return self::ok(array('data_orig' => $data_orig)); } /** @@ -1329,6 +1365,7 @@ public static function ok($data = array()) */ public static function err($code) { + self::debug("❌ Error response code: $code"); return array('_res' => 'err', '_msg' => $code); } diff --git a/src/rest.cls.php b/src/rest.cls.php index f0023cda2..9eca48825 100644 --- a/src/rest.cls.php +++ b/src/rest.cls.php @@ -55,7 +55,7 @@ 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'), @@ -100,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'), From 7783355fb21a30c0ad41e5b0d16035d7c6fe7103 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 19 Aug 2024 14:57:32 -0400 Subject: [PATCH 09/36] debugErr() func --- src/cloud.cls.php | 40 ++++++++++++++++++++++++++++++---------- src/root.cls.php | 11 +++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index f2dc809b0..b6a399449 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -196,12 +196,30 @@ private function _load_server_pk_pair($from_wpapi = false) } $resp = wp_remote_get($server_key_url); if (is_wp_error($resp)) { - self::debug('Failed to load key: ' . $resp->get_error_message()); + self::debugErr('Failed to load key: ' . $resp->get_error_message()); return false; } - self::debug('Loaded key from ' . $server_key_url . ': ' . $resp['body']); - $cloud_pk = base64_decode($resp['body']); - $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey(hex2bin($this->_summary['sk']), $cloud_pk); + $pk = trim($resp['body']); + self::debug('Loaded key from ' . $server_key_url . ': ' . $pk); + $cloud_pk = base64_decode($pk); + if (strlen($cloud_pk) !== SODIUM_CRYPTO_BOX_PUBLICKEYBYTES) { + self::debugErr('Invalid cloud public key length.'); + return false; + } + + $sk = hex2bin($this->_summary['sk']); + if (strlen($sk) !== SODIUM_CRYPTO_BOX_SECRETKEYBYTES) { + self::debugErr('Invalid local secret key length.'); + // Reset local pk/sk + unset($this->_summary['pk']); + unset($this->_summary['sk']); + $this->save_summary(); + self::debug('Unset local pk/sk pair.'); + + return false; + } + + $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($sk, $cloud_pk); return $keypair; } @@ -215,8 +233,12 @@ private function _decrypt($data, $nonce, $from_wpapi = false) // Try decryption try { $keypair = $this->_load_server_pk_pair($from_wpapi); + if ($keypair == false) { + return false; + } $databox_raw = sodium_crypto_box_open($data, $nonce, $keypair); } catch (\SodiumException $e) { + self::debugErr("Decryption failed: " . $e->getMessage()); return false; } self::debug('Decrypted info: ', $databox_raw); @@ -237,14 +259,12 @@ public function wp_rest_echo() } // open sealed box - try { - $databox_raw = $this->_decrypt($_POST['sealed_encrypted'], $_POST['sealed_encrypted_nonce'], true); - $databox = \json_decode($databox_raw, true); - } catch (\SodiumException $e) { - self::debug("❌ Decryption failed: " . $e->getMessage()); - return self::err('Decryption failed: ' . $e->getMessage()); + $databox_raw = $this->_decrypt($_POST['sealed_encrypted'], $_POST['sealed_encrypted_nonce'], true); + if ($databox_raw == false) { + return self::err('Opening sealed data from WPAPI REST echo failed'); } + $databox = \json_decode($databox_raw, true); self::debug("sealed box ", $databox_raw); if (empty($databox['data_encrypted']) || empty($databox['data_encrypted_nonce'])) { 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. * From 60043fc581ce3a2a836145c86646c61308a471b6 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 19 Aug 2024 15:02:03 -0400 Subject: [PATCH 10/36] cloud key address fix --- src/cloud.cls.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index b6a399449..96c552b9e 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -190,9 +190,9 @@ private function _encrypt($data, $from_wpapi = false) private function _load_server_pk_pair($from_wpapi = false) { // Load cloud pk - $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY; + $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY; if ($from_wpapi) { - $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY; + $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY; } $resp = wp_remote_get($server_key_url); if (is_wp_error($resp)) { From 2f92f6ccf483011330c6f71d6531e4d404b5da57 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 20 Aug 2024 11:42:00 -0400 Subject: [PATCH 11/36] v7.0-a3: * **Cloud** Dropped domain key. Used sodium encryption for authentication and validation. --- litespeed-cache.php | 4 +- readme.txt | 1 + src/cloud.cls.php | 100 +++++++++++++++++++++++++------------------- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index b94daaf99..4f9644180 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: 7.0-a2 + * Version: 7.0-a3 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.0-a2'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a3'); !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 diff --git a/readme.txt b/readme.txt index e3cabffb0..372bc7b33 100644 --- a/readme.txt +++ b/readme.txt @@ -257,6 +257,7 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro = 7.0 - Nov 2024 = * **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. * **3rd** Correct the integration with User Switching (John Blackbourn #725) = 6.5.0.2 - Sep 6 2024 = diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 96c552b9e..c53e03705 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -114,27 +114,24 @@ public function __construct() */ public function init_qc() { - if (empty($this->_summary['sk'])) { + if (empty($this->_summary['sk_b64'])) { $keypair = sodium_crypto_box_keypair(); - $pk = bin2hex(sodium_crypto_box_publickey($keypair)); - $sk = bin2hex(sodium_crypto_box_secretkey($keypair)); - $this->_summary['pk'] = $pk; - $this->_summary['sk'] = $sk; + $pk = base64_encode(sodium_crypto_box_publickey($keypair)); + $sk = base64_encode(sodium_crypto_box_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' => $this->_summary['pk'], + 'wp_pk_b64' => $this->_summary['pk_b64'], ); - $data = self::post(self::API_REST_ECHO, $req_data); - if (empty($data['_res']) || $data['_res'] != 'ok') { - self::debug('REST Echo Failed!'); + $res = self::post(self::API_REST_ECHO, $req_data); + if ($res === false) { + self::debugErr('REST Echo Failed!'); $msg = __('Your WP REST API seems blocked our QIUC.cloud server calls.', 'litespeed-cache'); - if (!empty($data['code'])) { - $msg .= ' (' . $data['code'] . ')'; - } Admin_Display::error($msg); wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); return; @@ -144,15 +141,15 @@ public function init_qc() // Load seperate thread echoed data from storage $echobox = self::get_option('echobox', array()); - if (empty($echobox['data_encrypted']) || empty($echobox['data_encrypted_nonce'])) { + if (empty($echobox['data_encrypted_b64']) || empty($echobox['data_encrypted_nonce_b64'])) { Admin_Display::error(__('Failed to load sealed box data from WPAPI', 'litespeed-cache')); wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); return; } $data = array( - 'data_encrypted' => $echobox['data_encrypted'], - 'data_encrypted_nonce' => $echobox['data_encrypted_nonce'], + 'data_encrypted_b64' => $echobox['data_encrypted_b64'], + 'data_encrypted_nonce_b64' => $echobox['data_encrypted_nonce_b64'], ); $server_ip = $this->conf(self::O_SERVER_IP); if ($server_ip) { @@ -167,7 +164,7 @@ public function init_qc() 'ref' => get_admin_url(null, 'admin.php?page=litespeed-general'), ); wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . Utility::arr2str($param)); - return; + exit(); } /** @@ -178,6 +175,9 @@ public function init_qc() private function _encrypt($data, $from_wpapi = false) { $keypair = $this->_load_server_pk_pair($from_wpapi); + if (strlen($keypair) !== SODIUM_CRYPTO_BOX_KEYPAIRBYTES) { + return false; + } $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); return sodium_crypto_box($data, $nonce, $keypair); } @@ -207,12 +207,12 @@ private function _load_server_pk_pair($from_wpapi = false) return false; } - $sk = hex2bin($this->_summary['sk']); + $sk = base64_decode($this->_summary['sk_b64']); if (strlen($sk) !== SODIUM_CRYPTO_BOX_SECRETKEYBYTES) { self::debugErr('Invalid local secret key length.'); // Reset local pk/sk - unset($this->_summary['pk']); - unset($this->_summary['sk']); + unset($this->_summary['pk_b64']); + unset($this->_summary['sk_b64']); $this->save_summary(); self::debug('Unset local pk/sk pair.'); @@ -254,12 +254,19 @@ public function wp_rest_echo() { self::debug('Parsing echo', $_POST); - if (empty($_POST['sealed_encrypted']) || empty($_POST['sealed_encrypted_nonce'])) { + if (empty($_POST['sealed_encrypted_b64']) || empty($_POST['sealed_encrypted_nonce_b64'])) { return self::err('No sealed data'); } + $sealed_encrypted = base64_decode($_POST['sealed_encrypted_b64'], true); + $sealed_encrypted_nonce = base64_decode($_POST['sealed_encrypted_nonce_b64'], true); + + if (strlen($sealed_encrypted_nonce) !== SODIUM_CRYPTO_BOX_NONCEBYTES) { + return self::err('Invalid nonce size'); + } + // open sealed box - $databox_raw = $this->_decrypt($_POST['sealed_encrypted'], $_POST['sealed_encrypted_nonce'], true); + $databox_raw = $this->_decrypt($sealed_encrypted, $sealed_encrypted_nonce, true); if ($databox_raw == false) { return self::err('Opening sealed data from WPAPI REST echo failed'); } @@ -267,12 +274,12 @@ public function wp_rest_echo() $databox = \json_decode($databox_raw, true); self::debug("sealed box ", $databox_raw); - if (empty($databox['data_encrypted']) || empty($databox['data_encrypted_nonce'])) { + if (empty($databox['data_encrypted_b64']) || empty($databox['data_encrypted_nonce_b64'])) { return self::err('Missing data_encrypted or nonce'); } self::update_option('echobox', $databox); - return self::err('nonoo'); + return self::ok(array('sealed_md5' => md5($databox_raw))); } /** @@ -737,7 +744,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); } @@ -829,7 +840,7 @@ private function _maybe_cloud($service_tag) */ public function activated() { - return !empty($this->_summary['sk']) && !empty($this->_summary['qc_activated']); + return !empty($this->_summary['sk_b64']) && !empty($this->_summary['qc_activated']); } /** @@ -903,7 +914,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); } @@ -935,13 +951,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"; @@ -955,43 +971,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.', @@ -1001,15 +1017,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( @@ -1287,12 +1303,12 @@ public function ip_validate() return self::err('setup_required'); } - if (empty($_POST['data']) || empty($_POST['nonce'])) { + if (empty($_POST['data_b64']) || empty($_POST['data_b64_nonce'])) { return self::err('lack_of_params'); } // Decrypt the data_orig and respond - $data_orig = $this->_decrypt($_POST['data'], $_POST['nonce']); + $data_orig = $this->_decrypt($_POST['data_b64'], $_POST['data_b64_nonce']); if (!$data_orig) { self::debug('__callback IP request decryption failed'); From d975978f603e5a10be792b1e3f2b099c41d96b55 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 20 Aug 2024 15:19:23 -0400 Subject: [PATCH 12/36] tpl correction --- src/optimize.cls.php | 2 +- tpl/page_optm/settings_css.tpl.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/optimize.cls.php b/src/optimize.cls.php index 79133fdbb..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->cls('Cloud')->activate()) { + 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/tpl/page_optm/settings_css.tpl.php b/tpl/page_optm/settings_css.tpl.php index 3a2eada8d..f34aabd28 100644 --- a/tpl/page_optm/settings_css.tpl.php +++ b/tpl/page_optm/settings_css.tpl.php @@ -62,7 +62,7 @@ build_switch($id); ?>
- cls('Cloud')->activate()) : ?> + cls('Cloud')->activated()) : ?>

@@ -175,7 +175,7 @@ build_switch($id); ?>
- cls('Cloud')->activate()) : ?> + cls('Cloud')->activated()) : ?>

From 1601bae9014a5fcedb64936842f0394b9267178c Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 21 Aug 2024 12:31:53 -0400 Subject: [PATCH 13/36] changlog --- readme.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.txt b/readme.txt index 372bc7b33..f74fcd0e6 100644 --- a/readme.txt +++ b/readme.txt @@ -258,6 +258,9 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro * **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. +* **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** Correct the integration with User Switching (John Blackbourn #725) = 6.5.0.2 - Sep 6 2024 = From 7fdfd544e272d342837f9918d28f186684becad7 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 21 Aug 2024 12:38:09 -0400 Subject: [PATCH 14/36] v7.0-a4: * **GUI** Switch buttons rtl compatibility. (Eliza/Mehrshad Darzi #603) --- assets/css/litespeed.css | 4 ++++ litespeed-cache.php | 4 ++-- readme.txt | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/assets/css/litespeed.css b/assets/css/litespeed.css index 787f1d0b1..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), diff --git a/litespeed-cache.php b/litespeed-cache.php index 4f9644180..d78ca24d5 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: 7.0-a3 + * Version: 7.0-a4 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.0-a3'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a4'); !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 diff --git a/readme.txt b/readme.txt index f74fcd0e6..df19d2901 100644 --- a/readme.txt +++ b/readme.txt @@ -261,6 +261,7 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro * **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) +* **GUI** Switch buttons rtl compatibility. (Eliza/Mehrshad Darzi #603) * **3rd** Correct the integration with User Switching (John Blackbourn #725) = 6.5.0.2 - Sep 6 2024 = From 833fb68f21f1c5e6c6de5c3704f2c53206cc8dd3 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 21 Aug 2024 14:53:58 -0400 Subject: [PATCH 15/36] v7.0-a5: * **Core** Added rewrite rule to disable visits to `.log` files. --- litespeed-cache.php | 4 ++-- readme.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index d78ca24d5..f58e90584 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: 7.0-a4 + * Version: 7.0-a5 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.0-a4'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a5'); !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 diff --git a/readme.txt b/readme.txt index df19d2901..bce278938 100644 --- a/readme.txt +++ b/readme.txt @@ -257,6 +257,7 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro = 7.0 - Nov 2024 = * **Core** Minimum required PHP version escalated to PHP v7.2.0. * **Core** Minimum required WP version escalated to WP v5.3. +* **Core** Added rewrite rule to disable visits to `.log` files. * **Cloud** Dropped domain key. Used sodium encryption for authentication and validation. * **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). From f8a671ca0967859bcab8d3ec959adf75542f3360 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Thu, 22 Aug 2024 15:07:05 -0400 Subject: [PATCH 16/36] v7 changelog update --- readme.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/readme.txt b/readme.txt index bce278938..8d687a687 100644 --- a/readme.txt +++ b/readme.txt @@ -257,13 +257,8 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro = 7.0 - Nov 2024 = * **Core** Minimum required PHP version escalated to PHP v7.2.0. * **Core** Minimum required WP version escalated to WP v5.3. -* **Core** Added rewrite rule to disable visits to `.log` files. -* **Cloud** Dropped domain key. Used sodium encryption for authentication and validation. -* **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) +* **Cloud** Dropped `Domain Key`. Used sodium encryption for authentication and validation. * **GUI** Switch buttons rtl compatibility. (Eliza/Mehrshad Darzi #603) -* **3rd** Correct the integration with User Switching (John Blackbourn #725) = 6.5.0.2 - Sep 6 2024 = * **Debug** Compatibility improvement for WP installations w/o `AUTH_KEY` defined in `wp-config.php`. From 69291b1dfeb409e1235ca9798adefb508096d232 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 26 Aug 2024 14:09:42 -0400 Subject: [PATCH 17/36] Jump back to dash after QC activation --- src/cloud.cls.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index c53e03705..80d32af59 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -133,7 +133,7 @@ public function init_qc() 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-general')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); return; } @@ -143,7 +143,7 @@ public function init_qc() $echobox = self::get_option('echobox', array()); if (empty($echobox['data_encrypted_b64']) || empty($echobox['data_encrypted_nonce_b64'])) { Admin_Display::error(__('Failed to load sealed box data from WPAPI', 'litespeed-cache')); - wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); return; } @@ -161,7 +161,7 @@ public function init_qc() 'site_url' => home_url(), 'ver' => Core::VER, 'data' => $data, - 'ref' => get_admin_url(null, 'admin.php?page=litespeed-general'), + 'ref' => get_admin_url(null, 'admin.php?page=litespeed'), ); wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . Utility::arr2str($param)); exit(); @@ -309,7 +309,7 @@ public function finish_qc_activation() $this->clear_cloud(); - wp_redirect(get_admin_url(null, 'admin.php?page=litespeed-general')); + wp_redirect(get_admin_url(null, 'admin.php?page=litespeed')); } /** @@ -1178,7 +1178,7 @@ private function _reset_qc_reg() 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); } From 2f3920878e639ebafe6ab6a4e3b1f2f765185513 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 26 Aug 2024 14:23:57 -0400 Subject: [PATCH 18/36] v7.0-a6: QC activation callback updated to encryption validation. --- litespeed-cache.php | 4 ++-- src/cloud.cls.php | 32 ++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index f58e90584..f7445ea13 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: 7.0-a5 + * Version: 7.0-a6 * Author: LiteSpeed Technologies * Author URI: https://www.litespeedtech.com * License: GPLv3 @@ -34,7 +34,7 @@ return; } -!defined('LSCWP_V') && define('LSCWP_V', '7.0-a5'); +!defined('LSCWP_V') && define('LSCWP_V', '7.0-a6'); !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 diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 80d32af59..90b9133ad 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -289,18 +289,42 @@ public function wp_rest_echo() */ public function finish_qc_activation() { - if (empty($_GET['qc_activated']) || !in_array($_GET['qc_activated'], array('anonymous', 'linked', 'cdn'))) { + if (empty($_GET['data_encrypted_b64']) || empty($_GET['data_encrypted_nonce_b64'])) { return; } - $this->_summary['qc_activated'] = $_GET['qc_activated']; + $data_enc = base64_decode($_GET['data_encrypted_b64']); + $data_enc_nonce = base64_decode($_GET['data_encrypted_nonce_b64']); + $databox_raw = $this->_decrypt($data_enc, $data_enc_nonce); + if ($databox_raw == false) { + self::debugErr("Failed to decrypt qc activation data"); + Admin_Display::error(sprintf(__('Failed to decrypt %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + $databox = \json_decode($databox_raw, true); + self::debug("sealed box from finish_qc_activation", $databox_raw); + + if (empty($databox['qc_activated']) || !in_array($databox['qc_activated'], array('anonymous', 'linked', 'cdn'))) { + self::debugErr("Failed to parse qc activation data", $databox); + Admin_Display::error(sprintf(__('Failed to parse %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + if (empty($databox['ts']) || abs(time() - $databox['ts']) > 86400) { + self::debugErr("QC activation data timeout", $databox); + Admin_Display::error(sprintf(__('%s activation data expired.', 'litespeed-cache'), 'QUIC.cloud')); + return; + } + + $this->_summary['qc_activated'] = $databox['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') { + if ($databox['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') { + if ($databox['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)); From 2a6430ad68c2d34017be8f7d2ce9717979d981db Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 26 Aug 2024 14:57:34 -0400 Subject: [PATCH 19/36] typo fix --- tpl/dash/dashboard.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index 1fc7ed580..4e7c9b5c6 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -202,7 +202,7 @@ - + Date: Tue, 27 Aug 2024 14:29:26 -0400 Subject: [PATCH 20/36] Used signature instead of encryption for QC/WPAPI validation --- src/cloud.cls.php | 192 +++++++++++++++++++++---------------- tpl/dash/dashboard.tpl.php | 2 +- 2 files changed, 109 insertions(+), 85 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 90b9133ad..57e516ac8 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -19,6 +19,7 @@ class Cloud extends Base 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'; @@ -93,6 +94,7 @@ class Cloud extends Base const TYPE_REDETECT_CLOUD = 'redetect_cloud'; const TYPE_CLEAR_CLOUD = 'clear_cloud'; const TYPE_ACTIVATE = 'activate'; + const TYPE_LINK = 'link'; const TYPE_SYNC_USAGE = 'sync_usage'; protected $_summary; @@ -115,9 +117,9 @@ public function __construct() public function init_qc() { if (empty($this->_summary['sk_b64'])) { - $keypair = sodium_crypto_box_keypair(); - $pk = base64_encode(sodium_crypto_box_publickey($keypair)); - $sk = base64_encode(sodium_crypto_box_secretkey($keypair)); + $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(); @@ -128,8 +130,8 @@ public function init_qc() $req_data = array( 'wp_pk_b64' => $this->_summary['pk_b64'], ); - $res = self::post(self::API_REST_ECHO, $req_data); - if ($res === false) { + $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); @@ -140,16 +142,16 @@ public function init_qc() self::debug("echo succeeded"); // Load seperate thread echoed data from storage - $echobox = self::get_option('echobox', array()); - if (empty($echobox['data_encrypted_b64']) || empty($echobox['data_encrypted_nonce_b64'])) { - Admin_Display::error(__('Failed to load sealed box data from WPAPI', 'litespeed-cache')); + 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( - 'data_encrypted_b64' => $echobox['data_encrypted_b64'], - 'data_encrypted_nonce_b64' => $echobox['data_encrypted_nonce_b64'], + '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) { @@ -163,7 +165,36 @@ public function init_qc() 'data' => $data, 'ref' => get_admin_url(null, 'admin.php?page=litespeed'), ); - wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . Utility::arr2str($param)); + wp_redirect(self::CLOUD_SERVER_DASH . '/' . self::SVC_U_ACTIVATE . '?data=' . urlencode(Utility::arr2str($param))); + exit(); + } + + /** + * Link to QC setup + * + * @since 7.0 + */ + public function link_qc() + { + 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(); } @@ -172,14 +203,13 @@ public function init_qc() * * @since 7.0 */ - private function _encrypt($data, $from_wpapi = false) + private function _sign_b64($data) { - $keypair = $this->_load_server_pk_pair($from_wpapi); - if (strlen($keypair) !== SODIUM_CRYPTO_BOX_KEYPAIRBYTES) { - return false; + if (!$this->activated()) { + return ''; } - $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); - return sodium_crypto_box($data, $nonce, $keypair); + $signature = sodium_crypto_sign_detached($data, base64_decode($this->_summary['sk_b64'])); + return base64_encode($signature); } /** @@ -187,12 +217,12 @@ private function _encrypt($data, $from_wpapi = false) * * @since 7.0 */ - private function _load_server_pk_pair($from_wpapi = false) + private function _load_server_pk($from_wpapi = false) { // Load cloud pk - $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY; + $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY . '?type=sign'; if ($from_wpapi) { - $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY; + $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY . '?type=sign'; } $resp = wp_remote_get($server_key_url); if (is_wp_error($resp)) { @@ -202,13 +232,13 @@ private function _load_server_pk_pair($from_wpapi = 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_BOX_PUBLICKEYBYTES) { + 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_BOX_SECRETKEYBYTES) { + if (strlen($sk) !== SODIUM_CRYPTO_SIGN_SECRETKEYBYTES) { self::debugErr('Invalid local secret key length.'); // Reset local pk/sk unset($this->_summary['pk_b64']); @@ -219,30 +249,7 @@ private function _load_server_pk_pair($from_wpapi = false) return false; } - $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($sk, $cloud_pk); - return $keypair; - } - - /** - * Decrypt cloud response encrypted box - * - * @since 7.0 - */ - private function _decrypt($data, $nonce, $from_wpapi = false) - { - // Try decryption - try { - $keypair = $this->_load_server_pk_pair($from_wpapi); - if ($keypair == false) { - return false; - } - $databox_raw = sodium_crypto_box_open($data, $nonce, $keypair); - } catch (\SodiumException $e) { - self::debugErr("Decryption failed: " . $e->getMessage()); - return false; - } - self::debug('Decrypted info: ', $databox_raw); - return $databox_raw; + return $cloud_pk; } /** @@ -254,32 +261,44 @@ public function wp_rest_echo() { self::debug('Parsing echo', $_POST); - if (empty($_POST['sealed_encrypted_b64']) || empty($_POST['sealed_encrypted_nonce_b64'])) { - return self::err('No sealed data'); + if (empty($_POST['wpapi_ts']) || empty($_POST['wpapi_signature_b64'])) { + return self::err('No echo data'); } - $sealed_encrypted = base64_decode($_POST['sealed_encrypted_b64'], true); - $sealed_encrypted_nonce = base64_decode($_POST['sealed_encrypted_nonce_b64'], true); - - if (strlen($sealed_encrypted_nonce) !== SODIUM_CRYPTO_BOX_NONCEBYTES) { - return self::err('Invalid nonce size'); + $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'); } - // open sealed box - $databox_raw = $this->_decrypt($sealed_encrypted, $sealed_encrypted_nonce, true); - if ($databox_raw == false) { - return self::err('Opening sealed data 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'); } + return self::ok(array('signature_b64' => $this->_sign_b64($_POST['wpapi_ts']))); + } - $databox = \json_decode($databox_raw, true); - self::debug("sealed box ", $databox_raw); - - if (empty($databox['data_encrypted_b64']) || empty($databox['data_encrypted_nonce_b64'])) { - return self::err('Missing data_encrypted or nonce'); + /** + * 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::update_option('echobox', $databox); - return self::ok(array('sealed_md5' => md5($databox_raw))); + self::debug('Signature validation result: ' . ($is_valid ? 'true' : 'false')); + return $is_valid; } /** @@ -289,42 +308,43 @@ public function wp_rest_echo() */ public function finish_qc_activation() { - if (empty($_GET['data_encrypted_b64']) || empty($_GET['data_encrypted_nonce_b64'])) { + if (empty($_GET['qc_activated']) || empty($_GET['qc_ts']) || empty($_GET['qc_signature_b64'])) { return; } - $data_enc = base64_decode($_GET['data_encrypted_b64']); - $data_enc_nonce = base64_decode($_GET['data_encrypted_nonce_b64']); - $databox_raw = $this->_decrypt($data_enc, $data_enc_nonce); - if ($databox_raw == false) { - self::debugErr("Failed to decrypt qc activation data"); - Admin_Display::error(sprintf(__('Failed to decrypt %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + $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'], json_encode($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; } - $databox = \json_decode($databox_raw, true); - self::debug("sealed box from finish_qc_activation", $databox_raw); - - if (empty($databox['qc_activated']) || !in_array($databox['qc_activated'], array('anonymous', 'linked', 'cdn'))) { - self::debugErr("Failed to parse qc activation data", $databox); - Admin_Display::error(sprintf(__('Failed to parse %s activation data.', 'litespeed-cache'), 'QUIC.cloud')); + 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; } - if (empty($databox['ts']) || abs(time() - $databox['ts']) > 86400) { - self::debugErr("QC activation data timeout", $databox); + $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'] = $databox['qc_activated']; + $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 ($databox['qc_activated'] == 'linked') { + if ($_GET['qc_activated'] == 'linked') { $msg = sprintf(__('Congratulations, %s successfully set this domain up for the online services.', 'litespeed-cache'), 'QUIC.cloud'); } - if ($databox['qc_activated'] == 'cdn') { + 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)); @@ -922,7 +942,7 @@ private function _post($service, $data = false, $time_out = false) } // Encrypt service as signature - $signature = $this->_encrypt($service_tag); + $signature = $this->_sign($service_tag); $data['signature'] = array( 'service_tag' => $service_tag, 'ts' => time(), @@ -1487,6 +1507,10 @@ public function handler() $this->init_qc(); break; + case self::TYPE_LINK: + $this->link_qc(); + break; + case self::TYPE_SYNC_USAGE: $this->sync_usage(); diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index 4e7c9b5c6..d14c9c0ec 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -206,7 +206,7 @@ Date: Tue, 27 Aug 2024 14:39:13 -0400 Subject: [PATCH 21/36] auto clear invalid sk/pk --- src/cloud.cls.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 57e516ac8..b20330ebf 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -206,9 +206,20 @@ public function link_qc() private function _sign_b64($data) { if (!$this->activated()) { - return ''; + 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, base64_decode($this->_summary['sk_b64'])); + $signature = sodium_crypto_sign_detached($data, $sk); return base64_encode($signature); } @@ -942,12 +953,13 @@ private function _post($service, $data = false, $time_out = false) } // Encrypt service as signature - $signature = $this->_sign($service_tag); - $data['signature'] = array( + $signature_ts = time(); + $sign_data = array( 'service_tag' => $service_tag, - 'ts' => time(), - 'signature' => $signature, + 'ts' => $signature_ts, ); + $data['signature_b64'] = $this->_sign_b64(json_encode($sign_data)); + $data['signature_ts'] = $signature_ts; $param = array( 'site_url' => home_url(), From ad99b7a50ffd7cfa750b5ffb6639c93d7ef8c085 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 27 Aug 2024 14:41:14 -0400 Subject: [PATCH 22/36] qc v3 my.qc link --- src/cloud.cls.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index b20330ebf..85a2b6933 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -907,7 +907,7 @@ public function qc_link() 'site_url' => home_url(), 'ver' => LSCWP_V, ); - return self::CLOUD_SERVER_DASH . '/u/wp?data=' . Utility::arr2str($data); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); + return self::CLOUD_SERVER_DASH . '/u/wp3/manage?data=' . urlencode(Utility::arr2str($data)); // . (!empty($this->_summary['is_linked']) ? '?wplogin=1' : ''); } /** From 2b4af7229b0b72a5b7c0557abe9490c5e88a91fb Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 27 Aug 2024 14:51:38 -0400 Subject: [PATCH 23/36] Clear QUIC.cloud activation button. --- src/cloud.cls.php | 31 ++++++++++++++++++++++++++++--- tpl/dash/dashboard.tpl.php | 9 +++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 85a2b6933..438933267 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -96,6 +96,7 @@ class Cloud extends Base const TYPE_ACTIVATE = 'activate'; const TYPE_LINK = 'link'; const TYPE_SYNC_USAGE = 'sync_usage'; + const TYPE_RESET = 'reset'; protected $_summary; @@ -360,13 +361,33 @@ public function finish_qc_activation() // Turn on CDN option $this->cls('Conf')->update_confs(array(self::O_CDN_QUIC => true)); } - Admin_Display::succeed('🎊 ' . $msg); + 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; + } + /** * Show latest commit version always if is on dev * @@ -1125,7 +1146,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']); } @@ -1515,6 +1536,10 @@ public function handler() $this->_clear_promo(); break; + case self::TYPE_RESET: + $this->reset_qc(); + break; + case self::TYPE_ACTIVATE: $this->init_qc(); break; @@ -1527,7 +1552,7 @@ public function handler() $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/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index d14c9c0ec..d43021449 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -171,6 +171,15 @@
+ activated()) : ?> + + +
From f6e88b702e4dab18dbf805077ce8efb8c26ffc4f Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 28 Aug 2024 10:49:40 -0400 Subject: [PATCH 24/36] Corrected activation btn --- tpl/dash/dashboard.tpl.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index d43021449..3609c7e82 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -211,12 +211,19 @@ - + activated()) : ?> + + From 1f8b3a1bc781e77b6979b6cfa69d65a772f8628a Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 28 Aug 2024 10:54:53 -0400 Subject: [PATCH 25/36] Relocated btn --- tpl/dash/dashboard.tpl.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tpl/dash/dashboard.tpl.php b/tpl/dash/dashboard.tpl.php index 3609c7e82..1a6d0d15e 100644 --- a/tpl/dash/dashboard.tpl.php +++ b/tpl/dash/dashboard.tpl.php @@ -171,15 +171,6 @@
- activated()) : ?> - - -
@@ -207,6 +198,7 @@
From 066ba271b40917436b72d4e6f20659acf0d86a96 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Wed, 28 Aug 2024 11:45:52 -0400 Subject: [PATCH 26/36] Echo sign done --- src/cloud.cls.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index 438933267..b037893f7 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -206,7 +206,8 @@ public function link_qc() */ private function _sign_b64($data) { - if (!$this->activated()) { + if (empty($this->_summary['sk_b64'])) { + self::debugErr('No sk to sign.'); return false; } $sk = base64_decode($this->_summary['sk_b64']); @@ -287,7 +288,10 @@ public function wp_rest_echo() self::debugErr("WPAPI echo data timeout [diff] " . $diff); return self::err('Echo data expired'); } - return self::ok(array('signature_b64' => $this->_sign_b64($_POST['wpapi_ts']))); + + $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)); } /** From 962d4e7d8d5f5d08f9fdbabc92d2a33350f4ed3d Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Thu, 29 Aug 2024 10:32:43 -0400 Subject: [PATCH 27/36] Used implode instead of json_encode for signature validation. --- src/cloud.cls.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index b037893f7..f1fb80e01 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -332,7 +332,7 @@ public function finish_qc_activation() 'wp_pk_b64' => $this->_summary['pk_b64'], 'qc_ts' => $_GET['qc_ts'], ); - $is_valid = $this->_validate_signature($_GET['qc_signature_b64'], json_encode($data_to_validate_signature)); + $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')); @@ -983,7 +983,7 @@ private function _post($service, $data = false, $time_out = false) 'service_tag' => $service_tag, 'ts' => $signature_ts, ); - $data['signature_b64'] = $this->_sign_b64(json_encode($sign_data)); + $data['signature_b64'] = $this->_sign_b64(implode('', $sign_data)); $data['signature_ts'] = $signature_ts; $param = array( From 2bd78107458d40546ae78fcecc1ca2c2c6d6ba0b Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Thu, 29 Aug 2024 11:10:18 -0400 Subject: [PATCH 28/36] Sign key path updated --- src/cloud.cls.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cloud.cls.php b/src/cloud.cls.php index f1fb80e01..f23a0776e 100644 --- a/src/cloud.cls.php +++ b/src/cloud.cls.php @@ -48,7 +48,7 @@ class Cloud extends Base const API_VER = 'ver_check'; const API_BETA_TEST = 'beta_test'; const API_REST_ECHO = 'tool/wp_rest_echo'; - const API_SERVER_KEY = 'server_key'; + const API_SERVER_KEY_SIGN = 'key_sign'; private static $CENTER_SVC_SET = array( self::SVC_U_ACTIVATE, @@ -63,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, self::API_REST_ECHO, self::API_SERVER_KEY); + 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, self::API_REST_ECHO, self::API_SERVER_KEY); + 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); @@ -233,9 +233,9 @@ private function _sign_b64($data) private function _load_server_pk($from_wpapi = false) { // Load cloud pk - $server_key_url = self::CLOUD_SERVER . '/' . self::API_SERVER_KEY . '?type=sign'; + $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 . '?type=sign'; + $server_key_url = self::CLOUD_SERVER_WP . '/' . self::API_SERVER_KEY_SIGN; } $resp = wp_remote_get($server_key_url); if (is_wp_error($resp)) { From 8fdc081c981388ba27655c630544b02b866fa481 Mon Sep 17 00:00:00 2001 From: Tim <162806658+timotei-litespeed@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:28:05 +0300 Subject: [PATCH 29/36] Add min php version (#741) Co-authored-by: Timotei --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 8d687a687..3352a86b4 100644 --- a/readme.txt +++ b/readme.txt @@ -2,6 +2,7 @@ Contributors: LiteSpeedTech Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cache, redis, memcached, database cleaner Requires at least: 5.3 +Requires PHP: 7.4 Tested up to: 6.6.1 Stable tag: 6.5.0.2 License: GPLv3 From 01d741d13c4cca186c53c66726b974e4b8b1bf7d Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 9 Sep 2024 10:30:24 -0400 Subject: [PATCH 30/36] Corrected min php ver --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 3352a86b4..8823b914a 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ Contributors: LiteSpeedTech Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cache, redis, memcached, database cleaner Requires at least: 5.3 -Requires PHP: 7.4 +Requires PHP: 7.2 Tested up to: 6.6.1 Stable tag: 6.5.0.2 License: GPLv3 From f28054c9d4f2b2d89da4784a0eb867a8236d053a Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 9 Sep 2024 11:12:53 -0400 Subject: [PATCH 31/36] =?UTF-8?q?*=20**Misc**=20Improved=20readme=20file?= =?UTF-8?q?=20by=20adding=20min=20supported=20PHP/WP=20versions.=20(Viktor?= =?UTF-8?q?=20Sz=C3=A9pe)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 8823b914a..10283a18e 100644 --- a/readme.txt +++ b/readme.txt @@ -260,6 +260,7 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro * **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`. From cfbb13e2b3f3b8c40d67e91a320683eb2d8d91b1 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Mon, 9 Sep 2024 11:17:38 -0400 Subject: [PATCH 32/36] - --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 10283a18e..b258f6ba5 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: LiteSpeedTech Tags: caching, optimize, performance, pagespeed, seo, image optimize, object cache, redis, memcached, database cleaner Requires at least: 5.3 Requires PHP: 7.2 -Tested up to: 6.6.1 +Tested up to: 6.6 Stable tag: 6.5.0.2 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl.html From e1f2aca1372bfb149089bb25d3adaf4fb206c8d7 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 10 Sep 2024 13:28:19 -0400 Subject: [PATCH 33/36] v7.0-a7: AVIF in dev --- src/base.cls.php | 1 + src/crawler.cls.php | 2 +- src/img-optm.cls.php | 6 +++++- src/lang.cls.php | 6 +++--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/base.cls.php b/src/base.cls.php index ec6bde47b..590f151a3 100644 --- a/src/base.cls.php +++ b/src/base.cls.php @@ -602,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, ); /** 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/img-optm.cls.php b/src/img-optm.cls.php index 6982d792e..0f67f256c 100644 --- a/src/img-optm.cls.php +++ b/src/img-optm.cls.php @@ -653,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); diff --git a/src/lang.cls.php b/src/lang.cls.php index 63ae44f81..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') . ' ' . @@ -210,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'), From 2a1e05fb17ec7c43c1305d1d5501a028fc8b35d7 Mon Sep 17 00:00:00 2001 From: Hai Zheng Date: Tue, 10 Sep 2024 18:14:35 -0400 Subject: [PATCH 34/36] =?UTF-8?q?v7.0-a7:=20*=20=F0=9F=8C=B1**Image=20Opti?= =?UTF-8?q?mization**=20Added=20AVIF=20format.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- litespeed-cache.php | 4 ++-- readme.txt | 3 ++- src/media.cls.php | 19 ++++++++++++++----- tpl/img_optm/settings.media_webp.tpl.php | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/litespeed-cache.php b/litespeed-cache.php index f7445ea13..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: 7.0-a6 + * 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', '7.0-a6'); +!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 diff --git a/readme.txt b/readme.txt index b258f6ba5..dc6fa22b4 100644 --- a/readme.txt +++ b/readme.txt @@ -49,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. @@ -256,6 +256,7 @@ 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. 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/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'))); ?>
From ad67220cde934a5305c9c7a44aefed5b13270a18 Mon Sep 17 00:00:00 2001 From: Timotei Date: Wed, 11 Sep 2024 18:01:22 +0300 Subject: [PATCH 35/36] CI changes Add Code Sniffer actions --- .github/workflows/ci.yml | 26 ++++++- .gitignore | 1 + .prettierignore | 6 ++ .prettierrc | 14 +++- composer.json | 13 ++++ composer.lock | 161 +++++++++++++++++++++++++++++++++++++++ package.json | 4 +- phpcs.xml.dist | 62 +++++++++++++++ 8 files changed, 278 insertions(+), 9 deletions(-) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 phpcs.xml.dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43e3edcf4..055405830 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,19 +8,39 @@ 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 Composer + - name: Setup and install composer + run: composer install + + # 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 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/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/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/* + + + + + + + + + + + + From a92561dc5e82d10377669a6e09cd74d0aa7ac9aa Mon Sep 17 00:00:00 2001 From: Timotei Date: Mon, 16 Sep 2024 22:03:29 +0300 Subject: [PATCH 36/36] Reorder items --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 055405830..b38bd4b03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,6 @@ jobs: path: .cache key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} - # Initialize Composer - - name: Setup and install composer - run: composer install - # Initialize Node.js - uses: actions/setup-node@v3 with: @@ -40,6 +36,7 @@ jobs: # Start actions - run: mkdir -p .cache + - run: composer install - run: composer run sniff-check - run: npm ci