';
/**
* 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 '
';
print_r($var);
echo '';
} 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[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 `:`, `YZZY;` 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;" : '' . 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 $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, '') == 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;
}
}