Current Path : /home/ncdcgo/public_html/upgrade/dup-installer/classes/utilities/ |
Current File : /home/ncdcgo/public_html/upgrade/dup-installer/classes/utilities/class.u.php |
<?php /** * Various Static Utility methods for working with the installer * * Standard: PSR-2 * * @link http://www.php-fig.org/psr/psr-2 Full Documentation * * @package SC\DUPX\U */ defined('ABSPATH') || defined('DUPXABSPATH') || exit; use Duplicator\Installer\Core\Bootstrap; use Duplicator\Installer\Utils\Log\Log; use Duplicator\Installer\Core\Params\PrmMng; use Duplicator\Libs\Snap\SnapIO; use Duplicator\Libs\Snap\SnapJson; class DUPX_U { const MAINTENANCE_INDEX_MARKER = '<!-- DUPLICATOR INSTALLER MAINTENANCE -->'; /** * Adds a slash to the end of a file or directory path * * @param string $path A path * * @return string The original $path with a with '/' added to the end. */ public static function addSlash($path) { $last_char = substr($path, strlen($path) - 1, 1); if ($last_char != '/') { $path .= '/'; } return $path; } /** * Does one string contain other * * @param string $haystack The full string to search * @param string $needle The substring to search on * * @return bool Returns true if the $needle was found in the $haystack */ public static function contains($haystack, $needle) { $pos = strpos($haystack, $needle); return ($pos !== false); } /** * move all folder content up to parent * * @param string $subFolderName full path * @param boolean $deleteSubFolder if true delete subFolder after moved all * * @return boolean */ public static function moveUpfromSubFolder($subFolderName, $deleteSubFolder = false) { if (!is_dir($subFolderName)) { return false; } $parentFolder = dirname($subFolderName); if (!is_writable($parentFolder)) { return false; } $success = true; if (($subList = glob(rtrim($subFolderName, '/') . '/*', GLOB_NOSORT)) === false) { Log::info("Problem glob folder " . $subFolderName); return false; } else { foreach ($subList as $cName) { $destination = $parentFolder . '/' . basename($cName); if (file_exists($destination)) { $success = SnapIO::rrmdir($destination); } if ($success) { $success = rename($cName, $destination); } else { break; } } if ($success && $deleteSubFolder) { $success = SnapIO::rrmdir($subFolderName); } } if (!$success) { Log::info("Problem om moveUpfromSubFolder subFolder:" . $subFolderName); } return $success; } /** * @param string $archive_filepath full path of zip archive * @param string $password archive password * * @return boolean|string path of dup-installer folder of false if not found */ public static function findDupInstallerFolder($archive_filepath, $password) { if (!DUPX_Conf_Utils::isPhpZipAvailable()) { return ''; } $zipArchive = new ZipArchive(); $result = false; $dupArchiveTxt = Bootstrap::ARCHIVE_PREFIX . Bootstrap::getPackageHash() . Bootstrap::ARCHIVE_EXTENSION; if ($zipArchive->open($archive_filepath) === true) { if (strlen($password)) { $zipArchive->setPassword($password); } for ($i = 0; $i < $zipArchive->numFiles; $i++) { $stat = $zipArchive->statIndex($i); $safePath = rtrim(self::setSafePath($stat['name']), '/'); if (substr_count($safePath, '/') > 2) { continue; } $exploded = explode('/', $safePath); if ( ($dup_index = array_search($dupArchiveTxt, $exploded)) !== false && $exploded[$dup_index - 1] === 'dup-installer' ) { $result = implode('/', array_slice($exploded, 0, $dup_index - 1)); break; } } if ($zipArchive->close() !== true) { Log::info("Can't close ziparchive:" . $archive_filepath); return false; } } else { Log::info("Can't open zip archive:" . $archive_filepath); return false; } return $result; } /** * Dumps a variable for debugging * * @param mixed $var The variable to view * @param bool $pretty Pretty print the var * * @return void */ public static function dump($var, $pretty = false) { if ($pretty) { echo '<pre>'; print_r($var); echo '</pre>'; } else { print_r($var); } } /** * Return a string with the elapsed time * * @see getMicrotime() * * @param int|float $end The final time in the sequence to measure * @param int|float $start The start time in the sequence to measure * * @return string The time elapsed from $start to $end */ public static function elapsedTime($end, $start) { return sprintf("%.4f sec.", abs($end - $start)); } /** * Echo 256 spaces * * PHP_SAPI for fcgi requires a data flush of at least 256 * bytes every 40 seconds or else it forces a script halt * * @return void */ public static function fcgiFlush() { echo(str_repeat(' ', 256)); flush(); } /** * Get current microtime as a float. Method is used for simple profiling * * @see elapsedTime * * @return float A float in the form "msec sec", where sec is the number of seconds since the Unix epoch */ public static function getMicrotime() { return microtime(true); } /** * Gets the size of a variable in memory * * @param mixed $var A valid PHP variable * * @return int The amount of memory the variable has consumed */ public static function getVarSize($var) { $start_memory = memory_get_usage(); $var = unserialize(serialize($var)); return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8; } /** * Is the string JSON * * @param string $string Any string blob * * @return bool Returns true if the string is JSON encoded */ public static function isJSON($string) { return is_string($string) && is_array(json_decode($string, true)) ? true : false; } /** * Display human readable byte sizes * * @param int $size The size in bytes * * @return string Human readable bytes such as 50MB, 1GB */ public static function readableByteSize($size) { try { $units = array( 'B', 'KB', 'MB', 'GB', 'TB', ); for ($i = 0; $size >= 1024 && $i < 4; $i++) { $size /= 1024; } return round($size, 2) . $units[$i]; } catch (Exception $e) { return "n/a"; } } /** * Makes path safe for any OS for PHP * * Paths should ALWAYS READ be "/" * uni: /home/path/file.txt * win: D:/home/path/file.txt * * @param string $path The path to make safe * * @return string The original $path with a with all slashes facing '/'. */ public static function setSafePath($path) { return str_replace("\\", "/", $path); } /** * Check PHP version * * @param string $version PHP version we looking for * * @return boolean Returns true if version is same or above. */ public static function isVersion($version) { return (version_compare(PHP_VERSION, $version) >= 0); } /** * The domain part of the given URL * www.myurl.co.uk => myurl.co.uk * www.google.com => google.com * my.test.myurl.co.uk => myurl.co.uk * www.myurl.localweb => myurl.localweb * * @param string $url string The URL whichs domain you want to get * * @return string */ public static function getDomain($url) { $pieces = parse_url($url); $domain = isset($pieces['host']) ? $pieces['host'] : ''; if (strpos($domain, ".") !== false) { if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) { return $regs['domain']; } else { $exDomain = explode('.', $domain); return implode('.', array_slice($exDomain, -2, 2)); } } else { return $domain; } } /** * * @param string $oldSubUrl The old sub url * @param string $oldMainUrl The old main url * * @return string */ public static function getDefaultURL($oldSubUrl, $oldMainUrl) { $paramsManager = PrmMng::getInstance(); $newMainUrl = $paramsManager->getValue(PrmMng::PARAM_URL_NEW); $parsedNewMainUrl = parse_url($newMainUrl); $parsedOldMainUrl = parse_url($oldMainUrl); $parsedOldSubUrl = parse_url($oldSubUrl); $oldMainDomain = $parsedOldMainUrl['host']; $oldSubDomain = $parsedOldSubUrl['host']; $newMainDomain = $parsedNewMainUrl['host']; // PARSE SCHEME $resultScheme = isset($parsedNewMainUrl['scheme']) ? $parsedNewMainUrl['scheme'] : 'http'; // PARSE HOST if ($oldMainDomain === $oldSubDomain) { $resultDomain = $newMainDomain; } else { $oldNoWwwMainDomain = (strpos($oldMainDomain, 'www.') === 0) ? substr($oldMainDomain, 4) : $oldMainDomain; $newNoWwwMainDomain = (strpos($newMainDomain, 'www.') === 0) ? substr($newMainDomain, 4) : $newMainDomain; if (($pos = strrpos($oldSubDomain, $oldNoWwwMainDomain)) === strlen($oldSubDomain) - strlen($oldNoWwwMainDomain)) { $subDif = substr($oldSubDomain, 0, $pos); $resultDomain = $subDif . $newNoWwwMainDomain; } else { // If I can't find a match it is a non-manageable url so I take the value of the old url. $resultDomain = $oldSubDomain; } } // PARSE PATH $oldMainPath = isset($parsedOldMainUrl['path']) ? $parsedOldMainUrl['path'] : ''; $oldSubPath = isset($parsedOldSubUrl['path']) ? $parsedOldSubUrl['path'] : ''; $newMainPath = isset($parsedNewMainUrl['path']) ? $parsedNewMainUrl['path'] : ''; if ($oldMainPath === $oldSubPath) { $resultPath = $newMainPath; } else { if (strpos($oldSubPath, $oldMainPath) === 0) { $subDif = substr($oldSubPath, strlen($oldMainPath)); $resultPath = $newMainPath . '/' . trim($subDif, '/'); } else { // If I can't find a match it is a non-manageable path so I take the value of the old path. $resultPath = $oldSubPath; } } if (empty($resultPath) || $resultPath === '/') { $resultPath = ''; } return $resultScheme . '://' . $resultDomain . '/' . trim($resultPath, '/'); } /** * Get default chunk size in byte * * @param int $min_chunk_size Min minimum chunk size in bytes * * @return int An integer chunk size byte value. */ public static function get_default_chunk_size_in_byte($min_chunk_size = 0) { if ($min_chunk_size == 0) { $min_chunk_size = 2 * MB_IN_BYTES; } $post_max_size_in_bytes = self::get_bytes_from_shorthand(ini_get('post_max_size')); $considered_post_max_size_in_bytes = $post_max_size_in_bytes - KB_IN_BYTES; $upload_max_filesize_in_bytes = self::get_bytes_from_shorthand(ini_get('upload_max_filesize')); $considered_upload_max_filesize_in_bytes = $upload_max_filesize_in_bytes - KB_IN_BYTES; $memory_limit_in_bytes = self::get_bytes_from_shorthand(ini_get('memory_limit')); $considered_memory_limit_in_bytes = $memory_limit_in_bytes - KB_IN_BYTES; $chunk_size_in_byte = min( $considered_post_max_size_in_bytes, $considered_upload_max_filesize_in_bytes, $considered_memory_limit_in_bytes, // In extraction process, 2 MB is improving speed, so we are using 5MB instead of 10 MB $min_chunk_size ); return $chunk_size_in_byte; } /** * Converts a shorthand byte value to an integer byte value. * * @param string $value A (PHP ini) byte value, either shorthand or ordinary. * * @return int An integer byte value. */ private static function get_bytes_from_shorthand($value) { $value = strtolower(trim($value)); $bytes = (int) $value; if (false !== strpos($value, 'g')) { $bytes *= GB_IN_BYTES; } elseif (false !== strpos($value, 'm')) { $bytes *= MB_IN_BYTES; } elseif (false !== strpos($value, 'k')) { $bytes *= KB_IN_BYTES; } // For windows 32 bit int max limit if ($bytes < 0) { return PHP_INT_MAX; } return min($bytes, PHP_INT_MAX); // Deal with large (float) values which run into the maximum integer size. } /** * Escaping for HTML blocks. * * @param string $text The text to be escaped. * * @return string */ public static function esc_html($text) { $safe_text = SnapJson::checkInvalidUTF8($text); $safe_text = self::_wp_specialchars($safe_text, ENT_QUOTES); /** * Filters a string cleaned and escaped for output in HTML. * * Text passed to esc_html() is stripped of invalid or special characters * before output. * * @param string $safe_text The text after it has been escaped. * @param string $text The text prior to being escaped. */ return $safe_text; } /** * Escape single quotes, htmlspecialchar " < > &, and fix line endings. * * Escapes text strings for echoing in JS. It is intended to be used for inline JS * (in a tag attribute, for example onclick="..."). Note that the strings have to * be in single quotes. The {@see 'js_escape'} filter is also applied here. * * @param string $text The text to be escaped. * * @return string Escaped text. */ public static function esc_js($text) { $safe_text = SnapJson::checkInvalidUTF8($text); $safe_text = self::_wp_specialchars($safe_text, ENT_COMPAT); $safe_text = preg_replace('/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes($safe_text)); $safe_text = str_replace("\r", '', $safe_text); $safe_text = str_replace("\n", '\\n', addslashes($safe_text)); /** * Filters a string cleaned and escaped for output in JavaScript. * * Text passed to esc_js() is stripped of invalid or special characters, * and properly slashed for output. * * @param string $safe_text The text after it has been escaped. * @param string $text The text prior to being escaped. */ return $safe_text; } /** * Escaping for HTML attributes. * * @param string $text The text to be escaped. * * @return string */ public static function esc_attr($text) { $safe_text = SnapJson::checkInvalidUTF8($text); $safe_text = self::_wp_specialchars($safe_text, ENT_QUOTES); /** * Filters a string cleaned and escaped for output in an HTML attribute. * * Text passed to esc_attr() is stripped of invalid or special characters * before output. * * @param string $safe_text The text after it has been escaped. * @param string $text The text prior to being escaped. */ return $safe_text; } /** * Escaping for textarea values. * * @param string $text The text to be escaped. * * @return string */ public static function esc_textarea($text) { $safe_text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); /** * Filters a string cleaned and escaped for output in a textarea element. * * @param string $safe_text The text after it has been escaped. * @param string $text The text prior to being escaped. */ return $safe_text; } /** * Escape an HTML tag name. * * @param string $tag_name The tag name to be escaped. * * @return string */ public function tag_escape($tag_name) { $safe_tag = strtolower(preg_replace('/[^a-zA-Z0-9_:]/', '', $tag_name)); /** * Filters a string cleaned and escaped for output as an HTML tag. * * @param string $safe_tag The tag name after it has been escaped. * @param string $tag_name The text before it was escaped. */ return $safe_tag; } /** * Converts a number of special characters into their HTML entities. * * Specifically deals with: &, <, >, ", and '. * * $quote_style can be set to ENT_COMPAT to encode " to * ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded. * * @param string $string The text which is to be encoded. * @param int|string $quote_style Optional. Converts double quotes if set to ENT_COMPAT, * both single and double if set to ENT_QUOTES or none if * set to ENT_NOQUOTES. Also compatible with old values; * converting single quotes if set to 'single', double if * set to 'double' or both if otherwise set. Default is * ENT_NOQUOTES. * @param bool|string $charset Optional. The character encoding of the string. Default is false. * @param bool $double_encode Optional. Whether to encode existing html entities. Default is false. * * @return string The encoded text with HTML entities. */ public static function _wp_specialchars($string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false) // phpcs:ignore { $string = (string) $string; if (0 === strlen($string)) { return ''; } // Don't bother if there are no specialchars - saves some processing if (!preg_match('/[&<>"\']/', $string)) { return $string; } // Account for the previous behaviour of the function when the $quote_style is not an accepted value if (empty($quote_style)) { $quote_style = ENT_NOQUOTES; } elseif (!in_array($quote_style, array(0, 2, 3, 'single', 'double'), true)) { $quote_style = ENT_QUOTES; } // Store the site charset as a static to avoid multiple calls to wp_load_alloptions() if (!$charset) { static $_charset = null; if (!isset($_charset)) { $_charset = ''; } $charset = $_charset; } if (in_array($charset, array('utf8', 'utf-8', 'UTF8'))) { $charset = 'UTF-8'; } $_quote_style = $quote_style; if ($quote_style === 'double') { $quote_style = ENT_COMPAT; $_quote_style = ENT_COMPAT; } elseif ($quote_style === 'single') { $quote_style = ENT_NOQUOTES; } if (!$double_encode) { // Guarantee every &entity; is valid, convert &garbage; into &garbage; // This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable. $string = self::wp_kses_normalize_entities($string); } $string = @htmlspecialchars($string, $quote_style, $charset, $double_encode); // Back-compat. if ('single' === $_quote_style) { $string = str_replace("'", ''', $string); } return $string; } /** * Perform a deep string replace operation to ensure the values in $search are no longer present * * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that * str_replace would return * * @access private * * @param string|string[] $search The value being searched for, otherwise known as the needle. * An array may be used to designate multiple needles. * @param string $subject The string being searched and replaced on, otherwise known as the haystack. * * @return string The string with the replaced svalues. */ private static function _deep_replace($search, $subject) // phpcs:ignore { $subject = (string) $subject; $count = 1; while ($count) { $subject = str_replace($search, '', $subject, $count); } return $subject; } /** * Converts and fixes HTML entities. * * This function normalizes HTML entities. It will convert `AT&T` to the correct * `AT&T`, `:` to `:`, `&#XYZZY;` to `&#XYZZY;` and so on. * * @param string $string Content to normalize entities * * @return string Content with normalized entities */ public static function wp_kses_normalize_entities($string) { // Disarm all entities by converting & to & $string = str_replace('&', '&', $string); // Change back the allowed entities in our entity whitelist $string = preg_replace_callback('/&([A-Za-z]{2,8}[0-9]{0,2});/', array(__CLASS__, 'wp_kses_named_entities'), $string); $string = preg_replace_callback('/&#(0*[0-9]{1,7});/', array(__CLASS__, 'wp_kses_normalize_entities2'), $string); $string = preg_replace_callback('/&#[Xx](0*[0-9A-Fa-f]{1,6});/', array(__CLASS__, 'wp_kses_normalize_entities3'), $string); return $string; } /** * Callback for wp_kses_normalize_entities() regular expression. * * This function only accepts valid named entity references, which are finite, * case-sensitive, and highly scrutinized by HTML and XML validators. * * @param string[] $matches preg_replace_callback() matches array * * @return string Correctly encoded entity */ public static function wp_kses_named_entities($matches) { if (empty($matches[1])) { return ''; } $allowedentitynames = array( 'nbsp', 'iexcl', 'cent', 'pound', 'curren', 'yen', 'brvbar', 'sect', 'uml', 'copy', 'ordf', 'laquo', 'not', 'shy', 'reg', 'macr', 'deg', 'plusmn', 'acute', 'micro', 'para', 'middot', 'cedil', 'ordm', 'raquo', 'iquest', 'Agrave', 'Aacute', 'Acirc', 'Atilde', 'Auml', 'Aring', 'AElig', 'Ccedil', 'Egrave', 'Eacute', 'Ecirc', 'Euml', 'Igrave', 'Iacute', 'Icirc', 'Iuml', 'ETH', 'Ntilde', 'Ograve', 'Oacute', 'Ocirc', 'Otilde', 'Ouml', 'times', 'Oslash', 'Ugrave', 'Uacute', 'Ucirc', 'Uuml', 'Yacute', 'THORN', 'szlig', 'agrave', 'aacute', 'acirc', 'atilde', 'auml', 'aring', 'aelig', 'ccedil', 'egrave', 'eacute', 'ecirc', 'euml', 'igrave', 'iacute', 'icirc', 'iuml', 'eth', 'ntilde', 'ograve', 'oacute', 'ocirc', 'otilde', 'ouml', 'divide', 'oslash', 'ugrave', 'uacute', 'ucirc', 'uuml', 'yacute', 'thorn', 'yuml', 'quot', 'amp', 'lt', 'gt', 'apos', 'OElig', 'oelig', 'Scaron', 'scaron', 'Yuml', 'circ', 'tilde', 'ensp', 'emsp', 'thinsp', 'zwnj', 'zwj', 'lrm', 'rlm', 'ndash', 'mdash', 'lsquo', 'rsquo', 'sbquo', 'ldquo', 'rdquo', 'bdquo', 'dagger', 'Dagger', 'permil', 'lsaquo', 'rsaquo', 'euro', 'fnof', 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma', 'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega', 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigmaf', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega', 'thetasym', 'upsih', 'piv', 'bull', 'hellip', 'prime', 'Prime', 'oline', 'frasl', 'weierp', 'image', 'real', 'trade', 'alefsym', 'larr', 'uarr', 'rarr', 'darr', 'harr', 'crarr', 'lArr', 'uArr', 'rArr', 'dArr', 'hArr', 'forall', 'part', 'exist', 'empty', 'nabla', 'isin', 'notin', 'ni', 'prod', 'sum', 'minus', 'lowast', 'radic', 'prop', 'infin', 'ang', 'and', 'or', 'cap', 'cup', 'int', 'sim', 'cong', 'asymp', 'ne', 'equiv', 'le', 'ge', 'sub', 'sup', 'nsub', 'sube', 'supe', 'oplus', 'otimes', 'perp', 'sdot', 'lceil', 'rceil', 'lfloor', 'rfloor', 'lang', 'rang', 'loz', 'spades', 'clubs', 'hearts', 'diams', 'sup1', 'sup2', 'sup3', 'frac14', 'frac12', 'frac34', 'there4', ); $i = $matches[1]; return (!in_array($i, $allowedentitynames) ) ? "&$i;" : "&$i;"; } /** * Helper function to determine if a Unicode value is valid. * * @param int $i Unicode value * * @return bool True if the value was a valid Unicode number */ public static function wp_valid_unicode($i) { return ( $i == 0x9 || $i == 0xa || $i == 0xd || ($i >= 0x20 && $i <= 0xd7ff) || ($i >= 0xe000 && $i <= 0xfffd) || ($i >= 0x10000 && $i <= 0x10ffff) ); } /** * Callback for wp_kses_normalize_entities() regular expression. * * This function helps wp_kses_normalize_entities() to only accept 16-bit * values and nothing more for `&#number;` entities. * * @access private * * @param string[] $matches preg_replace_callback() matches array * * @return string Correctly encoded entity */ public static function wp_kses_normalize_entities2($matches) { if (empty($matches[1])) { return ''; } $i = $matches[1]; if (self::wp_valid_unicode($i)) { // @phpstan-ignore-line $i = str_pad(ltrim($i, '0'), 3, '0', STR_PAD_LEFT); $i = "&#$i;"; } else { $i = "&#$i;"; } return $i; } /** * Callback for wp_kses_normalize_entities() for regular expression. * * This function helps wp_kses_normalize_entities() to only accept valid Unicode * numeric entities in hex form. * * @access private * * @param string[] $matches preg_replace_callback() matches array * * @return string Correctly encoded entity */ public static function wp_kses_normalize_entities3($matches) { if (empty($matches[1])) { return ''; } $hexchars = $matches[1]; return (!self::wp_valid_unicode(hexdec($hexchars)) ) ? "&#x$hexchars;" : '&#x' . ltrim($hexchars, '0') . ';'; } /** * Retrieve a list of protocols to allow in HTML attributes. * * @since 3.3.0 * @since 4.3.0 Added 'webcal' to the protocols array. * @since 4.7.0 Added 'urn' to the protocols array. * * @see wp_kses() * @see esc_url() * * @return string[] Array of allowed protocols. Defaults to an array containing 'http', 'https', * 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', * 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', 'webcal', and 'urn'. */ public static function wp_allowed_protocols() { static $protocols = array(); if (empty($protocols)) { $protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', 'webcal', 'urn', ); } return $protocols; } /** * Checks and cleans a URL. * * A number of characters are removed from the URL. If the URL is for displaying * (the default behaviour) ampersands are also replaced. The {@see 'clean_url'} filter * is applied to the returned cleaned URL. * * @since 2.8.0 * * @param string $url The URL to be cleaned. * @param string[] $protocols Optional. An array of acceptable protocols. * Defaults to return value of * wp_allowed_protocols() * @param string $_context Private. Use esc_url_raw() for database usage. * * @return string The cleaned $url after the {@see 'clean_url'} filter is applied. */ public static function esc_url($url, $protocols = null, $_context = 'display') { $original_url = $url; if ('' == $url) { return $url; } $url = str_replace(' ', '%20', $url); $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url); if ('' === $url) { return $url; } if (0 !== stripos($url, 'mailto:')) { $strip = array( '%0d', '%0a', '%0D', '%0A', ); $url = self::_deep_replace($strip, $url); } $url = str_replace(';//', '://', $url); /* If the URL doesn't appear to contain a scheme, we * presume it needs http:// prepended (unless a relative * link starting with /, # or ? or a php file). */ if ( strpos($url, ':') === false && !in_array($url[0], array('/', '#', '?')) && !preg_match('/^[a-z0-9-]+?\.php/i', $url) ) { $url = 'http://' . $url; } // Replace ampersands and single quotes only when displaying. if ('display' == $_context) { $url = self::wp_kses_normalize_entities($url); $url = str_replace('&', '&', $url); $url = str_replace("'", ''', $url); } if (( false !== strpos($url, '[') ) || ( false !== strpos($url, ']') )) { $parsed = wp_parse_url($url); $front = ''; if (isset($parsed['scheme'])) { $front .= $parsed['scheme'] . '://'; } elseif ('/' === $url[0]) { $front .= '//'; } if (isset($parsed['user'])) { $front .= $parsed['user']; } if (isset($parsed['pass'])) { $front .= ':' . $parsed['pass']; } if (isset($parsed['user']) || isset($parsed['pass'])) { $front .= '@'; } if (isset($parsed['host'])) { $front .= $parsed['host']; } if (isset($parsed['port'])) { $front .= ':' . $parsed['port']; } $end_dirty = str_replace($front, '', $url); $end_clean = str_replace(array('[', ']'), array('%5B', '%5D'), $end_dirty); $url = str_replace($end_dirty, $end_clean, $url); } if ('/' === $url[0]) { $good_protocol_url = $url; } else { if (!is_array($protocols)) { $protocols = self::wp_allowed_protocols(); } $good_protocol_url = self::wp_kses_bad_protocol($url, $protocols); if (strtolower($good_protocol_url) != strtolower($url)) { return ''; } } /** * Filters a string cleaned and escaped for output as a URL. * * @since 2.3.0 * * @param string $good_protocol_url The cleaned URL to be returned. * @param string $original_url The URL prior to cleaning. * @param string $_context If 'display', replace ampersands and single quotes only. */ return $good_protocol_url; } /** * Removes any invalid control characters in $string. * * Also removes any instance of the '\0' string. * * @param string $string The text which is to be purified. * @param array<string,mixed> $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'. * * @return string */ public static function wp_kses_no_null($string, $options = null) { if (!isset($options['slash_zero'])) { $options = array('slash_zero' => 'remove'); } $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string); if ('remove' == $options['slash_zero']) { $string = preg_replace('/\\\\+0+/', '', $string); } return $string; } /** * Sanitize string from bad protocols. * * This function removes all non-allowed protocols from the beginning of * $string. It ignores whitespace and the case of the letters, and it does * understand HTML entities. It does its work in a while loop, so it won't be * fooled by a string like "javascript:javascript:alert(57)". * * @param string $string Content to filter bad protocols from * @param string[] $allowed_protocols Allowed protocols to keep * * @return string Filtered content */ public static function wp_kses_bad_protocol($string, $allowed_protocols) { $string = self::wp_kses_no_null($string); $iterations = 0; do { $original_string = $string; $string = self::wp_kses_bad_protocol_once($string, $allowed_protocols); } while ($original_string != $string && ++$iterations < 6); if ($original_string != $string) { return ''; } return $string; } /** * Sanitizes content from bad protocols and other characters. * * This function searches for URL protocols at the beginning of $string, while * handling whitespace and HTML entities. * * @param string $string Content to check for bad protocols * @param string[] $allowed_protocols Allowed protocols * @param int $count Optional. Number of times the function * * @return string Sanitized content */ public static function wp_kses_bad_protocol_once($string, $allowed_protocols, $count = 1) { $string2 = preg_split('/:|�*58;|�*3a;/i', $string, 2); if (isset($string2[1]) && !preg_match('%/\?%', $string2[0])) { $string = trim($string2[1]); $protocol = self::wp_kses_bad_protocol_once2($string2[0], $allowed_protocols); if ('feed:' == $protocol) { if ($count > 2) { return ''; } $string = wp_kses_bad_protocol_once($string, $allowed_protocols, ++$count); if (empty($string)) { return $string; } } $string = $protocol . $string; } return $string; } /** * Convert all entities to their character counterparts. * * This function decodes numeric HTML entities (`A` and `A`). * It doesn't do anything with other entities like ä, but we don't * need them in the URL protocol whitelisting system anyway. * * @param string $string Content to change entities * * @return string Content after decoded entities */ public static function wp_kses_decode_entities($string) { $string = preg_replace_callback('/&#([0-9]+);/', array(__CLASS__, '_wp_kses_decode_entities_chr'), $string); $string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', array(__CLASS__, '_wp_kses_decode_entities_chr_hexdec'), $string); return $string; } /** * Regex callback for wp_kses_decode_entities() * * @param string[] $match preg match * * @return string */ public static function _wp_kses_decode_entities_chr($match) // phpcs:ignore { return chr($match[1]); // @phpstan-ignore-line } /** * Regex callback for wp_kses_decode_entities() * * @param string[] $match preg match * * @return string */ public static function _wp_kses_decode_entities_chr_hexdec($match) // phpcs:ignore { return chr(hexdec($match[1])); } /** * Callback for wp_kses_bad_protocol_once() regular expression. * * This function processes URL protocols, checks to see if they're in the * white-list or not, and returns different data depending on the answer. * * @access private * * @param string $string URI scheme to check against the whitelist * @param string[] $allowed_protocols Allowed protocols * * @return string Sanitized content */ public static function wp_kses_bad_protocol_once2($string, $allowed_protocols) { $string2 = self::wp_kses_decode_entities($string); $string2 = preg_replace('/\s/', '', $string2); $string2 = self::wp_kses_no_null($string2); $string2 = strtolower($string2); $allowed = false; foreach ((array) $allowed_protocols as $one_protocol) { if (strtolower($one_protocol) == $string2) { $allowed = true; break; } } if ($allowed) { return "$string2:"; } else { return ''; } } /** * Toggle maintenance mode for the site. * * Creates/deletes the maintenance file to enable/disable maintenance mode. * * @param bool $enable True to enable maintenance mode, false to disable. * * @return void */ public static function maintenanceMode($enable = false) { $homePath = SnapIO::trailingslashit(PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW)); if (!is_writable($homePath)) { Log::info('CAN\'T ' . ($enable ? 'SET' : 'REMOVE') . ' MAINTENANCE MODE, ROOT FOLDER NOT WRITABLE'); return; } $maintenanceFile = $homePath . '.maintenance'; $indexFile = $homePath . 'index.html'; if (file_exists($indexFile)) { $indexContent = file_get_contents($indexFile); $manageIndex = (strpos($indexContent, self::MAINTENANCE_INDEX_MARKER) !== false); } else { $manageIndex = true; } if ($enable) { Log::info('MAINTENANCE MODE ENABLE'); if (file_put_contents($maintenanceFile, '<?php $upgrading = ' . time() . '; ?>') == false) { Log::info('CAN\'T SET MAINTENANCE MODE FILE \"' . $maintenanceFile . "\""); } if ($manageIndex && SnapIo::copy(DUPX_INIT . '/assets/maintenance.html', $indexFile) == false) { Log::info('CAN\'T SET MAINTENANCE INDEX FILE \"' . $indexFile . "\""); } } else { Log::info('MAINTENANCE MODE DISABLE'); if (file_exists($maintenanceFile) && unlink($maintenanceFile) == false) { Log::info('CAN\'T REMOVE MAINTENANCE MODE FILE \"' . $maintenanceFile . "\""); } if ($manageIndex && file_exists($indexFile) && unlink($indexFile) == false) { Log::info('CAN\'T REMOVE MAINTENANCE INDEX FILE \"' . $indexFile . "\""); } } } /** * Check if string is base64 encoded * * @param string $str input string * * @return false|string return false if isn't base64 string or decoded string */ public static function is_base64($str) { // Check if there are valid base64 characters if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $str)) { return false; } // Decode the string in strict mode and check the results $decoded = base64_decode($str, true); if (false === $decoded) { return false; } // Encode the string again if (base64_encode($decoded) != $str) { return false; } return $decoded; } /** * * @param string[] $matches regex match * * @return string */ public static function encodeUtf8CharFromRegexMatch($matches) { if (empty($matches) || !is_array($matches)) { return ''; } else { return json_decode('"' . $matches[0] . '"'); } } /** * this function escape generic string to prevent security issue. * Used to replace string in wp transformer * * for example * abc'" become "abc'\"" * * @param string $str input string * @param bool $addQuote if true add " before and after string * * @return string */ public static function getEscapedGenericString($str, $addQuote = true) { $result = SnapJson::jsonEncode(trim($str)); $result = str_replace(array('\/', '$'), array('/', '\\$'), $result); $result = preg_replace_callback( '/\\\\u[a-fA-F0-9]{4}/m', array( __CLASS__, 'encodeUtf8CharFromRegexMatch', ), $result ); if (!$addQuote) { $result = substr($result, 1, strlen($result) - 2); } return $result; } }