_modified = false; // disallow others to call write $this->_allow_writing = false; $context = Context::getContext(); $psCookie = $context->cookie; $this->_path = $psCookie->_path; $this->_domain = $psCookie->_domain; $this->_secure = $psCookie->_secure; $this->name = $name; if ($name == self::DEFAULT_VARY_COOKIE_NAME) { $this->init($context, $psCookie); } } private function envChanged() { if ($this->vd['vv']['ov'] !== $this->vd['vv']['nv']) { return true; } if ($this->status & self::BM_IS_GUEST) { if (($this->vd['cv']['ov'] === null) && (($this->vd['vv']['nv'] === 'guest' && $this->vd['cv']['nv'] === null) || ($this->vd['vv']['nv'] === 'guestm' && $this->vd['cv']['nv'] === 'mobile~1~'))) { return false; } else { return true; } } else { // non guest return $this->vd['cv']['ov'] !== $this->vd['cv']['nv']; } } public static function setVary() { // this will only be called when all vary value determined $vary = new LiteSpeedCacheVaryCookie(); $vary->writeVary(); $changed = $vary->envChanged(); $debug_info = ''; if ($changed) { $debug_info = 'changed ' . json_encode($vary->vd); if ( _LITESPEED_DEBUG_ >= LSLog::LEVEL_ENVCOOKIE_CHANGE) { LSLog::log($debug_info, LSLog::LEVEL_ENVCOOKIE_CHANGE); } } elseif (($vary->status & (self::BM_HAS_VARYCOOKIE | self::BM_HAS_VARYVALUE)) > 0) { $debug_info = 'found & match ' . json_encode($vary->vd); if (_LITESPEED_DEBUG_ >= LSLog::LEVEL_ENVCOOKIE_DETAIL) { LSLog::log($debug_info, LSLog::LEVEL_ENVCOOKIE_DETAIL); } } if ($debug_info && $vary->debug_header) { header("X-LSCACHE-Debug-Vary: $debug_info"); } return $changed; } public static function setAmpVary($value) { // this will be called by Amp module from third-party integration $amp = new LiteSpeedCacheVaryCookie(self::AMP_VARY_COOKIE_NAME); $amp->writeAmpVary($value); } private function writeAmpVary($value) { if (headers_sent()) { $this->status |= self::BM_UPDATE_FAILED; return; } // check lscache vary cookie, not default PS cookie, workaround validator $cookies = ${'_COOKIE'}; $ov = null; if (isset($cookies[self::AMP_VARY_COOKIE_NAME])) { $ov = $cookies[self::AMP_VARY_COOKIE_NAME]; } if ($ov != $value) { setcookie(self::AMP_VARY_COOKIE_NAME, $value, 0, $this->_path, $this->_domain, $this->_secure, true); //LiteSpeedCache::forceNotCacheable('Amp vary change'); not needed any more, lscache engine can save with proper key. } } private function getPrivateId() { $len = 32; if (function_exists('random_bytes')) { $id = bin2hex(random_bytes($len)); } elseif (function_exists('openssl_random_pseudo_bytes')) { $id = bin2hex(openssl_random_pseudo_bytes($len)); } else { $id = uniqid(); } $val = $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . microtime() . $id; return md5($val); } private function writeVary() { if (headers_sent()) { $this->status |= self::BM_UPDATE_FAILED; return; } if (($this->status & self::BM_IS_GUEST) > 0 && ($this->status & self::BM_VARYVALUE_CHANGED) == 0 && LiteSpeedCache::isCacheable()) { // no cookie set for guest mode and only if for cacheable response. // for non-cacheable route, like ajax request, can set vary cookie return; } // always check private session cookie if ($this->vd['ps']['ov'] == null) { $privateId = $this->getPrivateId(); $this->vd['ps']['nv'] = $privateId; setcookie(self::PRIVATE_SESSION_COOKIE, $privateId, 0, $this->_path, $this->_domain, $this->_secure, true); } if ($this->status & self::BM_VARYCOOKIE_CHANGED) { $val = $this->vd['cv']['nv']; $time = 0; // end of session expire if ($val === null) { // delete cookie $val = ''; $time = 1000; } if (!setcookie($this->vd['cv']['name'], $val, $time, $this->_path, $this->_domain, $this->_secure, true)) { $this->status |= self::BM_UPDATE_FAILED; } } if ($this->status & self::BM_VARYVALUE_CHANGED) { header('X-LiteSpeed-Vary: value=' . $this->vd['vv']['nv']); } } private function init($context, $psCookie) { $this->vd = [ 'cv' => ['name' => $this->name, 'ov' => null, 'nv' => null], // cookieVary 'vv' => ['ov' => null, 'nv' => null], // valueVary 'ps' => ['ov' => null, 'nv' => null], // private session ]; $conf = LiteSpeedCacheConfig::getInstance(); // $diffCustomerGroup 0: No; 1: Yes; 2: login_out $diffCustomerGroup = $conf->getDiffCustomerGroup(); // $diffMobile 0: no; 1: yes $diffMobile = $conf->get(LiteSpeedCacheConfig::CFG_DIFFMOBILE); $isMobile = $diffMobile ? $context->getMobileDevice() : false; $bypass = $conf->getContextBypass(); $this->debug_header = $conf->get(LiteSpeedCacheConfig::CFG_DEBUG_HEADER); // check lscache vary cookie, not default PS cookie, workaround validator $cookies = ${'_COOKIE'}; $data = []; if (LiteSpeedCache::isRestrictedIP()) { $data['dev'] = 1; } if (!in_array('ctry', $bypass) && isset($psCookie->iso_code_country)) { $iso = $psCookie->iso_code_country; $id_country = (int)Configuration::get('PS_COUNTRY_DEFAULT'); $default_iso = Country::getIsoById($id_country); if ($iso != $default_iso) { $data['ctry'] = $iso; } } if (!in_array('curr', $bypass) && isset($psCookie->id_currency)) { $configuration_curr = Configuration::get('PS_CURRENCY_DEFAULT'); if ($psCookie->id_currency != $configuration_curr) { $data['curr'] = $psCookie->id_currency; } } if (!in_array('lang', $bypass) && isset($psCookie->id_lang) && Language::isMultiLanguageActivated()) { $configuration_id_lang = Configuration::get('PS_LANG_DEFAULT'); if ($psCookie->id_lang != $configuration_id_lang) { $data['lang'] = $psCookie->id_lang; } } if ($diffMobile && $isMobile) { $data['mobile'] = 1; $this->status |= self::BM_IS_MOBILEVIEW; } // customer maybe null if (($diffCustomerGroup != 0) && ($context->customer != null) && $context->customer->isLogged()) { // 1: every group, 2: inout if ($diffCustomerGroup == 1) { $data['cg'] = $context->customer->getGroups()[0]; } else { $data['cg'] = 1; } } if (!empty($data)) { ksort($data); // data is array, key sorted $newVal = ''; foreach ($data as $k => $v) { $newVal .= $k . '~' . $v . '~'; } $this->vd['cv']['nv'] = $newVal; $this->vd['cv']['data'] = $data; $this->status |= self::BM_HAS_VARYCOOKIE; } if (isset($cookies[$this->vd['cv']['name']])) { $oldVal = $cookies[$this->vd['cv']['name']]; if ($oldVal == 'deleted') { $oldVal = null; } $this->vd['cv']['ov'] = $oldVal; } if ($this->vd['cv']['ov'] !== $this->vd['cv']['nv']) { $this->status |= self::BM_VARYCOOKIE_CHANGED; } // check vary value if (isset($_SERVER['LSCACHE_VARY_VALUE'])) { $ov = $_SERVER['LSCACHE_VARY_VALUE']; $this->vd['vv']['ov'] = $this->vd['vv']['nv'] = $ov; $this->status |= self::BM_HAS_VARYVALUE; if ($diffMobile) { // check if mismatch if ($ov == 'guest' && $isMobile) { $this->vd['vv']['nv'] = 'guestm'; } elseif ($ov == 'guestm' && !$isMobile) { $this->vd['vv']['nv'] = 'guest'; } if ($this->vd['vv']['ov'] !== $this->vd['vv']['nv']) { $this->status |= self::BM_VARYVALUE_CHANGED; } } if ($ov == 'guest' || $ov == 'guestm') { $this->status |= self::BM_IS_GUEST; } } if (isset($cookies[self::PRIVATE_SESSION_COOKIE])) { $this->vd['ps']['ov'] = $cookies[self::PRIVATE_SESSION_COOKIE]; } } }