%PDF- %PDF-
Direktori : /home/kfvehpdt/safsu.fr/wp-content/plugins/sucuri-scanner/src/ |
Current File : /home/kfvehpdt/safsu.fr/wp-content/plugins/sucuri-scanner/src/cache.lib.php |
<?php /** * Code related to the cache.lib.php interface. * * PHP version 5 * * @category Library * @package Sucuri * @subpackage SucuriScanner * @author Daniel Cid <dcid@sucuri.net> * @copyright 2010-2018 Sucuri Inc. * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2 * @link https://wordpress.org/plugins/sucuri-scanner */ if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) { if (!headers_sent()) { /* Report invalid access if possible. */ header('HTTP/1.1 403 Forbidden'); } exit(1); } /** * File-based cache library. * * WP_Object_Cache [1] is WordPress' class for caching data which may be * computationally expensive to regenerate, such as the result of complex * database queries. However the object cache is non-persistent. This means that * data stored in the cache resides in memory only and only for the duration of * the request. Cached data will not be stored persistently across page loads * unless of the installation of a 3party persistent caching plugin [2]. * * [1] https://codex.wordpress.org/Class_Reference/WP_Object_Cache * [2] https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching * * @category Library * @package Sucuri * @subpackage SucuriScanner * @author Daniel Cid <dcid@sucuri.net> * @copyright 2010-2018 Sucuri Inc. * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2 * @link https://wordpress.org/plugins/sucuri-scanner */ class SucuriScanCache extends SucuriScan { /** * The unique name (or identifier) of the file with the data. * * The file should be located in the same folder where the dynamic data * generated by the plugin is stored, and using the following format [1], it * most be a PHP file because it is expected to have an exit point in the first * line of the file causing it to stop the execution if a unauthorized user * tries to access it directly. * * [1] /public/data/sucuri-DATASTORE.php * * @var string */ private $datastore; /** * The full path of the datastore file. * * @var string */ private $datastore_path; /** * Whether the datastore file is usable or not. * * This variable will only be TRUE if the datastore file specified exists, is * writable and readable, in any other case it will always be FALSE. * * @var bool */ private $usable_datastore; /** * Initializes the cache library. * * @param string $datastore Name of the storage file. * @param bool $auto_create Forces the creation of the storage file. * @return void */ public function __construct($datastore = '', $auto_create = true) { $this->datastore = $datastore; $this->datastore_path = $this->datastoreFilePath($auto_create); $this->usable_datastore = (bool) $this->datastore_path; } /** * Default attributes for every datastore file. * * @return array Default attributes for every datastore file. */ public function datastoreDefaultInfo() { $attrs = array( 'datastore' => $this->datastore, 'created_on' => time(), 'updated_on' => time(), ); return $attrs; } /** * Default content of every datastore file. * * @param array $finfo Rainbow table with the key names and decoded values. * @return string Default content of every datastore file. */ private function datastoreInfo($finfo = array()) { $attrs = $this->datastoreDefaultInfo(); $info_is_available = (bool) isset($finfo['info']); $info = "<?php\n"; foreach ($attrs as $attr_name => $attr_value) { if ($info_is_available && $attr_name != 'updated_on' && isset($finfo['info'][$attr_name]) ) { $attr_value = $finfo['info'][$attr_name]; } $info .= sprintf("// %s=%s;\n", $attr_name, $attr_value); } $info .= "exit(0);\n"; $info .= "?>\n"; return $info; } /** * Check if the datastore file exists, if it's writable and readable by the same * user running the server, in case that it does not exists the method will * tries to create it by itself with the right permissions to use it. * * @param bool $auto_create Create the file is it does not exists. * @return string|bool Absolute path to the storage file, false otherwise. */ private function datastoreFilePath($auto_create = false) { if (!$this->datastore) { return false; } $filename = $this->dataStorePath('sucuri-' . $this->datastore . '.php'); $directory = dirname($filename); /* create directory if necessary */ if (!file_exists($directory)) { @mkdir($directory, 0755, true); } if (!file_exists($filename) && is_writable($directory) && $auto_create) { @file_put_contents($filename, $this->datastoreInfo()); } return $filename; } /** * Check whether a key has a valid name or not. * * WARNING: Instead of using a regular expression to match the format of the * key we will use a primitive string transformation technique to reduce the * execution time, regular expressions are significantly slow. * * @param string $key Unique name for the data. * @return bool True if the key is valid, false otherwise. */ private function validKeyName($key = '') { $result = true; $length = strlen($key); $allowed = array( /* preg_match('/^([0-9a-zA-Z_]+)$/', $key) */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', ); for ($i = 0; $i < $length; $i++) { if (!in_array($key[$i], $allowed)) { $result = false; break; } } return $result; } /** * Update the content of the datastore file with the new entries. * * @param array $finfo Rainbow table with the key names and decoded values. * @return bool TRUE if the operation finished successfully, FALSE otherwise. */ private function saveNewEntries($finfo = array()) { if (!$finfo) { return false; } $metadata = $this->datastoreInfo($finfo); if (@file_put_contents($this->datastore_path, $metadata)) { foreach ($finfo['entries'] as $key => $data) { $line = sprintf("%s:%s\n", $key, json_encode($data)); @file_put_contents($this->datastore_path, $line, FILE_APPEND); } } return true; } /** * Retrieve and parse the cache file and generate a hash table with the keys * and decoded data as the values of each entry. Duplicated key names will * be merged automatically. * * @param bool $assoc Force object to array conversion. * @param bool $onlyInfo Returns the cache headers and no content. * @return array Rainbow table with the key names and decoded values. */ private function getDatastoreContent($assoc = false, $onlyInfo = false) { $object = array(); $object['info'] = array(); $object['entries'] = array(); if (($fh = @fopen($this->datastore_path, 'r')) === false) { return $object; } while (($line = fgets($fh)) !== false) { $line = trim($line); if (!$line) { continue; } if (strpos($line, "//\x20") === 0 && strpos($line, '=') !== false && $line[strlen($line) - 1] === ';' ) { $section = substr($line, 3, -1); list($header, $value) = explode('=', $section, 2); $object['info'][$header] = $value; continue; } // skip content if ($onlyInfo) { continue; } if (strpos($line, ':') !== false) { list($keyname, $value) = explode(':', $line, 2); $object['entries'][$keyname] = @json_decode($value, $assoc); } } fclose($fh); return $object; } /** * Retrieve the headers of the datastore file. * * Each datastore file has a list of attributes at the beginning of the it with * information like the creation and last update time. If you are extending the * functionality of these headers please refer to the method that contains the * default attributes and their values [1]. * * [1] SucuriScanCache::datastoreDefaultInfo() * * @return array|bool Default content of every datastore file. */ public function getDatastoreInfo() { $finfo = $this->getDatastoreContent(false, true); if (empty($finfo['info'])) { return false; } $finfo['info']['fpath'] = $this->datastore_path; if (!isset($finfo['info']['created_on'])) { $finfo['info']['created_on'] = time(); } if (!isset($finfo['info']['updated_on'])) { $finfo['info']['updated_on'] = time(); } return $finfo['info']; } /** * Returns the Unix timestamp when the cache was created. * * @return int Unix timestamp when the cache was created. */ public function createdAt() { $info = $this->getDatastoreInfo(); return (int) $info['created_on']; } /** * Returns the Unix timestamp when the cache was updated. * * @return int Unix timestamp when the cache was updated. */ public function updatedAt() { $info = $this->getDatastoreInfo(); return (int) $info['updated_on']; } /** * Get the total number of unique entries in the datastore file. * * @param array $finfo Rainbow table with the key names and decoded values. * @return int Total number of unique entries found in the datastore file. */ public function getCount($finfo = null) { if (!is_array($finfo)) { $finfo = $this->getDatastoreContent(); } return count($finfo['entries']); } /** * Check whether the last update time of the datastore file has surpassed the * lifetime specified for a key name. This method is the only one related with * the caching process, any others besides this are just methods used to handle * the data inside those files. * * @param int $lifetime Life time of the key in the datastore file. * @param array $finfo Rainbow table with the key names and decoded values. * @return bool TRUE if the life time of the data has expired, FALSE otherwise. */ public function dataHasExpired($lifetime = 0, $finfo = null) { if (!is_array($finfo)) { $meta = $this->getDatastoreInfo(); $finfo = array('info' => $meta); } if ($lifetime > 0 && !empty($finfo['info'])) { $diff_time = time() - intval($finfo['info']['updated_on']); if ($diff_time >= $lifetime) { return true; } } return false; } /** * JSON-encode the data and store it in the datastore file identifying it with * the key name, the data will be added to the file even if the key is * duplicated, but when getting the value of the same key later again it will * return only the value of the first occurrence found in the file. * * @param string $key Unique name for the data. * @param mixed $data Data to associate to the key. * @return bool True if the data was cached, false otherwise. */ public function add($key = '', $data = '') { return $this->set($key, $data); } /** * Update the data of all the key names matching the one specified. * * @param string $key Unique name for the data. * @param mixed $data Data to associate to the key. * @return bool True if the cache data was updated, false otherwise. */ public function set($key = '', $data = '') { if (!$this->validKeyName($key)) { return self::throwException(__('Invalid cache key name', 'sucuri-scanner')); } $finfo = $this->getDatastoreInfo(); if (empty($finfo['fpath'])) { return false; } $line = sprintf("%s:%s\n", $key, json_encode($data)); return (bool) @file_put_contents($finfo['fpath'], $line, FILE_APPEND); } /** * Retrieve the first occurrence of the key found in the datastore file. * * @param string $key Unique name for the data. * @param int $lifetime Seconds before the data expires. * @param string $assoc Force data to be converted to an array. * @return mixed Data associated to the key. */ public function get($key = '', $lifetime = 0, $assoc = '') { if (!$this->validKeyName($key)) { return self::throwException(__('Invalid cache key name', 'sucuri-scanner')); } $finfo = $this->getDatastoreContent($assoc === 'array'); if ($this->dataHasExpired($lifetime, $finfo) || !array_key_exists($key, $finfo['entries']) ) { return false; } return @$finfo['entries'][$key]; } /** * Retrieve all the entries found in the datastore file. * * @param int $lifetime Life time of the key in the datastore file. * @param string $assoc Force data to be converted to an array. * @return mixed All the entries stored in the cache file. */ public function getAll($lifetime = 0, $assoc = '') { $finfo = $this->getDatastoreContent($assoc === 'array'); if ($this->dataHasExpired($lifetime, $finfo)) { return false; } return $finfo['entries']; } /** * Check whether a specific key exists in the datastore file. * * @param string $key Unique name for the data. * @return bool True if the data exists, false otherwise. */ public function exists($key = '') { if (!$this->validKeyName($key)) { return self::throwException(__('Invalid cache key name', 'sucuri-scanner')); } $finfo = $this->getDatastoreContent(true); return array_key_exists($key, $finfo['entries']); } /** * Delete any entry from the datastore file matching the key name specified. * * @param string $key Unique name for the data. * @return bool True if the data was deleted, false otherwise. */ public function delete($key = '') { if (!$this->validKeyName($key)) { return self::throwException(__('Invalid cache key name', 'sucuri-scanner')); } $finfo = $this->getDatastoreContent(true); if (!array_key_exists($key, $finfo['entries'])) { return true; } unset($finfo['entries'][$key]); return $this->saveNewEntries($finfo); } /** * Replaces the entire content of the cache file. * * @param array $entries New data for the cache. * @return bool True if the cache was replaced. */ public function override($entries = array()) { return $this->saveNewEntries( array( 'info' => $this->getDatastoreInfo(), 'entries' => $entries, ) ); } /** * Remove all the entries from the datastore file. * * @return bool True, unless the cache file is not writable. */ public function flush() { $filename = 'sucuri-' . $this->datastore . '.php'; return @unlink($this->dataStorePath($filename)); } }