aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Core.php
diff options
context:
space:
mode:
authorCharles <sircharlesaze@gmail.com>2020-01-09 10:55:03 +0100
committerCharles <sircharlesaze@gmail.com>2020-01-09 13:09:38 +0100
commit04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa (patch)
tree5c691241355c943a3c68ddb06b8cf8c60aa11319 /srcs/phpmyadmin/libraries/classes/Core.php
parent7e0d85db834d6351ed85d01e5126ac31dc510b86 (diff)
downloadft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.gz
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.bz2
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.zip
phpmyadmin working
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Core.php')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Core.php1302
1 files changed, 1302 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Core.php b/srcs/phpmyadmin/libraries/classes/Core.php
new file mode 100644
index 0000000..581afbc
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Core.php
@@ -0,0 +1,1302 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Core functions used all over the scripts.
+ * This script is distinct from libraries/common.inc.php because this
+ * script is called from /test.
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin;
+
+use PhpMyAdmin\Di\Migration;
+use PhpMyAdmin\Display\Error as DisplayError;
+
+/**
+ * Core class
+ *
+ * @package PhpMyAdmin
+ */
+class Core
+{
+ /**
+ * the whitelist for goto parameter
+ * @static array $goto_whitelist
+ */
+ public static $goto_whitelist = [
+ 'db_datadict.php',
+ 'db_sql.php',
+ 'db_events.php',
+ 'db_export.php',
+ 'db_importdocsql.php',
+ 'db_multi_table_query.php',
+ 'db_qbe.php',
+ 'db_structure.php',
+ 'db_import.php',
+ 'db_operations.php',
+ 'db_search.php',
+ 'db_routines.php',
+ 'export.php',
+ 'import.php',
+ 'index.php',
+ 'pdf_pages.php',
+ 'pdf_schema.php',
+ 'server_binlog.php',
+ 'server_collations.php',
+ 'server_databases.php',
+ 'server_engines.php',
+ 'server_export.php',
+ 'server_import.php',
+ 'server_privileges.php',
+ 'server_sql.php',
+ 'server_status.php',
+ 'server_status_advisor.php',
+ 'server_status_monitor.php',
+ 'server_status_queries.php',
+ 'server_status_variables.php',
+ 'server_variables.php',
+ 'sql.php',
+ 'tbl_addfield.php',
+ 'tbl_change.php',
+ 'tbl_create.php',
+ 'tbl_import.php',
+ 'tbl_indexes.php',
+ 'tbl_sql.php',
+ 'tbl_export.php',
+ 'tbl_operations.php',
+ 'tbl_structure.php',
+ 'tbl_relation.php',
+ 'tbl_replace.php',
+ 'tbl_row_action.php',
+ 'tbl_select.php',
+ 'tbl_zoom_select.php',
+ 'transformation_overview.php',
+ 'transformation_wrapper.php',
+ 'user_password.php',
+ ];
+
+ /**
+ * checks given $var and returns it if valid, or $default of not valid
+ * given $var is also checked for type being 'similar' as $default
+ * or against any other type if $type is provided
+ *
+ * <code>
+ * // $_REQUEST['db'] not set
+ * echo Core::ifSetOr($_REQUEST['db'], ''); // ''
+ * // $_POST['sql_query'] not set
+ * echo Core::ifSetOr($_POST['sql_query']); // null
+ * // $cfg['EnableFoo'] not set
+ * echo Core::ifSetOr($cfg['EnableFoo'], false, 'boolean'); // false
+ * echo Core::ifSetOr($cfg['EnableFoo']); // null
+ * // $cfg['EnableFoo'] set to 1
+ * echo Core::ifSetOr($cfg['EnableFoo'], false, 'boolean'); // false
+ * echo Core::ifSetOr($cfg['EnableFoo'], false, 'similar'); // 1
+ * echo Core::ifSetOr($cfg['EnableFoo'], false); // 1
+ * // $cfg['EnableFoo'] set to true
+ * echo Core::ifSetOr($cfg['EnableFoo'], false, 'boolean'); // true
+ * </code>
+ *
+ * @param mixed $var param to check
+ * @param mixed $default default value
+ * @param mixed $type var type or array of values to check against $var
+ *
+ * @return mixed $var or $default
+ *
+ * @see self::isValid()
+ */
+ public static function ifSetOr(&$var, $default = null, $type = 'similar')
+ {
+ if (! self::isValid($var, $type, $default)) {
+ return $default;
+ }
+
+ return $var;
+ }
+
+ /**
+ * checks given $var against $type or $compare
+ *
+ * $type can be:
+ * - false : no type checking
+ * - 'scalar' : whether type of $var is integer, float, string or boolean
+ * - 'numeric' : whether type of $var is any number representation
+ * - 'length' : whether type of $var is scalar with a string length > 0
+ * - 'similar' : whether type of $var is similar to type of $compare
+ * - 'equal' : whether type of $var is identical to type of $compare
+ * - 'identical' : whether $var is identical to $compare, not only the type!
+ * - or any other valid PHP variable type
+ *
+ * <code>
+ * // $_REQUEST['doit'] = true;
+ * Core::isValid($_REQUEST['doit'], 'identical', 'true'); // false
+ * // $_REQUEST['doit'] = 'true';
+ * Core::isValid($_REQUEST['doit'], 'identical', 'true'); // true
+ * </code>
+ *
+ * NOTE: call-by-reference is used to not get NOTICE on undefined vars,
+ * but the var is not altered inside this function, also after checking a var
+ * this var exists nut is not set, example:
+ * <code>
+ * // $var is not set
+ * isset($var); // false
+ * functionCallByReference($var); // false
+ * isset($var); // true
+ * functionCallByReference($var); // true
+ * </code>
+ *
+ * to avoid this we set this var to null if not isset
+ *
+ * @param mixed $var variable to check
+ * @param mixed $type var type or array of valid values to check against $var
+ * @param mixed $compare var to compare with $var
+ *
+ * @return boolean whether valid or not
+ *
+ * @todo add some more var types like hex, bin, ...?
+ * @see https://secure.php.net/gettype
+ */
+ public static function isValid(&$var, $type = 'length', $compare = null): bool
+ {
+ if (! isset($var)) {
+ // var is not even set
+ return false;
+ }
+
+ if ($type === false) {
+ // no vartype requested
+ return true;
+ }
+
+ if (is_array($type)) {
+ return in_array($var, $type);
+ }
+
+ // allow some aliases of var types
+ $type = strtolower($type);
+ switch ($type) {
+ case 'identic':
+ $type = 'identical';
+ break;
+ case 'len':
+ $type = 'length';
+ break;
+ case 'bool':
+ $type = 'boolean';
+ break;
+ case 'float':
+ $type = 'double';
+ break;
+ case 'int':
+ $type = 'integer';
+ break;
+ case 'null':
+ $type = 'NULL';
+ break;
+ }
+
+ if ($type === 'identical') {
+ return $var === $compare;
+ }
+
+ // whether we should check against given $compare
+ if ($type === 'similar') {
+ switch (gettype($compare)) {
+ case 'string':
+ case 'boolean':
+ $type = 'scalar';
+ break;
+ case 'integer':
+ case 'double':
+ $type = 'numeric';
+ break;
+ default:
+ $type = gettype($compare);
+ }
+ } elseif ($type === 'equal') {
+ $type = gettype($compare);
+ }
+
+ // do the check
+ if ($type === 'length' || $type === 'scalar') {
+ $is_scalar = is_scalar($var);
+ if ($is_scalar && $type === 'length') {
+ return strlen((string) $var) > 0;
+ }
+ return $is_scalar;
+ }
+
+ if ($type === 'numeric') {
+ return is_numeric($var);
+ }
+
+ return gettype($var) === $type;
+ }
+
+ /**
+ * Removes insecure parts in a path; used before include() or
+ * require() when a part of the path comes from an insecure source
+ * like a cookie or form.
+ *
+ * @param string $path The path to check
+ *
+ * @return string The secured path
+ *
+ * @access public
+ */
+ public static function securePath(string $path): string
+ {
+ // change .. to .
+ return preg_replace('@\.\.*@', '.', $path);
+ } // end function
+
+ /**
+ * displays the given error message on phpMyAdmin error page in foreign language,
+ * ends script execution and closes session
+ *
+ * loads language file if not loaded already
+ *
+ * @param string $error_message the error message or named error message
+ * @param string|array $message_args arguments applied to $error_message
+ *
+ * @return void
+ */
+ public static function fatalError(
+ string $error_message,
+ $message_args = null
+ ): void {
+ /* Use format string if applicable */
+ if (is_string($message_args)) {
+ $error_message = sprintf($error_message, $message_args);
+ } elseif (is_array($message_args)) {
+ $error_message = vsprintf($error_message, $message_args);
+ }
+
+ /*
+ * Avoid using Response class as config does not have to be loaded yet
+ * (this can happen on early fatal error)
+ */
+ if (isset($GLOBALS['dbi']) && $GLOBALS['dbi'] !== null && isset($GLOBALS['PMA_Config']) && $GLOBALS['PMA_Config']->get('is_setup') === false && Response::getInstance()->isAjax()) {
+ $response = Response::getInstance();
+ $response->setRequestStatus(false);
+ $response->addJSON('message', Message::error($error_message));
+ } elseif (! empty($_REQUEST['ajax_request'])) {
+ // Generate JSON manually
+ self::headerJSON();
+ echo json_encode(
+ [
+ 'success' => false,
+ 'message' => Message::error($error_message)->getDisplay(),
+ ]
+ );
+ } else {
+ $error_message = strtr($error_message, ['<br>' => '[br]']);
+ $error_header = __('Error');
+ $lang = isset($GLOBALS['lang']) ? $GLOBALS['lang'] : 'en';
+ $dir = isset($GLOBALS['text_dir']) ? $GLOBALS['text_dir'] : 'ltr';
+
+ echo DisplayError::display(new Template(), $lang, $dir, $error_header, $error_message);
+ }
+ if (! defined('TESTSUITE')) {
+ exit;
+ }
+ }
+
+ /**
+ * Returns a link to the PHP documentation
+ *
+ * @param string $target anchor in documentation
+ *
+ * @return string the URL
+ *
+ * @access public
+ */
+ public static function getPHPDocLink(string $target): string
+ {
+ /* List of PHP documentation translations */
+ $php_doc_languages = [
+ 'pt_BR',
+ 'zh',
+ 'fr',
+ 'de',
+ 'it',
+ 'ja',
+ 'pl',
+ 'ro',
+ 'ru',
+ 'fa',
+ 'es',
+ 'tr',
+ ];
+
+ $lang = 'en';
+ if (in_array($GLOBALS['lang'], $php_doc_languages)) {
+ $lang = $GLOBALS['lang'];
+ }
+
+ return self::linkURL('https://secure.php.net/manual/' . $lang . '/' . $target);
+ }
+
+ /**
+ * Warn or fail on missing extension.
+ *
+ * @param string $extension Extension name
+ * @param bool $fatal Whether the error is fatal.
+ * @param string $extra Extra string to append to message.
+ *
+ * @return void
+ */
+ public static function warnMissingExtension(
+ string $extension,
+ bool $fatal = false,
+ string $extra = ''
+ ): void {
+ /* Gettext does not have to be loaded yet here */
+ if (function_exists('__')) {
+ $message = __(
+ 'The %s extension is missing. Please check your PHP configuration.'
+ );
+ } else {
+ $message
+ = 'The %s extension is missing. Please check your PHP configuration.';
+ }
+ $doclink = self::getPHPDocLink('book.' . $extension . '.php');
+ $message = sprintf(
+ $message,
+ '[a@' . $doclink . '@Documentation][em]' . $extension . '[/em][/a]'
+ );
+ if ($extra != '') {
+ $message .= ' ' . $extra;
+ }
+ if ($fatal) {
+ self::fatalError($message);
+ return;
+ }
+
+ $GLOBALS['error_handler']->addError(
+ $message,
+ E_USER_WARNING,
+ '',
+ '',
+ false
+ );
+ }
+
+ /**
+ * returns count of tables in given db
+ *
+ * @param string $db database to count tables for
+ *
+ * @return integer count of tables in $db
+ */
+ public static function getTableCount(string $db): int
+ {
+ $tables = $GLOBALS['dbi']->tryQuery(
+ 'SHOW TABLES FROM ' . Util::backquote($db) . ';',
+ DatabaseInterface::CONNECT_USER,
+ DatabaseInterface::QUERY_STORE
+ );
+ if ($tables) {
+ $num_tables = $GLOBALS['dbi']->numRows($tables);
+ $GLOBALS['dbi']->freeResult($tables);
+ } else {
+ $num_tables = 0;
+ }
+
+ return $num_tables;
+ }
+
+ /**
+ * Converts numbers like 10M into bytes
+ * Used with permission from Moodle (https://moodle.org) by Martin Dougiamas
+ * (renamed with PMA prefix to avoid double definition when embedded
+ * in Moodle)
+ *
+ * @param string|int $size size (Default = 0)
+ *
+ * @return integer
+ */
+ public static function getRealSize($size = 0): int
+ {
+ if (! $size) {
+ return 0;
+ }
+
+ $binaryprefixes = [
+ 'T' => 1099511627776,
+ 't' => 1099511627776,
+ 'G' => 1073741824,
+ 'g' => 1073741824,
+ 'M' => 1048576,
+ 'm' => 1048576,
+ 'K' => 1024,
+ 'k' => 1024,
+ ];
+
+ if (preg_match('/^([0-9]+)([KMGT])/i', $size, $matches)) {
+ return $matches[1] * $binaryprefixes[$matches[2]];
+ }
+
+ return (int) $size;
+ } // end getRealSize()
+
+ /**
+ * Checks given $page against given $whitelist and returns true if valid
+ * it optionally ignores query parameters in $page (script.php?ignored)
+ *
+ * @param string $page page to check
+ * @param array $whitelist whitelist to check page against
+ * @param boolean $include whether the page is going to be included
+ *
+ * @return boolean whether $page is valid or not (in $whitelist or not)
+ */
+ public static function checkPageValidity(&$page, array $whitelist = [], $include = false): bool
+ {
+ if (empty($whitelist)) {
+ $whitelist = self::$goto_whitelist;
+ }
+ if (empty($page)) {
+ return false;
+ }
+
+ if (in_array($page, $whitelist)) {
+ return true;
+ }
+ if ($include) {
+ return false;
+ }
+
+ $_page = mb_substr(
+ $page,
+ 0,
+ mb_strpos($page . '?', '?')
+ );
+ if (in_array($_page, $whitelist)) {
+ return true;
+ }
+
+ $_page = urldecode($page);
+ $_page = mb_substr(
+ $_page,
+ 0,
+ mb_strpos($_page . '?', '?')
+ );
+ if (in_array($_page, $whitelist)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * tries to find the value for the given environment variable name
+ *
+ * searches in $_SERVER, $_ENV then tries getenv() and apache_getenv()
+ * in this order
+ *
+ * @param string $var_name variable name
+ *
+ * @return string value of $var or empty string
+ */
+ public static function getenv(string $var_name): string
+ {
+ if (isset($_SERVER[$var_name])) {
+ return (string) $_SERVER[$var_name];
+ }
+
+ if (isset($_ENV[$var_name])) {
+ return (string) $_ENV[$var_name];
+ }
+
+ if (getenv($var_name)) {
+ return getenv($var_name);
+ }
+
+ if (function_exists('apache_getenv')
+ && apache_getenv($var_name, true)
+ ) {
+ return apache_getenv($var_name, true);
+ }
+
+ return '';
+ }
+
+ /**
+ * Send HTTP header, taking IIS limits into account (600 seems ok)
+ *
+ * @param string $uri the header to send
+ * @param bool $use_refresh whether to use Refresh: header when running on IIS
+ *
+ * @return void
+ */
+ public static function sendHeaderLocation(string $uri, bool $use_refresh = false): void
+ {
+ if ($GLOBALS['PMA_Config']->get('PMA_IS_IIS') && mb_strlen($uri) > 600) {
+ Response::getInstance()->disable();
+
+ $template = new Template();
+ echo $template->render('header_location', ['uri' => $uri]);
+
+ return;
+ }
+
+ /*
+ * Avoid relative path redirect problems in case user entered URL
+ * like /phpmyadmin/index.php/ which some web servers happily accept.
+ */
+ if ($uri[0] == '.') {
+ $uri = $GLOBALS['PMA_Config']->getRootPath() . substr($uri, 2);
+ }
+
+ $response = Response::getInstance();
+
+ session_write_close();
+ if ($response->headersSent()) {
+ trigger_error(
+ 'Core::sendHeaderLocation called when headers are already sent!',
+ E_USER_ERROR
+ );
+ }
+ // bug #1523784: IE6 does not like 'Refresh: 0', it
+ // results in a blank page
+ // but we need it when coming from the cookie login panel)
+ if ($GLOBALS['PMA_Config']->get('PMA_IS_IIS') && $use_refresh) {
+ $response->header('Refresh: 0; ' . $uri);
+ } else {
+ $response->header('Location: ' . $uri);
+ }
+ }
+
+ /**
+ * Outputs application/json headers. This includes no caching.
+ *
+ * @return void
+ */
+ public static function headerJSON(): void
+ {
+ if (defined('TESTSUITE')) {
+ return;
+ }
+ // No caching
+ self::noCacheHeader();
+ // MIME type
+ header('Content-Type: application/json; charset=UTF-8');
+ // Disable content sniffing in browser
+ // This is needed in case we include HTML in JSON, browser might assume it's
+ // html to display
+ header('X-Content-Type-Options: nosniff');
+ }
+
+ /**
+ * Outputs headers to prevent caching in browser (and on the way).
+ *
+ * @return void
+ */
+ public static function noCacheHeader(): void
+ {
+ if (defined('TESTSUITE')) {
+ return;
+ }
+ // rfc2616 - Section 14.21
+ header('Expires: ' . gmdate(DATE_RFC1123));
+ // HTTP/1.1
+ header(
+ 'Cache-Control: no-store, no-cache, must-revalidate,'
+ . ' pre-check=0, post-check=0, max-age=0'
+ );
+
+ header('Pragma: no-cache'); // HTTP/1.0
+ // test case: exporting a database into a .gz file with Safari
+ // would produce files not having the current time
+ // (added this header for Safari but should not harm other browsers)
+ header('Last-Modified: ' . gmdate(DATE_RFC1123));
+ }
+
+
+ /**
+ * Sends header indicating file download.
+ *
+ * @param string $filename Filename to include in headers if empty,
+ * none Content-Disposition header will be sent.
+ * @param string $mimetype MIME type to include in headers.
+ * @param int $length Length of content (optional)
+ * @param bool $no_cache Whether to include no-caching headers.
+ *
+ * @return void
+ */
+ public static function downloadHeader(
+ string $filename,
+ string $mimetype,
+ int $length = 0,
+ bool $no_cache = true
+ ): void {
+ if ($no_cache) {
+ self::noCacheHeader();
+ }
+ /* Replace all possibly dangerous chars in filename */
+ $filename = Sanitize::sanitizeFilename($filename);
+ if (! empty($filename)) {
+ header('Content-Description: File Transfer');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ }
+ header('Content-Type: ' . $mimetype);
+ // inform the server that compression has been done,
+ // to avoid a double compression (for example with Apache + mod_deflate)
+ $notChromeOrLessThan43 = PMA_USR_BROWSER_AGENT != 'CHROME' // see bug #4942
+ || (PMA_USR_BROWSER_AGENT == 'CHROME' && PMA_USR_BROWSER_VER < 43);
+ if (strpos($mimetype, 'gzip') !== false && $notChromeOrLessThan43) {
+ header('Content-Encoding: gzip');
+ }
+ header('Content-Transfer-Encoding: binary');
+ if ($length > 0) {
+ header('Content-Length: ' . $length);
+ }
+ }
+
+ /**
+ * Returns value of an element in $array given by $path.
+ * $path is a string describing position of an element in an associative array,
+ * eg. Servers/1/host refers to $array[Servers][1][host]
+ *
+ * @param string $path path in the array
+ * @param array $array the array
+ * @param mixed $default default value
+ *
+ * @return mixed array element or $default
+ */
+ public static function arrayRead(string $path, array $array, $default = null)
+ {
+ $keys = explode('/', $path);
+ $value =& $array;
+ foreach ($keys as $key) {
+ if (! isset($value[$key])) {
+ return $default;
+ }
+ $value =& $value[$key];
+ }
+ return $value;
+ }
+
+ /**
+ * Stores value in an array
+ *
+ * @param string $path path in the array
+ * @param array $array the array
+ * @param mixed $value value to store
+ *
+ * @return void
+ */
+ public static function arrayWrite(string $path, array &$array, $value): void
+ {
+ $keys = explode('/', $path);
+ $last_key = array_pop($keys);
+ $a =& $array;
+ foreach ($keys as $key) {
+ if (! isset($a[$key])) {
+ $a[$key] = [];
+ }
+ $a =& $a[$key];
+ }
+ $a[$last_key] = $value;
+ }
+
+ /**
+ * Removes value from an array
+ *
+ * @param string $path path in the array
+ * @param array $array the array
+ *
+ * @return void
+ */
+ public static function arrayRemove(string $path, array &$array): void
+ {
+ $keys = explode('/', $path);
+ $keys_last = array_pop($keys);
+ $path = [];
+ $depth = 0;
+
+ $path[0] =& $array;
+ $found = true;
+ // go as deep as required or possible
+ foreach ($keys as $key) {
+ if (! isset($path[$depth][$key])) {
+ $found = false;
+ break;
+ }
+ $depth++;
+ $path[$depth] =& $path[$depth - 1][$key];
+ }
+ // if element found, remove it
+ if ($found) {
+ unset($path[$depth][$keys_last]);
+ $depth--;
+ }
+
+ // remove empty nested arrays
+ for (; $depth >= 0; $depth--) {
+ if (! isset($path[$depth + 1]) || count($path[$depth + 1]) === 0) {
+ unset($path[$depth][$keys[$depth]]);
+ } else {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns link to (possibly) external site using defined redirector.
+ *
+ * @param string $url URL where to go.
+ *
+ * @return string URL for a link.
+ */
+ public static function linkURL(string $url): string
+ {
+ if (! preg_match('#^https?://#', $url)) {
+ return $url;
+ }
+
+ $params = [];
+ $params['url'] = $url;
+
+ $url = Url::getCommon($params);
+ //strip off token and such sensitive information. Just keep url.
+ $arr = parse_url($url);
+ parse_str($arr["query"], $vars);
+ $query = http_build_query(["url" => $vars["url"]]);
+
+ if ($GLOBALS['PMA_Config'] !== null && $GLOBALS['PMA_Config']->get('is_setup')) {
+ $url = '../url.php?' . $query;
+ } else {
+ $url = './url.php?' . $query;
+ }
+
+ return $url;
+ }
+
+ /**
+ * Checks whether domain of URL is whitelisted domain or not.
+ * Use only for URLs of external sites.
+ *
+ * @param string $url URL of external site.
+ *
+ * @return boolean True: if domain of $url is allowed domain,
+ * False: otherwise.
+ */
+ public static function isAllowedDomain(string $url): bool
+ {
+ $arr = parse_url($url);
+ // We need host to be set
+ if (! isset($arr['host']) || strlen($arr['host']) == 0) {
+ return false;
+ }
+ // We do not want these to be present
+ $blocked = [
+ 'user',
+ 'pass',
+ 'port',
+ ];
+ foreach ($blocked as $part) {
+ if (isset($arr[$part]) && strlen((string) $arr[$part]) != 0) {
+ return false;
+ }
+ }
+ $domain = $arr["host"];
+ $domainWhiteList = [
+ /* Include current domain */
+ $_SERVER['SERVER_NAME'],
+ /* phpMyAdmin domains */
+ 'wiki.phpmyadmin.net',
+ 'www.phpmyadmin.net',
+ 'phpmyadmin.net',
+ 'demo.phpmyadmin.net',
+ 'docs.phpmyadmin.net',
+ /* mysql.com domains */
+ 'dev.mysql.com',
+ 'bugs.mysql.com',
+ /* mariadb domains */
+ 'mariadb.org',
+ 'mariadb.com',
+ /* php.net domains */
+ 'php.net',
+ 'secure.php.net',
+ /* Github domains*/
+ 'github.com',
+ 'www.github.com',
+ /* Percona domains */
+ 'www.percona.com',
+ /* Following are doubtful ones. */
+ 'mysqldatabaseadministration.blogspot.com',
+ ];
+
+ return in_array($domain, $domainWhiteList);
+ }
+
+ /**
+ * Replace some html-unfriendly stuff
+ *
+ * @param string $buffer String to process
+ *
+ * @return string Escaped and cleaned up text suitable for html
+ */
+ public static function mimeDefaultFunction(string $buffer): string
+ {
+ $buffer = htmlspecialchars($buffer);
+ $buffer = str_replace(' ', ' &nbsp;', $buffer);
+ return preg_replace("@((\015\012)|(\015)|(\012))@", '<br>' . "\n", $buffer);
+ }
+
+ /**
+ * Displays SQL query before executing.
+ *
+ * @param array|string $query_data Array containing queries or query itself
+ *
+ * @return void
+ */
+ public static function previewSQL($query_data): void
+ {
+ $retval = '<div class="preview_sql">';
+ if (empty($query_data)) {
+ $retval .= __('No change');
+ } elseif (is_array($query_data)) {
+ foreach ($query_data as $query) {
+ $retval .= Util::formatSql($query);
+ }
+ } else {
+ $retval .= Util::formatSql($query_data);
+ }
+ $retval .= '</div>';
+ $response = Response::getInstance();
+ $response->addJSON('sql_data', $retval);
+ exit;
+ }
+
+ /**
+ * recursively check if variable is empty
+ *
+ * @param mixed $value the variable
+ *
+ * @return bool true if empty
+ */
+ public static function emptyRecursive($value): bool
+ {
+ $empty = true;
+ if (is_array($value)) {
+ array_walk_recursive(
+ $value,
+ function ($item) use (&$empty) {
+ $empty = $empty && empty($item);
+ }
+ );
+ } else {
+ $empty = empty($value);
+ }
+ return $empty;
+ }
+
+ /**
+ * Creates some globals from $_POST variables matching a pattern
+ *
+ * @param array $post_patterns The patterns to search for
+ *
+ * @return void
+ */
+ public static function setPostAsGlobal(array $post_patterns): void
+ {
+ foreach (array_keys($_POST) as $post_key) {
+ foreach ($post_patterns as $one_post_pattern) {
+ if (preg_match($one_post_pattern, $post_key)) {
+ Migration::getInstance()->setGlobal($post_key, $_POST[$post_key]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates some globals from $_REQUEST
+ *
+ * @param string $param db|table
+ *
+ * @return void
+ */
+ public static function setGlobalDbOrTable(string $param): void
+ {
+ $value = '';
+ if (self::isValid($_REQUEST[$param])) {
+ $value = $_REQUEST[$param];
+ }
+ Migration::getInstance()->setGlobal($param, $value);
+ Migration::getInstance()->setGlobal('url_params', [$param => $value] + $GLOBALS['url_params']);
+ }
+
+ /**
+ * PATH_INFO could be compromised if set, so remove it from PHP_SELF
+ * and provide a clean PHP_SELF here
+ *
+ * @return void
+ */
+ public static function cleanupPathInfo(): void
+ {
+ global $PMA_PHP_SELF;
+
+ $PMA_PHP_SELF = self::getenv('PHP_SELF');
+ if (empty($PMA_PHP_SELF)) {
+ $PMA_PHP_SELF = urldecode(self::getenv('REQUEST_URI'));
+ }
+ $_PATH_INFO = self::getenv('PATH_INFO');
+ if (! empty($_PATH_INFO) && ! empty($PMA_PHP_SELF)) {
+ $question_pos = mb_strpos($PMA_PHP_SELF, '?');
+ if ($question_pos != false) {
+ $PMA_PHP_SELF = mb_substr($PMA_PHP_SELF, 0, $question_pos);
+ }
+ $path_info_pos = mb_strrpos($PMA_PHP_SELF, $_PATH_INFO);
+ if ($path_info_pos !== false) {
+ $path_info_part = mb_substr($PMA_PHP_SELF, $path_info_pos, mb_strlen($_PATH_INFO));
+ if ($path_info_part == $_PATH_INFO) {
+ $PMA_PHP_SELF = mb_substr($PMA_PHP_SELF, 0, $path_info_pos);
+ }
+ }
+ }
+
+ $path = [];
+ foreach (explode('/', $PMA_PHP_SELF) as $part) {
+ // ignore parts that have no value
+ if (empty($part) || $part === '.') {
+ continue;
+ }
+
+ if ($part !== '..') {
+ // cool, we found a new part
+ $path[] = $part;
+ } elseif (count($path) > 0) {
+ // going back up? sure
+ array_pop($path);
+ }
+ // Here we intentionall ignore case where we go too up
+ // as there is nothing sane to do
+ }
+
+ $PMA_PHP_SELF = htmlspecialchars('/' . implode('/', $path));
+ }
+
+ /**
+ * Checks that required PHP extensions are there.
+ * @return void
+ */
+ public static function checkExtensions(): void
+ {
+ /**
+ * Warning about mbstring.
+ */
+ if (! function_exists('mb_detect_encoding')) {
+ self::warnMissingExtension('mbstring');
+ }
+
+ /**
+ * We really need this one!
+ */
+ if (! function_exists('preg_replace')) {
+ self::warnMissingExtension('pcre', true);
+ }
+
+ /**
+ * JSON is required in several places.
+ */
+ if (! function_exists('json_encode')) {
+ self::warnMissingExtension('json', true);
+ }
+
+ /**
+ * ctype is required for Twig.
+ */
+ if (! function_exists('ctype_alpha')) {
+ self::warnMissingExtension('ctype', true);
+ }
+
+ /**
+ * hash is required for cookie authentication.
+ */
+ if (! function_exists('hash_hmac')) {
+ self::warnMissingExtension('hash', true);
+ }
+ }
+
+ /**
+ * Gets the "true" IP address of the current user
+ *
+ * @return string|bool the ip of the user
+ *
+ * @access private
+ */
+ public static function getIp()
+ {
+ /* Get the address of user */
+ if (empty($_SERVER['REMOTE_ADDR'])) {
+ /* We do not know remote IP */
+ return false;
+ }
+
+ $direct_ip = $_SERVER['REMOTE_ADDR'];
+
+ /* Do we trust this IP as a proxy? If yes we will use it's header. */
+ if (! isset($GLOBALS['cfg']['TrustedProxies'][$direct_ip])) {
+ /* Return true IP */
+ return $direct_ip;
+ }
+
+ /**
+ * Parse header in form:
+ * X-Forwarded-For: client, proxy1, proxy2
+ */
+ // Get header content
+ $value = self::getenv($GLOBALS['cfg']['TrustedProxies'][$direct_ip]);
+ // Grab first element what is client adddress
+ $value = explode(',', $value)[0];
+ // checks that the header contains only one IP address,
+ $is_ip = filter_var($value, FILTER_VALIDATE_IP);
+
+ if ($is_ip !== false) {
+ // True IP behind a proxy
+ return $value;
+ }
+
+ // We could not parse header
+ return false;
+ } // end of the 'getIp()' function
+
+ /**
+ * Sanitizes MySQL hostname
+ *
+ * * strips p: prefix(es)
+ *
+ * @param string $name User given hostname
+ *
+ * @return string
+ */
+ public static function sanitizeMySQLHost(string $name): string
+ {
+ while (strtolower(substr($name, 0, 2)) == 'p:') {
+ $name = substr($name, 2);
+ }
+
+ return $name;
+ }
+
+ /**
+ * Sanitizes MySQL username
+ *
+ * * strips part behind null byte
+ *
+ * @param string $name User given username
+ *
+ * @return string
+ */
+ public static function sanitizeMySQLUser(string $name): string
+ {
+ $position = strpos($name, chr(0));
+ if ($position !== false) {
+ return substr($name, 0, $position);
+ }
+ return $name;
+ }
+
+ /**
+ * Safe unserializer wrapper
+ *
+ * It does not unserialize data containing objects
+ *
+ * @param string $data Data to unserialize
+ *
+ * @return mixed
+ */
+ public static function safeUnserialize(string $data)
+ {
+ if (! is_string($data)) {
+ return null;
+ }
+
+ /* validate serialized data */
+ $length = strlen($data);
+ $depth = 0;
+ for ($i = 0; $i < $length; $i++) {
+ $value = $data[$i];
+
+ switch ($value) {
+ case '}':
+ /* end of array */
+ if ($depth <= 0) {
+ return null;
+ }
+ $depth--;
+ break;
+ case 's':
+ /* string */
+ // parse sting length
+ $strlen = intval(substr($data, $i + 2));
+ // string start
+ $i = strpos($data, ':', $i + 2);
+ if ($i === false) {
+ return null;
+ }
+ // skip string, quotes and ;
+ $i += 2 + $strlen + 1;
+ if ($data[$i] != ';') {
+ return null;
+ }
+ break;
+
+ case 'b':
+ case 'i':
+ case 'd':
+ /* bool, integer or double */
+ // skip value to sepearator
+ $i = strpos($data, ';', $i);
+ if ($i === false) {
+ return null;
+ }
+ break;
+ case 'a':
+ /* array */
+ // find array start
+ $i = strpos($data, '{', $i);
+ if ($i === false) {
+ return null;
+ }
+ // remember nesting
+ $depth++;
+ break;
+ case 'N':
+ /* null */
+ // skip to end
+ $i = strpos($data, ';', $i);
+ if ($i === false) {
+ return null;
+ }
+ break;
+ default:
+ /* any other elements are not wanted */
+ return null;
+ }
+ }
+
+ // check unterminated arrays
+ if ($depth > 0) {
+ return null;
+ }
+
+ return unserialize($data);
+ }
+
+ /**
+ * Applies changes to PHP configuration.
+ *
+ * @return void
+ */
+ public static function configure(): void
+ {
+ /**
+ * Set utf-8 encoding for PHP
+ */
+ ini_set('default_charset', 'utf-8');
+ mb_internal_encoding('utf-8');
+
+ /**
+ * Set precision to sane value, with higher values
+ * things behave slightly unexpectedly, for example
+ * round(1.2, 2) returns 1.199999999999999956.
+ */
+ ini_set('precision', '14');
+
+ /**
+ * check timezone setting
+ * this could produce an E_WARNING - but only once,
+ * if not done here it will produce E_WARNING on every date/time function
+ */
+ date_default_timezone_set(@date_default_timezone_get());
+ }
+
+ /**
+ * Check whether PHP configuration matches our needs.
+ *
+ * @return void
+ */
+ public static function checkConfiguration(): void
+ {
+ /**
+ * As we try to handle charsets by ourself, mbstring overloads just
+ * break it, see bug 1063821.
+ *
+ * We specifically use empty here as we are looking for anything else than
+ * empty value or 0.
+ */
+ if (extension_loaded('mbstring') && ! empty(ini_get('mbstring.func_overload'))) {
+ self::fatalError(
+ __(
+ 'You have enabled mbstring.func_overload in your PHP '
+ . 'configuration. This option is incompatible with phpMyAdmin '
+ . 'and might cause some data to be corrupted!'
+ )
+ );
+ }
+
+ /**
+ * The ini_set and ini_get functions can be disabled using
+ * disable_functions but we're relying quite a lot of them.
+ */
+ if (! function_exists('ini_get') || ! function_exists('ini_set')) {
+ self::fatalError(
+ __(
+ 'The ini_get and/or ini_set functions are disabled in php.ini. '
+ . 'phpMyAdmin requires these functions!'
+ )
+ );
+ }
+ }
+
+ /**
+ * Checks request and fails with fatal error if something problematic is found
+ *
+ * @return void
+ */
+ public static function checkRequest(): void
+ {
+ if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
+ self::fatalError(__("GLOBALS overwrite attempt"));
+ }
+
+ /**
+ * protect against possible exploits - there is no need to have so much variables
+ */
+ if (count($_REQUEST) > 1000) {
+ self::fatalError(__('possible exploit'));
+ }
+ }
+
+ /**
+ * Sign the sql query using hmac using the session token
+ *
+ * @param string $sqlQuery The sql query
+ * @return string
+ */
+ public static function signSqlQuery($sqlQuery)
+ {
+ /** @var array $cfg */
+ global $cfg;
+ return hash_hmac('sha256', $sqlQuery, $_SESSION[' HMAC_secret '] . $cfg['blowfish_secret']);
+ }
+
+ /**
+ * Check that the sql query has a valid hmac signature
+ *
+ * @param string $sqlQuery The sql query
+ * @param string $signature The Signature to check
+ * @return bool
+ */
+ public static function checkSqlQuerySignature($sqlQuery, $signature)
+ {
+ /** @var array $cfg */
+ global $cfg;
+ $hmac = hash_hmac('sha256', $sqlQuery, $_SESSION[' HMAC_secret '] . $cfg['blowfish_secret']);
+ return hash_equals($hmac, $signature);
+ }
+}