$i)); $url = html_entity_decode($link); exit(""); } /** * Check if can run optimize * * @since 1.3 * @since 2.3.1 Relocated from cdn.cls * @access public */ public function can_optm() { $can = true; if (is_user_logged_in() && $this->conf(self::O_OPTM_GUEST_ONLY)) { $can = false; } elseif (is_admin()) { $can = false; } elseif (is_feed()) { $can = false; } elseif (is_preview()) { $can = false; } elseif (self::is_ajax()) { $can = false; } if (self::_is_login_page()) { Debug2::debug('[Router] Optm bypassed: login/reg page'); $can = false; } $can_final = apply_filters('litespeed_can_optm', $can); if ($can_final != $can) { Debug2::debug('[Router] Optm bypassed: filter'); } return $can_final; } /** * Check referer page to see if its from admin * * @since 2.4.2.1 * @access public */ public static function from_admin() { return !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], get_admin_url()) === 0; } /** * Check if it can use CDN replacement * * @since 1.2.3 * @since 2.3.1 Relocated from cdn.cls * @access public */ public static function can_cdn() { $can = true; if (is_admin()) { if (!self::is_ajax()) { Debug2::debug2('[Router] CDN bypassed: is not ajax call'); $can = false; } if (self::from_admin()) { Debug2::debug2('[Router] CDN bypassed: ajax call from admin'); $can = false; } } elseif (is_feed()) { $can = false; } elseif (is_preview()) { $can = false; } /** * Bypass cron to avoid deregister jq notice `Do not deregister the jquery-core script in the administration area.` * @since 2.7.2 */ if (defined('DOING_CRON')) { $can = false; } /** * Bypass login/reg page * @since 1.6 */ if (self::_is_login_page()) { Debug2::debug('[Router] CDN bypassed: login/reg page'); $can = false; } /** * Bypass post/page link setting * @since 2.9.8.5 */ $rest_prefix = function_exists('rest_get_url_prefix') ? rest_get_url_prefix() : apply_filters('rest_url_prefix', 'wp-json'); if ( !empty($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], $rest_prefix . '/wp/v2/media') !== false && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'wp-admin') !== false ) { Debug2::debug('[Router] CDN bypassed: wp-json on admin page'); $can = false; } $can_final = apply_filters('litespeed_can_cdn', $can); if ($can_final != $can) { Debug2::debug('[Router] CDN bypassed: filter'); } return $can_final; } /** * Check if is login page or not * * @since 2.3.1 * @access protected */ protected static function _is_login_page() { if (in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php'), true)) { return true; } return false; } /** * UCSS/Crawler role simulator * * @since 1.9.1 * @since 3.3 Renamed from `is_crawler_role_simulation` */ public function is_role_simulation() { if (is_admin()) { return; } if (empty($_COOKIE['litespeed_hash']) && empty($_COOKIE['litespeed_flash_hash'])) { return; } self::debug('starting role validation'); // Check if is from crawler // if ( empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) || strpos( $_SERVER[ 'HTTP_USER_AGENT' ], Crawler::FAST_USER_AGENT ) !== 0 ) { // Debug2::debug( '[Router] user agent not match' ); // return; // } // Flash hash validation if (!empty($_COOKIE['litespeed_flash_hash'])) { $hash_data = self::get_option(self::ITEM_FLASH_HASH, array()); if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts']) && !empty($hash_data['uid'])) { if (time() - $hash_data['ts'] < 120 && $_COOKIE['litespeed_flash_hash'] == $hash_data['hash']) { self::debug('role simulate uid ' . $hash_data['uid']); self::delete_option(self::ITEM_FLASH_HASH); wp_set_current_user($hash_data['uid']); return; } } } // Hash validation // if (!empty($_COOKIE['litespeed_hash'])) { // $hash_data = self::get_option(self::ITEM_HASH, array()); // if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts']) && !empty($hash_data['uid'])) { // if (time() - $hash_data['ts'] < $this->conf(Base::O_CRAWLER_RUN_DURATION) && $_COOKIE['litespeed_hash'] == $hash_data['hash']) { // if (empty($hash_data['ip'])) { // $hash_data['ip'] = self::get_ip(); // self::update_option(self::ITEM_HASH, $hash_data); // } else { // $server_ips = apply_filters('litespeed_server_ips', array($hash_data['ip'])); // if (!self::ip_access($server_ips)) { // self::debug('WARNING: role simulator ip check failed [db ip] ' . $hash_data['ip'], $server_ips); // return; // } // } // wp_set_current_user($hash_data['uid']); // return; // } // } // } self::debug('WARNING: role simulator hash not match'); } /** * Get a short ttl hash (2mins) * * @since 6.4 */ public function get_flash_hash($uid) { $hash_data = self::get_option(self::ITEM_FLASH_HASH, array()); if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts'])) { if (time() - $hash_data['ts'] < 60) { return $hash_data['hash']; } } $hash = Str::rrand(32); self::update_option(self::ITEM_FLASH_HASH, array('hash' => $hash, 'ts' => time(), 'uid' => $uid)); return $hash; } /** * Get a security hash * * @since 3.3 */ public function get_hash($uid) { // As this is called only when starting crawling, not per page, no need to reuse $hash = Str::rrand(32); self::update_option(self::ITEM_HASH, array('hash' => $hash, 'ts' => time(), 'uid' => $uid)); return $hash; } /** * Get user role * * @since 1.6.2 */ public static function get_role($uid = null) { if (defined('LITESPEED_WP_ROLE')) { return LITESPEED_WP_ROLE; } if ($uid === null) { $uid = get_current_user_id(); } $role = false; if ($uid) { $user = get_userdata($uid); if (isset($user->roles) && is_array($user->roles)) { $tmp = array_values($user->roles); $role = implode(',', $tmp); // Combine for PHP5.3 const comaptibility } } Debug2::debug('[Router] get_role: ' . $role); if (!$role) { return $role; // Guest user Debug2::debug('[Router] role: guest'); /** * Fix double login issue * The previous user init refactoring didn't fix this bcos this is in login process and the user role could change * @see https://github.com/litespeedtech/lscache_wp/commit/69e7bc71d0de5cd58961bae953380b581abdc088 * @since 2.9.8 Won't assign const if in login process */ if (substr_compare(wp_login_url(), $GLOBALS['pagenow'], -strlen($GLOBALS['pagenow'])) === 0) { return $role; } } define('LITESPEED_WP_ROLE', $role); return LITESPEED_WP_ROLE; } /** * Get frontend path * * @since 1.2.2 * @access public * @return boolean */ public static function frontend_path() { //todo: move to htaccess.cls ? if (!isset(self::$_frontend_path)) { $frontend = rtrim(ABSPATH, '/'); // /home/user/public_html/frontend // get home path failed. Trac ticket #37668 (e.g. frontend:/blog backend:/wordpress) if (!$frontend) { Debug2::debug('[Router] No ABSPATH, generating from home option'); $frontend = parse_url(get_option('home')); $frontend = !empty($frontend['path']) ? $frontend['path'] : ''; $frontend = $_SERVER['DOCUMENT_ROOT'] . $frontend; } $frontend = realpath($frontend); self::$_frontend_path = $frontend; } return self::$_frontend_path; } /** * Check if ESI is enabled or not * * @since 1.2.0 * @access public * @return boolean */ public function esi_enabled() { if (!isset(self::$_esi_enabled)) { self::$_esi_enabled = defined('LITESPEED_ON') && $this->conf(self::O_ESI); if (!empty($_REQUEST[self::ACTION])) { self::$_esi_enabled = false; } } return self::$_esi_enabled; } /** * Check if crawler is enabled on server level * * @since 1.1.1 * @access public */ public static function can_crawl() { if (isset($_SERVER['X-LSCACHE']) && strpos($_SERVER['X-LSCACHE'], 'crawler') === false) { return false; } // CLI will bypass this check as crawler library can always do the 428 check if (defined('LITESPEED_CLI')) { return true; } return true; } /** * Check action * * @since 1.1.0 * @access public * @return string */ public static function get_action() { if (!isset(self::$_action)) { self::$_action = false; self::cls()->verify_action(); if (self::$_action) { defined('LSCWP_LOG') && Debug2::debug('[Router] LSCWP_CTRL verified: ' . var_export(self::$_action, true)); } } return self::$_action; } /** * Check if is logged in * * @since 1.1.3 * @access public * @return boolean */ public static function is_logged_in() { if (!isset(self::$_is_logged_in)) { self::$_is_logged_in = is_user_logged_in(); } return self::$_is_logged_in; } /** * Check if is ajax call * * @since 1.1.0 * @access public * @return boolean */ public static function is_ajax() { if (!isset(self::$_is_ajax)) { self::$_is_ajax = defined('DOING_AJAX') && DOING_AJAX; } return self::$_is_ajax; } /** * Check if is admin ip * * @since 1.1.0 * @access public * @return boolean */ public function is_admin_ip() { if (!isset(self::$_is_admin_ip)) { $ips = $this->conf(self::O_DEBUG_IPS); self::$_is_admin_ip = $this->ip_access($ips); } return self::$_is_admin_ip; } /** * Get type value * * @since 1.6 * @access public */ public static function verify_type() { if (empty($_REQUEST[self::TYPE])) { Debug2::debug('[Router] no type', 2); return false; } Debug2::debug('[Router] parsed type: ' . $_REQUEST[self::TYPE], 2); return $_REQUEST[self::TYPE]; } /** * Check privilege and nonce for the action * * @since 1.1.0 * @access private */ private function verify_action() { if (empty($_REQUEST[Router::ACTION])) { Debug2::debug2('[Router] LSCWP_CTRL bypassed empty'); return; } $action = stripslashes($_REQUEST[Router::ACTION]); if (!$action) { return; } $_is_public_action = false; // Each action must have a valid nonce unless its from admin ip and is public action // Validate requests nonce (from admin logged in page or cli) if (!$this->verify_nonce($action)) { // check if it is from admin ip if (!$this->is_admin_ip()) { Debug2::debug('[Router] LSCWP_CTRL query string - did not match admin IP: ' . $action); return; } // check if it is public action if ( !in_array($action, array( Core::ACTION_QS_NOCACHE, Core::ACTION_QS_PURGE, Core::ACTION_QS_PURGE_SINGLE, Core::ACTION_QS_SHOW_HEADERS, Core::ACTION_QS_PURGE_ALL, Core::ACTION_QS_PURGE_EMPTYCACHE, )) ) { Debug2::debug('[Router] LSCWP_CTRL query string - did not match admin IP Actions: ' . $action); return; } if (apply_filters('litespeed_qs_forbidden', false)) { Debug2::debug('[Router] LSCWP_CTRL forbidden by hook litespeed_qs_forbidden'); return; } $_is_public_action = true; } /* Now it is a valid action, lets log and check the permission */ Debug2::debug('[Router] LSCWP_CTRL: ' . $action); // OK, as we want to do something magic, lets check if its allowed $_is_multisite = is_multisite(); $_is_network_admin = $_is_multisite && is_network_admin(); $_can_network_option = $_is_network_admin && current_user_can('manage_network_options'); $_can_option = current_user_can('manage_options'); switch ($action) { case self::ACTION_SAVE_SETTINGS_NETWORK: // Save network settings if ($_can_network_option) { self::$_action = $action; } return; case Core::ACTION_PURGE_BY: if (defined('LITESPEED_ON') && ($_can_network_option || $_can_option || self::is_ajax())) { //here may need more security self::$_action = $action; } return; case self::ACTION_DB_OPTM: if ($_can_network_option || $_can_option) { self::$_action = $action; } return; case Core::ACTION_PURGE_EMPTYCACHE: // todo: moved to purge.cls type action if ((defined('LITESPEED_ON') || $_is_network_admin) && ($_can_network_option || (!$_is_multisite && $_can_option))) { self::$_action = $action; } return; case Core::ACTION_QS_NOCACHE: case Core::ACTION_QS_PURGE: case Core::ACTION_QS_PURGE_SINGLE: case Core::ACTION_QS_SHOW_HEADERS: case Core::ACTION_QS_PURGE_ALL: case Core::ACTION_QS_PURGE_EMPTYCACHE: if (defined('LITESPEED_ON') && ($_is_public_action || self::is_ajax())) { self::$_action = $action; } return; case self::ACTION_PLACEHOLDER: case self::ACTION_AVATAR: case self::ACTION_IMG_OPTM: case self::ACTION_CLOUD: case self::ACTION_CDN_SETUP: case self::ACTION_CDN_CLOUDFLARE: case self::ACTION_CRAWLER: case self::ACTION_PRESET: case self::ACTION_IMPORT: case self::ACTION_REPORT: case self::ACTION_CSS: case self::ACTION_UCSS: case self::ACTION_VPI: case self::ACTION_CONF: case self::ACTION_ACTIVATION: case self::ACTION_HEALTH: case self::ACTION_SAVE_SETTINGS: // Save settings if ($_can_option && !$_is_network_admin) { self::$_action = $action; } return; case self::ACTION_PURGE: case self::ACTION_DEBUG2: if ($_can_network_option || $_can_option) { self::$_action = $action; } return; case Core::ACTION_DISMISS: /** * Non ajax call can dismiss too * @since 2.9 */ // if ( self::is_ajax() ) { self::$_action = $action; // } return; default: Debug2::debug('[Router] LSCWP_CTRL match failed: ' . $action); return; } } /** * Verify nonce * * @since 1.1.0 * @access public * @param string $action * @return bool */ public function verify_nonce($action) { if (!isset($_REQUEST[Router::NONCE]) || !wp_verify_nonce($_REQUEST[Router::NONCE], $action)) { return false; } else { return true; } } /** * Check if the ip is in the range * * @since 1.1.0 * @access public */ public function ip_access($ip_list) { if (!$ip_list) { return false; } if (!isset(self::$_ip)) { self::$_ip = self::get_ip(); } if (!self::$_ip) { return false; } // $uip = explode('.', $_ip); // if(empty($uip) || count($uip) != 4) Return false; // foreach($ip_list as $key => $ip) $ip_list[$key] = explode('.', trim($ip)); // foreach($ip_list as $key => $ip) { // if(count($ip) != 4) continue; // for($i = 0; $i <= 3; $i++) if($ip[$i] == '*') $ip_list[$key][$i] = $uip[$i]; // } return in_array(self::$_ip, $ip_list); } /** * Get client ip * * @since 1.1.0 * @since 1.6.5 changed to public * @access public * @return string */ public static function get_ip() { $_ip = ''; // if ( function_exists( 'apache_request_headers' ) ) { // $apache_headers = apache_request_headers(); // $_ip = ! empty( $apache_headers['True-Client-IP'] ) ? $apache_headers['True-Client-IP'] : false; // if ( ! $_ip ) { // $_ip = ! empty( $apache_headers['X-Forwarded-For'] ) ? $apache_headers['X-Forwarded-For'] : false; // $_ip = explode( ',', $_ip ); // $_ip = $_ip[ 0 ]; // } // } if (!$_ip) { $_ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false; } return $_ip; } /** * Check if opcode cache is enabled * * @since 1.8.2 * @access public */ public static function opcache_enabled() { return function_exists('opcache_reset') && ini_get('opcache.enable'); } /** * Handle static files * * @since 3.0 */ public function serve_static() { if (!empty($_SERVER['SCRIPT_URI'])) { if (strpos($_SERVER['SCRIPT_URI'], LITESPEED_STATIC_URL . '/') !== 0) { return; } $path = substr($_SERVER['SCRIPT_URI'], strlen(LITESPEED_STATIC_URL . '/')); } elseif (!empty($_SERVER['REQUEST_URI'])) { $static_path = parse_url(LITESPEED_STATIC_URL, PHP_URL_PATH) . '/'; if (strpos($_SERVER['REQUEST_URI'], $static_path) !== 0) { return; } $path = substr(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), strlen($static_path)); } else { return; } $path = explode('/', $path, 2); if (empty($path[0]) || empty($path[1])) { return; } switch ($path[0]) { case 'avatar': $this->cls('Avatar')->serve_static($path[1]); break; case 'localres': $this->cls('Localization')->serve_static($path[1]); break; default: break; } } /** * Handle all request actions from main cls * * This is different than other handlers * * @since 3.0 * @access public */ public function handler($cls) { if (!in_array($cls, self::$_HANDLERS)) { return; } return $this->cls($cls)->handler(); } }