Url.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Static methods for URL/hidden inputs generating
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\Crypto\Crypto;
  10. /**
  11. * Static methods for URL/hidden inputs generating
  12. *
  13. * @package PhpMyAdmin
  14. */
  15. class Url
  16. {
  17. /**
  18. * Generates text with hidden inputs.
  19. *
  20. * @param string|array $db optional database name
  21. * (can also be an array of parameters)
  22. * @param string $table optional table name
  23. * @param int $indent indenting level
  24. * @param string|array $skip do not generate a hidden field for this parameter
  25. * (can be an array of strings)
  26. *
  27. * @see Url::getCommon()
  28. *
  29. * @return string string with input fields
  30. *
  31. * @access public
  32. */
  33. public static function getHiddenInputs($db = '', $table = '',
  34. $indent = 0, $skip = array()
  35. ) {
  36. /** @var Config $PMA_Config */
  37. global $PMA_Config;
  38. if (is_array($db)) {
  39. $params =& $db;
  40. $_indent = empty($table) ? $indent : $table;
  41. $_skip = empty($indent) ? $skip : $indent;
  42. $indent =& $_indent;
  43. $skip =& $_skip;
  44. } else {
  45. $params = array();
  46. if (strlen($db) > 0) {
  47. $params['db'] = $db;
  48. }
  49. if (strlen($table) > 0) {
  50. $params['table'] = $table;
  51. }
  52. }
  53. if (! empty($GLOBALS['server'])
  54. && $GLOBALS['server'] != $GLOBALS['cfg']['ServerDefault']
  55. ) {
  56. $params['server'] = $GLOBALS['server'];
  57. }
  58. if (empty($PMA_Config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
  59. $params['lang'] = $GLOBALS['lang'];
  60. }
  61. if (! is_array($skip)) {
  62. if (isset($params[$skip])) {
  63. unset($params[$skip]);
  64. }
  65. } else {
  66. foreach ($skip as $skipping) {
  67. if (isset($params[$skipping])) {
  68. unset($params[$skipping]);
  69. }
  70. }
  71. }
  72. return Url::getHiddenFields($params);
  73. }
  74. /**
  75. * create hidden form fields from array with name => value
  76. *
  77. * <code>
  78. * $values = array(
  79. * 'aaa' => aaa,
  80. * 'bbb' => array(
  81. * 'bbb_0',
  82. * 'bbb_1',
  83. * ),
  84. * 'ccc' => array(
  85. * 'a' => 'ccc_a',
  86. * 'b' => 'ccc_b',
  87. * ),
  88. * );
  89. * echo Url::getHiddenFields($values);
  90. *
  91. * // produces:
  92. * <input type="hidden" name="aaa" Value="aaa" />
  93. * <input type="hidden" name="bbb[0]" Value="bbb_0" />
  94. * <input type="hidden" name="bbb[1]" Value="bbb_1" />
  95. * <input type="hidden" name="ccc[a]" Value="ccc_a" />
  96. * <input type="hidden" name="ccc[b]" Value="ccc_b" />
  97. * </code>
  98. *
  99. * @param array $values hidden values
  100. * @param string $pre prefix
  101. *
  102. * @return string form fields of type hidden
  103. */
  104. public static function getHiddenFields(array $values, $pre = '')
  105. {
  106. $fields = '';
  107. /* Always include token in plain forms */
  108. if ($pre === '') {
  109. $values['token'] = $_SESSION[' PMA_token '];
  110. }
  111. foreach ($values as $name => $value) {
  112. if (! empty($pre)) {
  113. $name = $pre . '[' . $name . ']';
  114. }
  115. if (is_array($value)) {
  116. $fields .= Url::getHiddenFields($value, $name);
  117. } else {
  118. // do not generate an ending "\n" because
  119. // Url::getHiddenInputs() is sometimes called
  120. // from a JS document.write()
  121. $fields .= '<input type="hidden" name="' . htmlspecialchars($name)
  122. . '" value="' . htmlspecialchars($value) . '" />';
  123. }
  124. }
  125. return $fields;
  126. }
  127. /**
  128. * Generates text with URL parameters.
  129. *
  130. * <code>
  131. * $params['myparam'] = 'myvalue';
  132. * $params['db'] = 'mysql';
  133. * $params['table'] = 'rights';
  134. * // note the missing ?
  135. * echo 'script.php' . Url::getCommon($params);
  136. * // produces with cookies enabled:
  137. * // script.php?myparam=myvalue&amp;db=mysql&amp;table=rights
  138. * // with cookies disabled:
  139. * // script.php?server=1&amp;lang=en&amp;myparam=myvalue&amp;db=mysql
  140. * // &amp;table=rights
  141. *
  142. * // note the missing ?
  143. * echo 'script.php' . Url::getCommon();
  144. * // produces with cookies enabled:
  145. * // script.php
  146. * // with cookies disabled:
  147. * // script.php?server=1&amp;lang=en
  148. * </code>
  149. *
  150. * @param mixed $params optional, Contains an associative array with url params
  151. * @param string $divider optional character to use instead of '?'
  152. * @param bool $encrypt whether to encrypt URL params
  153. *
  154. * @return string string with URL parameters
  155. * @access public
  156. */
  157. public static function getCommon($params = array(), $divider = '?', $encrypt = true)
  158. {
  159. return htmlspecialchars(
  160. Url::getCommonRaw($params, $divider, $encrypt)
  161. );
  162. }
  163. /**
  164. * Generates text with URL parameters.
  165. *
  166. * <code>
  167. * $params['myparam'] = 'myvalue';
  168. * $params['db'] = 'mysql';
  169. * $params['table'] = 'rights';
  170. * // note the missing ?
  171. * echo 'script.php' . Url::getCommon($params);
  172. * // produces with cookies enabled:
  173. * // script.php?myparam=myvalue&amp;db=mysql&amp;table=rights
  174. * // with cookies disabled:
  175. * // script.php?server=1&amp;lang=en&amp;myparam=myvalue&amp;db=mysql
  176. * // &amp;table=rights
  177. *
  178. * // note the missing ?
  179. * echo 'script.php' . Url::getCommon();
  180. * // produces with cookies enabled:
  181. * // script.php
  182. * // with cookies disabled:
  183. * // script.php?server=1&amp;lang=en
  184. * </code>
  185. *
  186. * @param mixed $params optional, Contains an associative array with url params
  187. * @param string $divider optional character to use instead of '?'
  188. * @param bool $encrypt whether to encrypt URL params
  189. *
  190. * @return string string with URL parameters
  191. * @access public
  192. */
  193. public static function getCommonRaw($params = array(), $divider = '?', $encrypt = true)
  194. {
  195. /** @var Config $PMA_Config */
  196. global $PMA_Config;
  197. // avoid overwriting when creating navi panel links to servers
  198. if (isset($GLOBALS['server'])
  199. && $GLOBALS['server'] != $GLOBALS['cfg']['ServerDefault']
  200. && ! isset($params['server'])
  201. && ! $PMA_Config->get('is_setup')
  202. ) {
  203. $params['server'] = $GLOBALS['server'];
  204. }
  205. if (empty($PMA_Config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
  206. $params['lang'] = $GLOBALS['lang'];
  207. }
  208. $query = self::buildHttpQuery($params, $encrypt);
  209. if ($divider != '?' || strlen($query) > 0) {
  210. return $divider . $query;
  211. }
  212. return '';
  213. }
  214. /**
  215. * @param array<string, mixed> $params
  216. * @param bool $encrypt whether to encrypt URL params
  217. *
  218. * @return string
  219. */
  220. public static function buildHttpQuery($params, $encrypt = true)
  221. {
  222. global $PMA_Config;
  223. $separator = self::getArgSeparator();
  224. if (! $encrypt || ! $PMA_Config->get('URLQueryEncryption')) {
  225. return http_build_query($params, null, $separator);
  226. }
  227. $data = $params;
  228. $keys = [
  229. 'db',
  230. 'table',
  231. 'field',
  232. 'sql_query',
  233. 'sql_signature',
  234. 'where_clause',
  235. 'goto',
  236. 'back',
  237. 'message_to_show',
  238. 'username',
  239. 'hostname',
  240. 'dbname',
  241. 'tablename',
  242. 'checkprivsdb',
  243. 'checkprivstable',
  244. ];
  245. $paramsToEncrypt = [];
  246. foreach ($params as $paramKey => $paramValue) {
  247. if (! in_array($paramKey, $keys)) {
  248. continue;
  249. }
  250. $paramsToEncrypt[$paramKey] = $paramValue;
  251. unset($data[$paramKey]);
  252. }
  253. if ($paramsToEncrypt !== []) {
  254. $data['eq'] = self::encryptQuery(json_encode($paramsToEncrypt));
  255. }
  256. return http_build_query($data, null, $separator);
  257. }
  258. /**
  259. * @param string $query
  260. *
  261. * @return string
  262. */
  263. public static function encryptQuery($query)
  264. {
  265. $crypto = new Crypto();
  266. return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_');
  267. }
  268. /**
  269. * @param string $query
  270. *
  271. * @return string|null
  272. */
  273. public static function decryptQuery($query)
  274. {
  275. $crypto = new Crypto();
  276. return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/')));
  277. }
  278. /**
  279. * Returns url separator
  280. *
  281. * extracted from arg_separator.input as set in php.ini
  282. * we do not use arg_separator.output to avoid problems with &amp; and &
  283. *
  284. * @param string $encode whether to encode separator or not,
  285. * currently 'none' or 'html'
  286. *
  287. * @return string character used for separating url parts usually ; or &
  288. * @access public
  289. */
  290. public static function getArgSeparator($encode = 'none')
  291. {
  292. static $separator = null;
  293. static $html_separator = null;
  294. if (null === $separator) {
  295. // use separators defined by php, but prefer ';'
  296. // as recommended by W3C
  297. // (see https://www.w3.org/TR/1999/REC-html401-19991224/appendix
  298. // /notes.html#h-B.2.2)
  299. $arg_separator = ini_get('arg_separator.input');
  300. if (mb_strpos($arg_separator, ';') !== false) {
  301. $separator = ';';
  302. } elseif (strlen($arg_separator) > 0) {
  303. $separator = $arg_separator[0];
  304. } else {
  305. $separator = '&';
  306. }
  307. $html_separator = htmlentities($separator);
  308. }
  309. switch ($encode) {
  310. case 'html':
  311. return $html_separator;
  312. case 'text' :
  313. case 'none' :
  314. default :
  315. return $separator;
  316. }
  317. }
  318. }