$item) { if (substr($key, -$len) == $envName) { return $item; } } return false; } protected static function getPassedHash() { // First check if it is passed in as an environment variable $hash = self::getEnvPassedInRewriteRule('HASH'); if ($hash !== false) { return $hash; } // Then check if it is passed in the query string if (isset($_GET['hash'])) { return $_GET['hash']; } return false; } protected static function getWebPExpressContentDirWithDocRoot() { // Get relative path to wp-content // -------------------------------- self::$checking = 'Relative path to wp-content dir'; // Passed in env variable? $wpContentDirRel = self::getEnvPassedInRewriteRule('WPCONTENT'); if ($wpContentDirRel === false) { // Passed in QS? if (isset($_GET['wp-content'])) { $wpContentDirRel = SanityCheck::pathWithoutDirectoryTraversal($_GET['wp-content']); } else { // In case above fails, fall back to standard location $wpContentDirRel = 'wp-content'; } } // Check WebP Express content dir // --------------------------------- self::$checking = 'WebP Express content dir'; $webExpressContentDirAbs = SanityCheck::absPathExistsAndIsDir(self::$docRoot . '/' . $wpContentDirRel . '/webp-express'); return $webExpressContentDirAbs; } protected static function getWebPExpressContentDirNoDocRoot() { // Check wp-content // ---------------------- self::$checking = 'relative path between webp-express plugin dir and wp-content dir'; // From v0.22.0, we pass relative to webp-express dir rather than to the general plugin dir. // - this allows symlinking the webp-express dir. $wpContentDirRelToWEPluginDir = self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR'); if (!$wpContentDirRelToWEPluginDir) { // Passed in QS? if (isset($_GET['xwp-content-rel-to-we-plugin-dir'])) { $xwpContentDirRelToWEPluginDir = SanityCheck::noControlChars($_GET['xwp-content-rel-to-we-plugin-dir']); $wpContentDirRelToWEPluginDir = SanityCheck::pathDirectoryTraversalAllowed(substr($xwpContentDirRelToWEPluginDir, 1)); } } // Old .htaccess rules from before 0.22.0 passed relative path to general plugin dir. // these rules must still be supported, which is what we do here: if (!$wpContentDirRelToWEPluginDir) { self::$checking = 'relative path between plugin dir and wp-content dir'; $wpContentDirRelToPluginDir = self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR'); if ($wpContentDirRelToPluginDir === false) { // Passed in QS? if (isset($_GET['xwp-content-rel-to-plugin-dir'])) { $xwpContentDirRelToPluginDir = SanityCheck::noControlChars($_GET['xwp-content-rel-to-plugin-dir']); $wpContentDirRelToPluginDir = SanityCheck::pathDirectoryTraversalAllowed(substr($xwpContentDirRelToPluginDir, 1)); } else { throw new \Exception('Path to wp-content was not received in any way'); } } $wpContentDirRelToWEPluginDir = $wpContentDirRelToPluginDir . '..'; } // Check WebP Express content dir // --------------------------------- self::$checking = 'WebP Express content dir'; $pathToWEPluginDir = dirname(dirname(__DIR__)); $webExpressContentDirAbs = SanityCheck::pathDirectoryTraversalAllowed($pathToWEPluginDir . '/' . $wpContentDirRelToWEPluginDir . '/webp-express'); //$pathToPluginDir = dirname(dirname(dirname(__DIR__))); //$webExpressContentDirAbs = SanityCheck::pathDirectoryTraversalAllowed($pathToPluginDir . '/' . $wpContentDirRelToPluginDir . '/webp-express'); //echo $webExpressContentDirAbs; exit; if (@!file_exists($webExpressContentDirAbs)) { throw new \Exception('Dir not found'); } $webExpressContentDirAbs = @realpath($webExpressContentDirAbs); if ($webExpressContentDirAbs === false) { throw new \Exception('WebP Express content dir is outside restricted open_basedir!'); } return $webExpressContentDirAbs; } protected static function getImageRootsDef() { if (!isset(self::$wodOptions['image-roots'])) { throw new \Exception('No image roots defined in config.'); } return new ImageRoots(self::$wodOptions['image-roots']); } /** * Find the hash from config.[hash].json inside a directory. * * @param string $configDir Absolute path to the config directory (no trailing slash) */ protected static function findConfigFileByInspection($configDir) { if (!is_dir($configDir)) { throw new \Exception( 'WebP Express configuration directory was not found. ' . 'Please check that wp-content/webp-express/config exists and is readable.' ); } if (!is_readable($configDir)) { throw new \Exception('WebP Express configuration directory is not readable. Please fix the permissions (wp-content/webp-express/config)'); } $files = glob($configDir . '/wod-options.*.json'); if ($files === false || empty($files)) { // Failed finding the pattern. Lets see if old plain "wod-options.json" is there due to migration not complete $oldFileName = $configDir . '/wod-options.json'; if (file_exists($oldFileName)) { return $oldFileName; } throw new \Exception('Could not find a file that matches the pattern "wod-options.[hash].json" in the config dir. Is it there? Please check manually in this folder: wp-content/webp-express/config'); } // We expect only one match. But failed migrations / manual restores could perhaps cause there to be several // So therefore, lets make sure to pick the newest file, in case there are several usort($files, function ($a, $b) { return filemtime($b) <=> filemtime($a); }); $configFilename = $files[0]; if (!file_exists($configFilename)) { throw new \Exception('Configuration file found in filesystem, but at the same, it seems not to exist!'); } return $configFilename; // Take the first match //$filename = basename($files[0]); // Extract hash between "config." and ".json" /* if (preg_match('/^wod-options\.([a-f0-9]+)\.json$/i', $filename, $matches)) { return $matches[1]; }*/ // throw new \Exception('The filename Could not find a file that matches the pattern "wod-options.[hash].json" in the config dir. Is it there? Please check manually in this folder: wp-content/webp-express/config'); } protected static function loadConfig() { self::$checking = 'config folder'; $usingDocRoot = !( isset($_GET['xwp-content-rel-to-we-plugin-dir']) || self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR') || isset($_GET['xwp-content-rel-to-plugin-dir']) || self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR') ); self::$usingDocRoot = $usingDocRoot; if ($usingDocRoot) { // Check DOCUMENT_ROOT // ---------------------- self::$checking = 'DOCUMENT_ROOT'; $docRootAvailable = PathHelper::isDocRootAvailableAndResolvable(); if (!$docRootAvailable) { throw new \Exception( 'Document root is no longer available. It was available when the .htaccess rules was created and ' . 'the rules are based on that. You need to regenerate the rules (or fix your document root configuration)' ); } $docRoot = SanityCheck::absPath($_SERVER["DOCUMENT_ROOT"]); $docRoot = rtrim($docRoot, '/'); self::$docRoot = $docRoot; } if ($usingDocRoot) { self::$webExpressContentDirAbs = self::getWebPExpressContentDirWithDocRoot(); } else { self::$webExpressContentDirAbs = self::getWebPExpressContentDirNoDocRoot(); } // Check config file name // --------------------------------- self::$checking = 'config file'; $hash = self::getPassedHash(); if ($hash === false) { self::$checking = 'finding config file by introspecting config dir'; $configFilename = self::findConfigFileByInspection(self::$webExpressContentDirAbs . '/config'); } else { $hash = SanityCheck::noDirectoryTraversal($hash); if (!preg_match('/^[a-f0-9]{32}$/i', $hash)) { //throw new \Exception('Provided hash does not match correct pattern. Hash is expected to be 32 chars of letters/digits. Check the .htaccess files, its where the hash comes from (unless you are using Nginx - then check your Nginx rewrite rules)'); self::$checking = 'finding config file by introspecting config dir (as no hash passed in did not conform to expected pattern, which is 32 chars of letters/digits)'; $configFilename = self::findConfigFileByInspection(self::$webExpressContentDirAbs . '/config'); } else { $filename = 'wod-options.' . $hash . '.json'; $configFilename = self::$webExpressContentDirAbs . '/config/' . $filename; if (!file_exists($configFilename)) { self::$checking = 'finding config file by introspecting config dir (as no config file was found using hash passed in)'; $configFilename = self::findConfigFileByInspection(self::$webExpressContentDirAbs . '/config'); } } } // Check config file // -------------------- $configLoadResult = file_get_contents($configFilename); if ($configLoadResult === false) { throw new \Exception('Cannot open config file: ' . $configFilename); } $json = SanityCheck::isJSONObject($configLoadResult); self::$options = json_decode($json, true); self::$wodOptions = self::$options['wod']; } /** * Must be called after conversion. */ protected static function fixConfigIfEwwwDiscoveredNonFunctionalApiKeys() { if (isset(Ewww::$nonFunctionalApiKeysDiscoveredDuringConversion)) { // We got an invalid or exceeded api key (at least one). //error_log('look:' . print_r(Ewww::$nonFunctionalApiKeysDiscoveredDuringConversion, true)); EwwwTools::markApiKeysAsNonFunctional( Ewww::$nonFunctionalApiKeysDiscoveredDuringConversion, self::$webExpressContentDirAbs . '/config' ); } } }