Translated using Weblate (Portuguese (Brazil))
[phpmyadmin.git] / libraries / core.lib.php
blob17e9ff41c6a5f4ded7acad577a4ca54557762c6f
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Core functions used all over the scripts.
5 * This script is distinct from libraries/common.inc.php because this
6 * script is called from /test.
8 * @package PhpMyAdmin
9 */
10 use PMA\libraries\Message;
11 use PMA\libraries\Response;
12 use PMA\libraries\URL;
13 use PMA\libraries\Sanitize;
16 if (! defined('PHPMYADMIN')) {
17 exit;
20 /**
21 * String handling (security)
23 require_once 'libraries/string.lib.php';
25 /**
26 * checks given $var and returns it if valid, or $default of not valid
27 * given $var is also checked for type being 'similar' as $default
28 * or against any other type if $type is provided
30 * <code>
31 * // $_REQUEST['db'] not set
32 * echo PMA_ifSetOr($_REQUEST['db'], ''); // ''
33 * // $_REQUEST['sql_query'] not set
34 * echo PMA_ifSetOr($_REQUEST['sql_query']); // null
35 * // $cfg['EnableFoo'] not set
36 * echo PMA_ifSetOr($cfg['EnableFoo'], false, 'boolean'); // false
37 * echo PMA_ifSetOr($cfg['EnableFoo']); // null
38 * // $cfg['EnableFoo'] set to 1
39 * echo PMA_ifSetOr($cfg['EnableFoo'], false, 'boolean'); // false
40 * echo PMA_ifSetOr($cfg['EnableFoo'], false, 'similar'); // 1
41 * echo PMA_ifSetOr($cfg['EnableFoo'], false); // 1
42 * // $cfg['EnableFoo'] set to true
43 * echo PMA_ifSetOr($cfg['EnableFoo'], false, 'boolean'); // true
44 * </code>
46 * @param mixed &$var param to check
47 * @param mixed $default default value
48 * @param mixed $type var type or array of values to check against $var
50 * @return mixed $var or $default
52 * @see PMA_isValid()
54 function PMA_ifSetOr(&$var, $default = null, $type = 'similar')
56 if (! PMA_isValid($var, $type, $default)) {
57 return $default;
60 return $var;
63 /**
64 * checks given $var against $type or $compare
66 * $type can be:
67 * - false : no type checking
68 * - 'scalar' : whether type of $var is integer, float, string or boolean
69 * - 'numeric' : whether type of $var is any number representation
70 * - 'length' : whether type of $var is scalar with a string length > 0
71 * - 'similar' : whether type of $var is similar to type of $compare
72 * - 'equal' : whether type of $var is identical to type of $compare
73 * - 'identical' : whether $var is identical to $compare, not only the type!
74 * - or any other valid PHP variable type
76 * <code>
77 * // $_REQUEST['doit'] = true;
78 * PMA_isValid($_REQUEST['doit'], 'identical', 'true'); // false
79 * // $_REQUEST['doit'] = 'true';
80 * PMA_isValid($_REQUEST['doit'], 'identical', 'true'); // true
81 * </code>
83 * NOTE: call-by-reference is used to not get NOTICE on undefined vars,
84 * but the var is not altered inside this function, also after checking a var
85 * this var exists nut is not set, example:
86 * <code>
87 * // $var is not set
88 * isset($var); // false
89 * functionCallByReference($var); // false
90 * isset($var); // true
91 * functionCallByReference($var); // true
92 * </code>
94 * to avoid this we set this var to null if not isset
96 * @param mixed &$var variable to check
97 * @param mixed $type var type or array of valid values to check against $var
98 * @param mixed $compare var to compare with $var
100 * @return boolean whether valid or not
102 * @todo add some more var types like hex, bin, ...?
103 * @see https://secure.php.net/gettype
105 function PMA_isValid(&$var, $type = 'length', $compare = null)
107 if (! isset($var)) {
108 // var is not even set
109 return false;
112 if ($type === false) {
113 // no vartype requested
114 return true;
117 if (is_array($type)) {
118 return in_array($var, $type);
121 // allow some aliases of var types
122 $type = strtolower($type);
123 switch ($type) {
124 case 'identic' :
125 $type = 'identical';
126 break;
127 case 'len' :
128 $type = 'length';
129 break;
130 case 'bool' :
131 $type = 'boolean';
132 break;
133 case 'float' :
134 $type = 'double';
135 break;
136 case 'int' :
137 $type = 'integer';
138 break;
139 case 'null' :
140 $type = 'NULL';
141 break;
144 if ($type === 'identical') {
145 return $var === $compare;
148 // whether we should check against given $compare
149 if ($type === 'similar') {
150 switch (gettype($compare)) {
151 case 'string':
152 case 'boolean':
153 $type = 'scalar';
154 break;
155 case 'integer':
156 case 'double':
157 $type = 'numeric';
158 break;
159 default:
160 $type = gettype($compare);
162 } elseif ($type === 'equal') {
163 $type = gettype($compare);
166 // do the check
167 if ($type === 'length' || $type === 'scalar') {
168 $is_scalar = is_scalar($var);
169 if ($is_scalar && $type === 'length') {
170 return strlen($var) > 0;
172 return $is_scalar;
175 if ($type === 'numeric') {
176 return is_numeric($var);
179 if (gettype($var) === $type) {
180 return true;
183 return false;
187 * Removes insecure parts in a path; used before include() or
188 * require() when a part of the path comes from an insecure source
189 * like a cookie or form.
191 * @param string $path The path to check
193 * @return string The secured path
195 * @access public
197 function PMA_securePath($path)
199 // change .. to .
200 $path = preg_replace('@\.\.*@', '.', $path);
202 return $path;
203 } // end function
206 * displays the given error message on phpMyAdmin error page in foreign language,
207 * ends script execution and closes session
209 * loads language file if not loaded already
211 * @param string $error_message the error message or named error message
212 * @param string|array $message_args arguments applied to $error_message
214 * @return void
216 function PMA_fatalError($error_message, $message_args = null) {
217 /* Use format string if applicable */
218 if (is_string($message_args)) {
219 $error_message = sprintf($error_message, $message_args);
220 } elseif (is_array($message_args)) {
221 $error_message = vsprintf($error_message, $message_args);
225 * Avoid using Response class as config does not have to be loaded yet
226 * (this can happen on early fatal error)
228 if (! empty($_REQUEST['ajax_request'])) {
229 // Generate JSON manually
230 PMA_headerJSON();
231 echo json_encode(
232 array(
233 'success' => false,
234 'error' => Message::error($error_message)->getDisplay(),
237 } else {
238 $error_message = strtr($error_message, array('<br />' => '[br]'));
240 // these variables are used in the included file libraries/error.inc.php
241 //first check if php-mbstring is available
242 if (function_exists('mb_detect_encoding')) {
243 //If present use gettext
244 $error_header = __('Error');
245 } else {
246 $error_header = 'Error';
248 $lang = isset($GLOBALS['lang']) ? $GLOBALS['lang'] : 'en';
249 $dir = isset($GLOBALS['text_dir']) ? $GLOBALS['text_dir'] : 'ltr';
251 // Displays the error message
252 include './libraries/error.inc.php';
254 if (! defined('TESTSUITE')) {
255 exit;
260 * Returns a link to the PHP documentation
262 * @param string $target anchor in documentation
264 * @return string the URL
266 * @access public
268 function PMA_getPHPDocLink($target)
270 /* List of PHP documentation translations */
271 $php_doc_languages = array(
272 'pt_BR', 'zh', 'fr', 'de', 'it', 'ja', 'pl', 'ro', 'ru', 'fa', 'es', 'tr'
275 $lang = 'en';
276 if (in_array($GLOBALS['lang'], $php_doc_languages)) {
277 $lang = $GLOBALS['lang'];
280 return PMA_linkURL('https://secure.php.net/manual/' . $lang . '/' . $target);
284 * Warn or fail on missing extension.
286 * @param string $extension Extension name
287 * @param bool $fatal Whether the error is fatal.
288 * @param string $extra Extra string to append to message.
290 * @return void
292 function PMA_warnMissingExtension($extension, $fatal = false, $extra = '')
294 /* Gettext does not have to be loaded yet here */
295 if (function_exists('__')) {
296 $message = __(
297 'The %s extension is missing. Please check your PHP configuration.'
299 } else {
300 $message
301 = 'The %s extension is missing. Please check your PHP configuration.';
303 $doclink = PMA_getPHPDocLink('book.' . $extension . '.php');
304 $message = sprintf(
305 $message,
306 '[a@' . $doclink . '@Documentation][em]' . $extension . '[/em][/a]'
308 if ($extra != '') {
309 $message .= ' ' . $extra;
311 if ($fatal) {
312 PMA_fatalError($message);
313 return;
316 $GLOBALS['error_handler']->addError(
317 $message,
318 E_USER_WARNING,
321 false
326 * returns count of tables in given db
328 * @param string $db database to count tables for
330 * @return integer count of tables in $db
332 function PMA_getTableCount($db)
334 $tables = $GLOBALS['dbi']->tryQuery(
335 'SHOW TABLES FROM ' . PMA\libraries\Util::backquote($db) . ';',
336 null, PMA\libraries\DatabaseInterface::QUERY_STORE
338 if ($tables) {
339 $num_tables = $GLOBALS['dbi']->numRows($tables);
340 $GLOBALS['dbi']->freeResult($tables);
341 } else {
342 $num_tables = 0;
345 return $num_tables;
349 * Converts numbers like 10M into bytes
350 * Used with permission from Moodle (https://moodle.org) by Martin Dougiamas
351 * (renamed with PMA prefix to avoid double definition when embedded
352 * in Moodle)
354 * @param string|int $size size (Default = 0)
356 * @return integer $size
358 function PMA_getRealSize($size = 0)
360 if (! $size) {
361 return 0;
364 $binaryprefixes = array(
365 'T' => 1099511627776,
366 't' => 1099511627776,
367 'G' => 1073741824,
368 'g' => 1073741824,
369 'M' => 1048576,
370 'm' => 1048576,
371 'K' => 1024,
372 'k' => 1024,
375 if (preg_match('/^([0-9]+)([KMGT])/i', $size, $matches)) {
376 return $matches[1] * $binaryprefixes[$matches[2]];
379 return (int) $size;
380 } // end function PMA_getRealSize()
383 * boolean phpMyAdmin.PMA_checkPageValidity(string &$page, array $whitelist)
385 * checks given $page against given $whitelist and returns true if valid
386 * it optionally ignores query parameters in $page (script.php?ignored)
388 * @param string &$page page to check
389 * @param array $whitelist whitelist to check page against
391 * @return boolean whether $page is valid or not (in $whitelist or not)
393 function PMA_checkPageValidity(&$page, $whitelist)
395 if (! isset($page) || !is_string($page)) {
396 return false;
399 if (in_array($page, $whitelist)) {
400 return true;
403 $_page = mb_substr(
404 $page,
406 mb_strpos($page . '?', '?')
408 if (in_array($_page, $whitelist)) {
409 return true;
412 $_page = urldecode($page);
413 $_page = mb_substr(
414 $_page,
416 mb_strpos($_page . '?', '?')
418 if (in_array($_page, $whitelist)) {
419 return true;
422 return false;
426 * tries to find the value for the given environment variable name
428 * searches in $_SERVER, $_ENV then tries getenv() and apache_getenv()
429 * in this order
431 * @param string $var_name variable name
433 * @return string value of $var or empty string
435 function PMA_getenv($var_name)
437 if (isset($_SERVER[$var_name])) {
438 return $_SERVER[$var_name];
441 if (isset($_ENV[$var_name])) {
442 return $_ENV[$var_name];
445 if (getenv($var_name)) {
446 return getenv($var_name);
449 if (function_exists('apache_getenv')
450 && apache_getenv($var_name, true)
452 return apache_getenv($var_name, true);
455 return '';
459 * Send HTTP header, taking IIS limits into account (600 seems ok)
461 * @param string $uri the header to send
462 * @param bool $use_refresh whether to use Refresh: header when running on IIS
464 * @return void
466 function PMA_sendHeaderLocation($uri, $use_refresh = false)
468 if ($GLOBALS['PMA_Config']->get('PMA_IS_IIS') && mb_strlen($uri) > 600) {
469 Response::getInstance()->disable();
471 echo PMA\libraries\Template::get('header_location')
472 ->render(array('uri' => $uri));
474 return;
478 * Avoid relative path redirect problems in case user entered URL
479 * like /phpmyadmin/index.php/ which some web servers happily accept.
481 if ($uri[0] == '.') {
482 $uri = $GLOBALS['PMA_Config']->getRootPath() . substr($uri, 2);
485 $response = Response::getInstance();
487 session_write_close();
488 if ($response->headersSent()) {
489 trigger_error(
490 'PMA_sendHeaderLocation called when headers are already sent!',
491 E_USER_ERROR
494 // bug #1523784: IE6 does not like 'Refresh: 0', it
495 // results in a blank page
496 // but we need it when coming from the cookie login panel)
497 if ($GLOBALS['PMA_Config']->get('PMA_IS_IIS') && $use_refresh) {
498 $response->header('Refresh: 0; ' . $uri);
499 } else {
500 $response->header('Location: ' . $uri);
505 * Outputs application/json headers. This includes no caching.
507 * @return void
509 function PMA_headerJSON()
511 if (defined('TESTSUITE')) {
512 return;
514 // No caching
515 PMA_noCacheHeader();
516 // MIME type
517 header('Content-Type: application/json; charset=UTF-8');
518 // Disable content sniffing in browser
519 // This is needed in case we include HTML in JSON, browser might assume it's
520 // html to display
521 header('X-Content-Type-Options: nosniff');
525 * Outputs headers to prevent caching in browser (and on the way).
527 * @return void
529 function PMA_noCacheHeader()
531 if (defined('TESTSUITE')) {
532 return;
534 // rfc2616 - Section 14.21
535 header('Expires: ' . gmdate(DATE_RFC1123));
536 // HTTP/1.1
537 header(
538 'Cache-Control: no-store, no-cache, must-revalidate,'
539 . ' pre-check=0, post-check=0, max-age=0'
542 header('Pragma: no-cache'); // HTTP/1.0
543 // test case: exporting a database into a .gz file with Safari
544 // would produce files not having the current time
545 // (added this header for Safari but should not harm other browsers)
546 header('Last-Modified: ' . gmdate(DATE_RFC1123));
551 * Sends header indicating file download.
553 * @param string $filename Filename to include in headers if empty,
554 * none Content-Disposition header will be sent.
555 * @param string $mimetype MIME type to include in headers.
556 * @param int $length Length of content (optional)
557 * @param bool $no_cache Whether to include no-caching headers.
559 * @return void
561 function PMA_downloadHeader($filename, $mimetype, $length = 0, $no_cache = true)
563 if ($no_cache) {
564 PMA_noCacheHeader();
566 /* Replace all possibly dangerous chars in filename */
567 $filename = Sanitize::sanitizeFilename($filename);
568 if (!empty($filename)) {
569 header('Content-Description: File Transfer');
570 header('Content-Disposition: attachment; filename="' . $filename . '"');
572 header('Content-Type: ' . $mimetype);
573 // inform the server that compression has been done,
574 // to avoid a double compression (for example with Apache + mod_deflate)
575 $notChromeOrLessThan43 = PMA_USR_BROWSER_AGENT != 'CHROME' // see bug #4942
576 || (PMA_USR_BROWSER_AGENT == 'CHROME' && PMA_USR_BROWSER_VER < 43);
577 if (strpos($mimetype, 'gzip') !== false && $notChromeOrLessThan43) {
578 header('Content-Encoding: gzip');
580 header('Content-Transfer-Encoding: binary');
581 if ($length > 0) {
582 header('Content-Length: ' . $length);
587 * Returns value of an element in $array given by $path.
588 * $path is a string describing position of an element in an associative array,
589 * eg. Servers/1/host refers to $array[Servers][1][host]
591 * @param string $path path in the array
592 * @param array $array the array
593 * @param mixed $default default value
595 * @return mixed array element or $default
597 function PMA_arrayRead($path, $array, $default = null)
599 $keys = explode('/', $path);
600 $value =& $array;
601 foreach ($keys as $key) {
602 if (! isset($value[$key])) {
603 return $default;
605 $value =& $value[$key];
607 return $value;
611 * Stores value in an array
613 * @param string $path path in the array
614 * @param array &$array the array
615 * @param mixed $value value to store
617 * @return void
619 function PMA_arrayWrite($path, &$array, $value)
621 $keys = explode('/', $path);
622 $last_key = array_pop($keys);
623 $a =& $array;
624 foreach ($keys as $key) {
625 if (! isset($a[$key])) {
626 $a[$key] = array();
628 $a =& $a[$key];
630 $a[$last_key] = $value;
634 * Removes value from an array
636 * @param string $path path in the array
637 * @param array &$array the array
639 * @return void
641 function PMA_arrayRemove($path, &$array)
643 $keys = explode('/', $path);
644 $keys_last = array_pop($keys);
645 $path = array();
646 $depth = 0;
648 $path[0] =& $array;
649 $found = true;
650 // go as deep as required or possible
651 foreach ($keys as $key) {
652 if (! isset($path[$depth][$key])) {
653 $found = false;
654 break;
656 $depth++;
657 $path[$depth] =& $path[$depth - 1][$key];
659 // if element found, remove it
660 if ($found) {
661 unset($path[$depth][$keys_last]);
662 $depth--;
665 // remove empty nested arrays
666 for (; $depth >= 0; $depth--) {
667 if (! isset($path[$depth+1]) || count($path[$depth+1]) == 0) {
668 unset($path[$depth][$keys[$depth]]);
669 } else {
670 break;
676 * Returns link to (possibly) external site using defined redirector.
678 * @param string $url URL where to go.
680 * @return string URL for a link.
682 function PMA_linkURL($url)
684 if (!preg_match('#^https?://#', $url)) {
685 return $url;
688 $params = array();
689 $params['url'] = $url;
691 $url = URL::getCommon($params);
692 //strip off token and such sensitive information. Just keep url.
693 $arr = parse_url($url);
694 parse_str($arr["query"], $vars);
695 $query = http_build_query(array("url" => $vars["url"]));
697 if (defined('PMA_SETUP')) {
698 $url = '../url.php?' . $query;
699 } else {
700 $url = './url.php?' . $query;
703 return $url;
707 * Checks whether domain of URL is whitelisted domain or not.
708 * Use only for URLs of external sites.
710 * @param string $url URL of external site.
712 * @return boolean True: if domain of $url is allowed domain,
713 * False: otherwise.
715 function PMA_isAllowedDomain($url)
717 $arr = parse_url($url);
718 // We need host to be set
719 if (! isset($arr['host']) || strlen($arr['host']) == 0) {
720 return false;
722 // We do not want these to be present
723 $blocked = array('user', 'pass', 'port');
724 foreach ($blocked as $part) {
725 if (isset($arr[$part]) && strlen($arr[$part]) != 0) {
726 return false;
729 $domain = $arr["host"];
730 $domainWhiteList = array(
731 /* Include current domain */
732 $_SERVER['SERVER_NAME'],
733 /* phpMyAdmin domains */
734 'wiki.phpmyadmin.net',
735 'www.phpmyadmin.net',
736 'phpmyadmin.net',
737 'demo.phpmyadmin.net',
738 'docs.phpmyadmin.net',
739 /* mysql.com domains */
740 'dev.mysql.com','bugs.mysql.com',
741 /* mariadb domains */
742 'mariadb.org', 'mariadb.com',
743 /* php.net domains */
744 'php.net',
745 'secure.php.net',
746 /* sourceforge.net domain */
747 'sourceforge.net',
748 /* Github domains*/
749 'github.com','www.github.com',
750 /* Percona domains */
751 'www.percona.com',
752 /* Following are doubtful ones. */
753 'mysqldatabaseadministration.blogspot.com',
755 if (in_array($domain, $domainWhiteList)) {
756 return true;
759 return false;
763 * Replace some html-unfriendly stuff
765 * @param string $buffer String to process
767 * @return string Escaped and cleaned up text suitable for html
769 function PMA_mimeDefaultFunction($buffer)
771 $buffer = htmlspecialchars($buffer);
772 $buffer = str_replace(' ', ' &nbsp;', $buffer);
773 $buffer = preg_replace("@((\015\012)|(\015)|(\012))@", '<br />' . "\n", $buffer);
775 return $buffer;
779 * Displays SQL query before executing.
781 * @param array|string $query_data Array containing queries or query itself
783 * @return void
785 function PMA_previewSQL($query_data)
787 $retval = '<div class="preview_sql">';
788 if (empty($query_data)) {
789 $retval .= __('No change');
790 } elseif (is_array($query_data)) {
791 foreach ($query_data as $query) {
792 $retval .= PMA\libraries\Util::formatSql($query);
794 } else {
795 $retval .= PMA\libraries\Util::formatSql($query_data);
797 $retval .= '</div>';
798 $response = Response::getInstance();
799 $response->addJSON('sql_data', $retval);
800 exit;
804 * recursively check if variable is empty
806 * @param mixed $value the variable
808 * @return bool true if empty
810 function PMA_emptyRecursive($value)
812 $empty = true;
813 if (is_array($value)) {
814 array_walk_recursive(
815 $value,
816 function ($item) use (&$empty) {
817 $empty = $empty && empty($item);
820 } else {
821 $empty = empty($value);
823 return $empty;
827 * Creates some globals from $_POST variables matching a pattern
829 * @param array $post_patterns The patterns to search for
831 * @return void
833 function PMA_setPostAsGlobal($post_patterns)
835 foreach (array_keys($_POST) as $post_key) {
836 foreach ($post_patterns as $one_post_pattern) {
837 if (preg_match($one_post_pattern, $post_key)) {
838 $GLOBALS[$post_key] = $_POST[$post_key];
845 * Creates some globals from $_REQUEST
847 * @param string $param db|table
849 * @return void
851 function PMA_setGlobalDbOrTable($param)
853 $GLOBALS[$param] = '';
854 if (PMA_isValid($_REQUEST[$param])) {
855 // can we strip tags from this?
856 // only \ and / is not allowed in db names for MySQL
857 $GLOBALS[$param] = $_REQUEST[$param];
858 $GLOBALS['url_params'][$param] = $GLOBALS[$param];
863 * PATH_INFO could be compromised if set, so remove it from PHP_SELF
864 * and provide a clean PHP_SELF here
866 * @return void
868 function PMA_cleanupPathInfo()
870 global $PMA_PHP_SELF;
872 $PMA_PHP_SELF = PMA_getenv('PHP_SELF');
873 if (empty($PMA_PHP_SELF)) {
874 $PMA_PHP_SELF = urldecode(PMA_getenv('REQUEST_URI'));
876 $_PATH_INFO = PMA_getenv('PATH_INFO');
877 if (! empty($_PATH_INFO) && ! empty($PMA_PHP_SELF)) {
878 $question_pos = mb_strpos($PMA_PHP_SELF, '?');
879 if ($question_pos != false) {
880 $PMA_PHP_SELF = mb_substr($PMA_PHP_SELF, 0, $question_pos);
882 $path_info_pos = mb_strrpos($PMA_PHP_SELF, $_PATH_INFO);
883 if ($path_info_pos !== false) {
884 $path_info_part = mb_substr($PMA_PHP_SELF, $path_info_pos, mb_strlen($_PATH_INFO));
885 if ($path_info_part == $_PATH_INFO) {
886 $PMA_PHP_SELF = mb_substr($PMA_PHP_SELF, 0, $path_info_pos);
891 $path = [];
892 foreach(explode('/', $PMA_PHP_SELF) as $part) {
893 // ignore parts that have no value
894 if (empty($part) || $part === '.') continue;
896 if ($part !== '..') {
897 // cool, we found a new part
898 array_push($path, $part);
899 } else if (count($path) > 0) {
900 // going back up? sure
901 array_pop($path);
903 // Here we intentionall ignore case where we go too up
904 // as there is nothing sane to do
907 $PMA_PHP_SELF = htmlspecialchars('/' . join('/', $path));
911 * Checks that required PHP extensions are there.
912 * @return void
914 function PMA_checkExtensions()
917 * Warning about mbstring.
919 if (! function_exists('mb_detect_encoding')) {
920 PMA_warnMissingExtension('mbstring', true);
924 * We really need this one!
926 if (! function_exists('preg_replace')) {
927 PMA_warnMissingExtension('pcre', true);
931 * JSON is required in several places.
933 if (! function_exists('json_encode')) {
934 PMA_warnMissingExtension('json', true);
939 * Gets the "true" IP address of the current user
941 * @return string the ip of the user
943 * @access private
945 function PMA_getIp()
947 /* Get the address of user */
948 if (empty($_SERVER['REMOTE_ADDR'])) {
949 /* We do not know remote IP */
950 return false;
953 $direct_ip = $_SERVER['REMOTE_ADDR'];
955 /* Do we trust this IP as a proxy? If yes we will use it's header. */
956 if (!isset($GLOBALS['cfg']['TrustedProxies'][$direct_ip])) {
957 /* Return true IP */
958 return $direct_ip;
962 * Parse header in form:
963 * X-Forwarded-For: client, proxy1, proxy2
965 // Get header content
966 $value = PMA_getenv($GLOBALS['cfg']['TrustedProxies'][$direct_ip]);
967 // Grab first element what is client adddress
968 $value = explode(',', $value)[0];
969 // checks that the header contains only one IP address,
970 $is_ip = filter_var($value, FILTER_VALIDATE_IP);
972 if ($is_ip !== false) {
973 // True IP behind a proxy
974 return $value;
977 // We could not parse header
978 return false;
979 } // end of the 'PMA_getIp()' function
982 /* Compatibility with PHP < 5.6 */
983 if(! function_exists('hash_equals')) {
986 * Timing attack safe string comparison
988 * @param string $a first string
989 * @param string $b second string
991 * @return boolean whether they are equal
993 function hash_equals($a, $b) {
994 $ret = strlen($a) ^ strlen($b);
995 $ret |= array_sum(unpack("C*", $a ^ $b));
996 return ! $ret;
999 /* Compatibility with PHP < 5.1 or PHP without hash extension */
1000 if (! function_exists('hash_hmac')) {
1001 function hash_hmac($algo, $data, $key, $raw_output = false)
1003 $algo = strtolower($algo);
1004 $pack = 'H'.strlen($algo('test'));
1005 $size = 64;
1006 $opad = str_repeat(chr(0x5C), $size);
1007 $ipad = str_repeat(chr(0x36), $size);
1009 if (strlen($key) > $size) {
1010 $key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
1011 } else {
1012 $key = str_pad($key, $size, chr(0x00));
1015 for ($i = 0; $i < strlen($key) - 1; $i++) {
1016 $opad[$i] = $opad[$i] ^ $key[$i];
1017 $ipad[$i] = $ipad[$i] ^ $key[$i];
1020 $output = $algo($opad.pack($pack, $algo($ipad.$data)));
1022 return ($raw_output) ? pack($pack, $output) : $output;
1027 * Sanitizes MySQL hostname
1029 * * strips p: prefix(es)
1031 * @param string $name User given hostname
1033 * @return string
1035 function PMA_sanitizeMySQLHost($name)
1037 while (strtolower(substr($name, 0, 2)) == 'p:') {
1038 $name = substr($name, 2);
1041 return $name;
1045 * Sanitizes MySQL username
1047 * * strips part behind null byte
1049 * @param string $name User given username
1051 * @return string
1053 function PMA_sanitizeMySQLUser($name)
1055 $position = strpos($name, chr(0));
1056 if ($position !== false) {
1057 return substr($name, 0, $position);
1059 return $name;
1063 * Safe unserializer wrapper
1065 * It does not unserialize data containing objects
1067 * @param string $data Data to unserialize
1069 * @return mixed
1071 function PMA_safeUnserialize($data)
1073 if (! is_string($data)) {
1074 return null;
1077 /* validate serialized data */
1078 $length = strlen($data);
1079 $depth = 0;
1080 for ($i = 0; $i < $length; $i++) {
1081 $value = $data[$i];
1083 switch ($value)
1085 case '}':
1086 /* end of array */
1087 if ($depth <= 0) {
1088 return null;
1090 $depth--;
1091 break;
1092 case 's':
1093 /* string */
1094 // parse sting length
1095 $strlen = intval(substr($data, $i + 2));
1096 // string start
1097 $i = strpos($data, ':', $i + 2);
1098 if ($i === false) {
1099 return null;
1101 // skip string, quotes and ;
1102 $i += 2 + $strlen + 1;
1103 if ($data[$i] != ';') {
1104 return null;
1106 break;
1108 case 'b':
1109 case 'i':
1110 case 'd':
1111 /* bool, integer or double */
1112 // skip value to sepearator
1113 $i = strpos($data, ';', $i);
1114 if ($i === false) {
1115 return null;
1117 break;
1118 case 'a':
1119 /* array */
1120 // find array start
1121 $i = strpos($data, '{', $i);
1122 if ($i === false) {
1123 return null;
1125 // remember nesting
1126 $depth++;
1127 break;
1128 case 'N':
1129 /* null */
1130 // skip to end
1131 $i = strpos($data, ';', $i);
1132 if ($i === false) {
1133 return null;
1135 break;
1136 default:
1137 /* any other elements are not wanted */
1138 return null;
1142 // check unterminated arrays
1143 if ($depth > 0) {
1144 return null;
1147 return unserialize($data);