Fakultas Ilmu Komputer UI

functions.inc.php 42.9 KB
Newer Older
jakubvrana's avatar
jakubvrana committed
1
<?php
jakubvrana's avatar
jakubvrana committed
2
3
4
/** Get database connection
* @return Min_DB
*/
jakubvrana's avatar
jakubvrana committed
5
6
7
8
function connection() {
	// can be used in customization, $connection is minified
	global $connection;
	return $connection;
jakubvrana's avatar
jakubvrana committed
9
10
}

11
12
13
14
15
16
17
18
/** Get Adminer object
* @return Adminer
*/
function adminer() {
	global $adminer;
	return $adminer;
}

Jakub Vrana's avatar
Jakub Vrana committed
19
20
21
22
23
24
25
26
/** Get Adminer version
* @return string
*/
function version() {
	global $VERSION;
	return $VERSION;
}

jakubvrana's avatar
jakubvrana committed
27
28
29
30
/** Unescape database identifier
* @param string text inside ``
* @return string
*/
jakubvrana's avatar
jakubvrana committed
31
function idf_unescape($idf) {
jakubvrana's avatar
jakubvrana committed
32
33
	$last = substr($idf, -1);
	return str_replace($last . $last, $last, substr($idf, 1, -1));
jakubvrana's avatar
jakubvrana committed
34
35
}

jakubvrana's avatar
jakubvrana committed
36
37
38
39
40
/** Escape string to use inside ''
* @param string
* @return string
*/
function escape_string($val) {
41
	return substr(q($val), 1, -1);
jakubvrana's avatar
jakubvrana committed
42
43
}

44
45
46
47
48
/** Remove non-digits from a string
* @param string
* @return string
*/
function number($val) {
Jakub Vrana's avatar
Jakub Vrana committed
49
	return preg_replace('~[^0-9]+~', '', $val);
50
51
}

52
53
54
55
/** Get regular expression to match numeric types
* @return string
*/
function number_type() {
56
	return '((?<!o)int(?!er)|numeric|real|float|double|decimal|money)'; // not point, not interval
57
58
}

59
60
/** Disable magic_quotes_gpc
* @param array e.g. (&$_GET, &$_POST, &$_COOKIE)
61
* @param bool whether to leave values as is
62
63
* @return null modified in place
*/
64
function remove_slashes($process, $filter = false) {
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	if (get_magic_quotes_gpc()) {
		while (list($key, $val) = each($process)) {
			foreach ($val as $k => $v) {
				unset($process[$key][$k]);
				if (is_array($v)) {
					$process[$key][stripslashes($k)] = $v;
					$process[] = &$process[$key][stripslashes($k)];
				} else {
					$process[$key][stripslashes($k)] = ($filter ? $v : stripslashes($v));
				}
			}
		}
	}
}

jakubvrana's avatar
jakubvrana committed
80
81
82
83
84
/** Escape or unescape string to use inside form []
* @param string
* @param bool
* @return string
*/
jakubvrana's avatar
jakubvrana committed
85
function bracket_escape($idf, $back = false) {
jakubvrana's avatar
jakubvrana committed
86
	// escape brackets inside name="x[]"
87
	static $trans = array(':' => ':1', ']' => ':2', '[' => ':3', '"' => ':4');
jakubvrana's avatar
jakubvrana committed
88
89
90
	return strtr($idf, ($back ? array_flip($trans) : $trans));
}

Jakub Vrana's avatar
Jakub Vrana committed
91
92
93
/** Check if connection has at least the given version
* @param string required version
* @param string required MariaDB version
94
* @param Min_DB defaults to $connection
Jakub Vrana's avatar
Jakub Vrana committed
95
96
* @return bool
*/
97
function min_version($version, $maria_db = "", $connection2 = null) {
Jakub Vrana's avatar
Jakub Vrana committed
98
	global $connection;
99
100
101
102
	if (!$connection2) {
		$connection2 = $connection;
	}
	$server_info = $connection2->server_info;
Jakub Vrana's avatar
Jakub Vrana committed
103
104
105
106
107
108
109
	if ($maria_db && preg_match('~([\d.]+)-MariaDB~', $server_info, $match)) {
		$server_info = $match[1];
		$version = $maria_db;
	}
	return (version_compare($server_info, $version) >= 0);
}

Jakub Vrana's avatar
Jakub Vrana committed
110
111
112
113
114
/** Get connection charset
* @param Min_DB
* @return string
*/
function charset($connection) {
Jakub Vrana's avatar
Jakub Vrana committed
115
	return (min_version("5.5.3", 0, $connection) ? "utf8mb4" : "utf8"); // SHOW CHARSET would require an extra query
Jakub Vrana's avatar
Jakub Vrana committed
116
117
}

Jakub Vrana's avatar
Jakub Vrana committed
118
119
120
121
122
123
/** Return <script> element
* @param string
* @param string
* @return string
*/
function script($source, $trailing = "\n") {
Jakub Vrana's avatar
Jakub Vrana committed
124
	return "<script" . nonce() . ">$source</script>$trailing";
Jakub Vrana's avatar
Jakub Vrana committed
125
126
}

Jakub Vrana's avatar
Jakub Vrana committed
127
128
129
130
131
/** Return <script src> element
* @param string
* @return string
*/
function script_src($url) {
Jakub Vrana's avatar
Jakub Vrana committed
132
133
134
135
136
137
138
139
	return "<script src='" . h($url) . "'" . nonce() . "></script>\n";
}

/** Get a nonce="" attribute with CSP nonce
* @return string
*/
function nonce() {
	return ' nonce="' . get_nonce() . '"';
Jakub Vrana's avatar
Jakub Vrana committed
140
141
}

142
143
144
145
/** Get a target="_blank" attribute
* @return string
*/
function target_blank() {
146
	return ' target="_blank" rel="noreferrer noopener"';
147
148
}

jakubvrana's avatar
jakubvrana committed
149
150
151
152
/** Escape for HTML
* @param string
* @return string
*/
jakubvrana's avatar
jakubvrana committed
153
function h($string) {
154
	return str_replace("\0", "&#0;", htmlspecialchars($string, ENT_QUOTES, 'utf-8'));
jakubvrana's avatar
jakubvrana committed
155
156
}

jakubvrana's avatar
jakubvrana committed
157
158
159
160
161
162
163
164
/** Convert \n to <br>
* @param string
* @return string
*/
function nl_br($string) {
	return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3
}

jakubvrana's avatar
jakubvrana committed
165
166
167
168
169
170
/** Generate HTML checkbox
* @param string
* @param string
* @param bool
* @param string
* @param string
171
* @param string
172
* @param string
173
* @return string
jakubvrana's avatar
jakubvrana committed
174
*/
175
function checkbox($name, $value, $checked, $label = "", $onclick = "", $class = "", $labelled_by = "") {
176
177
	$return = "<input type='checkbox' name='$name' value='" . h($value) . "'"
		. ($checked ? " checked" : "")
178
		. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
179
		. ">"
Jakub Vrana's avatar
Jakub Vrana committed
180
		. ($onclick ? script("qsl('input').onclick = function () { $onclick };", "") : "")
181
	;
182
	return ($label != "" || $class ? "<label" . ($class ? " class='$class'" : "") . ">$return" . h($label) . "</label>" : $return);
jakubvrana's avatar
jakubvrana committed
183
184
}

jakubvrana's avatar
jakubvrana committed
185
186
187
188
189
190
/** Generate list of HTML options
* @param array array of strings or arrays (creates optgroup)
* @param mixed
* @param bool always use array keys for value="", otherwise only string keys are used
* @return string
*/
191
function optionlist($options, $selected = null, $use_keys = false) {
jakubvrana's avatar
jakubvrana committed
192
193
	$return = "";
	foreach ($options as $k => $v) {
194
		$opts = array($k => $v);
jakubvrana's avatar
jakubvrana committed
195
		if (is_array($v)) {
jakubvrana's avatar
jakubvrana committed
196
			$return .= '<optgroup label="' . h($k) . '">';
197
			$opts = $v;
jakubvrana's avatar
jakubvrana committed
198
		}
199
		foreach ($opts as $key => $val) {
jakubvrana's avatar
jakubvrana committed
200
			$return .= '<option' . ($use_keys || is_string($key) ? ' value="' . h($key) . '"' : '') . (($use_keys || is_string($key) ? (string) $key : $val) === $selected ? ' selected' : '') . '>' . h($val);
jakubvrana's avatar
jakubvrana committed
201
202
203
204
205
206
207
208
		}
		if (is_array($v)) {
			$return .= '</optgroup>';
		}
	}
	return $return;
}

jakubvrana's avatar
jakubvrana committed
209
210
211
212
213
/** Generate HTML radio list
* @param string
* @param array
* @param string
* @param string true for no onchange, false for radio
214
* @param string
jakubvrana's avatar
jakubvrana committed
215
216
* @return string
*/
217
function html_select($name, $options, $value = "", $onchange = true, $labelled_by = "") {
jakubvrana's avatar
jakubvrana committed
218
	if ($onchange) {
219
220
		return "<select name='" . h($name) . "'"
			. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
221
			. ">" . optionlist($options, $value) . "</select>"
Jakub Vrana's avatar
Jakub Vrana committed
222
			. (is_string($onchange) ? script("qsl('select').onchange = function () { $onchange };", "") : "")
223
		;
jakubvrana's avatar
jakubvrana committed
224
225
226
227
228
229
230
231
	}
	$return = "";
	foreach ($options as $key => $val) {
		$return .= "<label><input type='radio' name='" . h($name) . "' value='" . h($key) . "'" . ($key == $value ? " checked" : "") . ">" . h($val) . "</label>";
	}
	return $return;
}

Jakub Vrana's avatar
Jakub Vrana committed
232
/** Generate HTML <select> or <input> if $options are empty
Jakub Vrana's avatar
Jakub Vrana committed
233
234
235
236
* @param string
* @param array
* @param string
* @param string
237
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
238
239
* @return string
*/
240
241
242
243
244
function select_input($attrs, $options, $value = "", $onchange = "", $placeholder = "") {
	$tag = ($options ? "select" : "input");
	return "<$tag$attrs" . ($options
		? "><option value=''>$placeholder" . optionlist($options, $value, true) . "</select>"
		: " size='10' value='" . h($value) . "' placeholder='$placeholder'>"
Jakub Vrana's avatar
Jakub Vrana committed
245
	) . ($onchange ? script("qsl('$tag').onchange = $onchange;", "") : ""); //! use oninput for input
Jakub Vrana's avatar
Jakub Vrana committed
246
247
}

Jakub Vrana's avatar
Jakub Vrana committed
248
/** Get onclick confirmation
249
* @param string
250
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
251
252
* @return string
*/
253
function confirm($message = "", $selector = "qsl('input')") {
254
	return script("$selector.onclick = function () { return confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "'); };", "");
Jakub Vrana's avatar
Jakub Vrana committed
255
256
}

Jakub Vrana's avatar
Jakub Vrana committed
257
258
259
260
261
262
/** Print header for hidden fieldset (close by </div></fieldset>)
* @param string
* @param string
* @param bool
* @return null
*/
263
264
265
function print_fieldset($id, $legend, $visible = false) {
	echo "<fieldset><legend>";
	echo "<a href='#fieldset-$id'>$legend</a>";
Jakub Vrana's avatar
Jakub Vrana committed
266
	echo script("qsl('a').onclick = partial(toggle, 'fieldset-$id');", "");
267
268
	echo "</legend>";
	echo "<div id='fieldset-$id'" . ($visible ? "" : " class='hidden'") . ">\n";
Jakub Vrana's avatar
Jakub Vrana committed
269
270
271
272
}

/** Return class='active' if $bold is true
* @param bool
273
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
274
275
* @return string
*/
276
277
function bold($bold, $class = "") {
	return ($bold ? " class='active $class'" : ($class ? " class='$class'" : ""));
Jakub Vrana's avatar
Jakub Vrana committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
}

/** Generate class for odd rows
* @param string return this for odd rows, empty to reset counter
* @return string
*/
function odd($return = ' class="odd"') {
	static $i = 0;
	if (!$return) { // reset counter
		$i = -1;
	}
	return ($i++ % 2 ? $return : '');
}

292
293
294
295
296
297
298
299
/** Escape string for JavaScript apostrophes
* @param string
* @return string
*/
function js_escape($string) {
	return addcslashes($string, "\r\n'\\/"); // slash for <script>
}

Jakub Vrana's avatar
Jakub Vrana committed
300
301
302
303
304
305
306
307
308
309
310
/** Print one row in JSON object
* @param string or "" to close the object
* @param string
* @return null
*/
function json_row($key, $val = null) {
	static $first = true;
	if ($first) {
		echo "{";
	}
	if ($key != "") {
311
		echo ($first ? "" : ",") . "\n\t\"" . addcslashes($key, "\r\n\t\"\\/") . '": ' . ($val !== null ? '"' . addcslashes($val, "\r\n\"\\/") . '"' : 'null');
Jakub Vrana's avatar
Jakub Vrana committed
312
313
314
315
316
317
318
		$first = false;
	} else {
		echo "\n}\n";
		$first = true;
	}
}

jakubvrana's avatar
jakubvrana committed
319
320
321
322
323
324
/** Get INI boolean value
* @param string
* @return bool
*/
function ini_bool($ini) {
	$val = ini_get($ini);
Jakub Vrana's avatar
Jakub Vrana committed
325
	return (preg_match('~^(on|true|yes)$~i', $val) || (int) $val); // boolean values set by php_value are strings
jakubvrana's avatar
jakubvrana committed
326
327
}

328
329
330
331
/** Check if SID is neccessary
* @return bool
*/
function sid() {
Jakub Vrana's avatar
Jakub Vrana committed
332
	static $return;
333
	if ($return === null) { // restart_session() defines SID
Jakub Vrana's avatar
Jakub Vrana committed
334
335
336
		$return = (SID && !($_COOKIE && ini_bool("session.use_cookies"))); // $_COOKIE - don't pass SID with permanent login
	}
	return $return;
337
338
}

Jakub Vrana's avatar
Jakub Vrana committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/** Set password to session
* @param string
* @param string
* @param string
* @param string
* @return null
*/
function set_password($vendor, $server, $username, $password) {
	$_SESSION["pwds"][$vendor][$server][$username] = ($_COOKIE["adminer_key"] && is_string($password)
		? array(encrypt_string($password, $_COOKIE["adminer_key"]))
		: $password
	);
}

/** Get password from session
354
* @return string or null for missing password or false for expired password
Jakub Vrana's avatar
Jakub Vrana committed
355
356
357
358
359
360
361
362
363
364
365
366
*/
function get_password() {
	$return = get_session("pwds");
	if (is_array($return)) {
		$return = ($_COOKIE["adminer_key"]
			? decrypt_string($return[0], $_COOKIE["adminer_key"])
			: false
		);
	}
	return $return;
}

Jakub Vrana's avatar
Jakub Vrana committed
367
/** Shortcut for $connection->quote($string)
368
369
370
* @param string
* @return string
*/
371
function q($string) {
Jakub Vrana's avatar
Jakub Vrana committed
372
373
	global $connection;
	return $connection->quote($string);
374
375
}

jakubvrana's avatar
jakubvrana committed
376
377
378
379
380
/** Get list of values from database
* @param string
* @param mixed
* @return array
*/
381
function get_vals($query, $column = 0) {
jakubvrana's avatar
jakubvrana committed
382
	global $connection;
jakubvrana's avatar
jakubvrana committed
383
	$return = array();
jakubvrana's avatar
jakubvrana committed
384
	$result = $connection->query($query);
jakubvrana's avatar
jakubvrana committed
385
	if (is_object($result)) {
jakubvrana's avatar
jakubvrana committed
386
		while ($row = $result->fetch_row()) {
387
			$return[] = $row[$column];
jakubvrana's avatar
jakubvrana committed
388
		}
jakubvrana's avatar
jakubvrana committed
389
390
391
392
	}
	return $return;
}

jakubvrana's avatar
jakubvrana committed
393
394
395
/** Get keys from first column and values from second
* @param string
* @param Min_DB
396
* @param bool
jakubvrana's avatar
jakubvrana committed
397
398
* @return array
*/
Jakub Vrana's avatar
Jakub Vrana committed
399
function get_key_vals($query, $connection2 = null, $set_keys = true) {
jakubvrana's avatar
jakubvrana committed
400
401
402
403
404
405
	global $connection;
	if (!is_object($connection2)) {
		$connection2 = $connection;
	}
	$return = array();
	$result = $connection2->query($query);
Jakub Vrana's avatar
Jakub Vrana committed
406
407
	if (is_object($result)) {
		while ($row = $result->fetch_row()) {
408
409
410
411
412
			if ($set_keys) {
				$return[$row[0]] = $row[1];
			} else {
				$return[] = $row[0];
			}
Jakub Vrana's avatar
Jakub Vrana committed
413
		}
jakubvrana's avatar
jakubvrana committed
414
415
416
417
	}
	return $return;
}

Jakub Vrana's avatar
Jakub Vrana committed
418
419
/** Get all rows of result
* @param string
420
421
* @param Min_DB
* @param string
422
* @return array of associative arrays
Jakub Vrana's avatar
Jakub Vrana committed
423
*/
Jakub Vrana's avatar
Jakub Vrana committed
424
function get_rows($query, $connection2 = null, $error = "<p class='error'>") {
Jakub Vrana's avatar
Jakub Vrana committed
425
	global $connection;
426
	$conn = (is_object($connection2) ? $connection2 : $connection);
Jakub Vrana's avatar
Jakub Vrana committed
427
	$return = array();
428
	$result = $conn->query($query);
Jakub Vrana's avatar
Jakub Vrana committed
429
430
431
432
	if (is_object($result)) { // can return true
		while ($row = $result->fetch_assoc()) {
			$return[] = $row;
		}
433
	} elseif (!$result && !is_object($connection2) && $error && defined("PAGE_HEADER")) {
Jakub Vrana's avatar
Jakub Vrana committed
434
		echo $error . error() . "\n";
Jakub Vrana's avatar
Jakub Vrana committed
435
436
437
438
	}
	return $return;
}

jakubvrana's avatar
jakubvrana committed
439
440
441
/** Find unique identifier of a row
* @param array
* @param array result of indexes()
442
* @return array or null if there is no unique identifier
jakubvrana's avatar
jakubvrana committed
443
*/
jakubvrana's avatar
jakubvrana committed
444
function unique_array($row, $indexes) {
jakubvrana's avatar
jakubvrana committed
445
	foreach ($indexes as $index) {
Jakub Vrana's avatar
Jakub Vrana committed
446
		if (preg_match("~PRIMARY|UNIQUE~", $index["type"])) {
jakubvrana's avatar
jakubvrana committed
447
448
			$return = array();
			foreach ($index["columns"] as $key) {
jakubvrana's avatar
jakubvrana committed
449
				if (!isset($row[$key])) { // NULL is ambiguous
jakubvrana's avatar
jakubvrana committed
450
					continue 2;
jakubvrana's avatar
jakubvrana committed
451
				}
jakubvrana's avatar
jakubvrana committed
452
				$return[$key] = $row[$key];
jakubvrana's avatar
jakubvrana committed
453
			}
jakubvrana's avatar
jakubvrana committed
454
			return $return;
jakubvrana's avatar
jakubvrana committed
455
456
		}
	}
jakubvrana's avatar
jakubvrana committed
457
458
}

459
460
461
462
463
464
465
466
467
468
469
/** Escape column key used in where()
* @param string
* @return string
*/
function escape_key($key) {
	if (preg_match('(^([\w(]+)(' . str_replace("_", ".*", preg_quote(idf_escape("_"))) . ')([ \w)]+)$)', $key, $match)) { //! columns looking like functions
		return $match[1] . idf_escape(idf_unescape($match[2])) . $match[3]; //! SQL injection
	}
	return idf_escape($key);
}

jakubvrana's avatar
jakubvrana committed
470
471
/** Create SQL condition from parsed query string
* @param array parsed query string
472
* @param array
jakubvrana's avatar
jakubvrana committed
473
474
* @return string
*/
475
function where($where, $fields = array()) {
Jakub Vrana's avatar
Jakub Vrana committed
476
	global $connection, $jush;
jakubvrana's avatar
jakubvrana committed
477
	$return = array();
jakubvrana's avatar
jakubvrana committed
478
	foreach ((array) $where["where"] as $key => $val) {
Jakub Vrana's avatar
Jakub Vrana committed
479
		$key = bracket_escape($key, 1); // 1 - back
480
		$column = escape_key($key);
481
		$return[] = $column
482
483
			. ($jush == "sql" && is_numeric($val) && preg_match('~\.~', $val) ? " LIKE " . q($val) // LIKE because of floats but slow with ints
				: ($jush == "mssql" ? " LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val)) // LIKE because of text
484
				: " = " . unconvert_field($fields[$key], q($val))
485
			))
jakubvrana's avatar
jakubvrana committed
486
		; //! enum and set
487
		if ($jush == "sql" && preg_match('~char|text~', $fields[$key]["type"]) && preg_match("~[^ -@]~", $val)) { // not just [a-z] to catch non-ASCII characters
Jakub Vrana's avatar
Jakub Vrana committed
488
			$return[] = "$column = " . q($val) . " COLLATE " . charset($connection) . "_bin";
489
		}
jakubvrana's avatar
jakubvrana committed
490
491
	}
	foreach ((array) $where["null"] as $key) {
492
		$return[] = escape_key($key) . " IS NULL";
jakubvrana's avatar
jakubvrana committed
493
	}
jakubvrana's avatar
jakubvrana committed
494
	return implode(" AND ", $return);
jakubvrana's avatar
jakubvrana committed
495
496
}

jakubvrana's avatar
jakubvrana committed
497
498
/** Create SQL condition from query string
* @param string
499
* @param array
jakubvrana's avatar
jakubvrana committed
500
501
* @return string
*/
502
function where_check($val, $fields = array()) {
jakubvrana's avatar
jakubvrana committed
503
	parse_str($val, $check);
504
	remove_slashes(array(&$check));
505
	return where($check, $fields);
jakubvrana's avatar
jakubvrana committed
506
507
}

jakubvrana's avatar
jakubvrana committed
508
509
510
511
/** Create query string where condition from value
* @param int condition order
* @param string column identifier
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
512
* @param string
jakubvrana's avatar
jakubvrana committed
513
* @return string
jakubvrana's avatar
jakubvrana committed
514
*/
jakubvrana's avatar
jakubvrana committed
515
function where_link($i, $column, $value, $operator = "=") {
516
	return "&where%5B$i%5D%5Bcol%5D=" . urlencode($column) . "&where%5B$i%5D%5Bop%5D=" . urlencode(($value !== null ? $operator : "IS NULL")) . "&where%5B$i%5D%5Bval%5D=" . urlencode($value);
jakubvrana's avatar
jakubvrana committed
517
518
}

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/** Get select clause for convertible fields
* @param array
* @param array
* @param array
* @return string
*/
function convert_fields($columns, $fields, $select = array()) {
	$return = "";
	foreach ($columns as $key => $val) {
		if ($select && !in_array(idf_escape($key), $select)) {
			continue;
		}
		$as = convert_field($fields[$key]);
		if ($as) {
			$return .= ", $as AS " . idf_escape($key);
		}
	}
	return $return;
}

539
/** Set cookie valid on current path
jakubvrana's avatar
jakubvrana committed
540
541
* @param string
* @param string
542
* @param int number of seconds, 0 for session cookie
jakubvrana's avatar
jakubvrana committed
543
544
* @return bool
*/
545
function cookie($name, $value, $lifetime = 2592000) { // 2592000 - 30 days
Jakub Vrana's avatar
Jakub Vrana committed
546
	global $HTTPS;
547
548
	return header("Set-Cookie: $name=" . urlencode($value)
		. ($lifetime ? "; expires=" . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT" : "")
Jakub Vrana's avatar
Jakub Vrana committed
549
		. "; path=" . preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"])
550
		. ($HTTPS ? "; secure" : "")
551
552
		. "; HttpOnly; SameSite=lax",
		false);
jakubvrana's avatar
jakubvrana committed
553
554
}

jakubvrana's avatar
jakubvrana committed
555
556
557
558
/** Restart stopped session
* @return null
*/
function restart_session() {
jakubvrana's avatar
jakubvrana committed
559
	if (!ini_bool("session.use_cookies")) {
560
		// session_start();
jakubvrana's avatar
jakubvrana committed
561
562
563
	}
}

Jakub Vrana's avatar
Jakub Vrana committed
564
565
/** Stop session if possible
* @param bool
Jakub Vrana's avatar
Jakub Vrana committed
566
567
* @return null
*/
Jakub Vrana's avatar
Jakub Vrana committed
568
function stop_session($force = false) {
569
570
	$use_cookies = ini_bool("session.use_cookies");
	if (!$use_cookies || $force) {
Jakub Vrana's avatar
Jakub Vrana committed
571
		session_write_close(); // improves concurrency if a user opens several pages at once, may be restarted later
572
		if ($use_cookies && @ini_set("session.use_cookies", false) === false) { // @ - may be disabled
573
			// session_start();
574
		}
Jakub Vrana's avatar
Jakub Vrana committed
575
576
577
	}
}

jakubvrana's avatar
jakubvrana committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
/** Get session variable for current server
* @param string
* @return mixed
*/
function &get_session($key) {
	return $_SESSION[$key][DRIVER][SERVER][$_GET["username"]];
}

/** Set session variable for current server
* @param string
* @param mixed
* @return mixed
*/
function set_session($key, $val) {
	$_SESSION[$key][DRIVER][SERVER][$_GET["username"]] = $val; // used also in auth.inc.php
}

Jakub Vrana's avatar
Jakub Vrana committed
595
596
597
598
/** Get authenticated URL
* @param string
* @param string
* @param string
599
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
600
601
* @return string
*/
Jakub Vrana's avatar
Jakub Vrana committed
602
function auth_url($vendor, $server, $username, $db = null) {
Jakub Vrana's avatar
Jakub Vrana committed
603
	global $drivers;
Jakub Vrana's avatar
Jakub Vrana committed
604
	preg_match('~([^?]*)\??(.*)~', remove_from_uri(implode("|", array_keys($drivers)) . "|username|" . ($db !== null ? "db|" : "") . session_name()), $match);
Jakub Vrana's avatar
Jakub Vrana committed
605
	return "$match[1]?"
606
		. (sid() ? SID . "&" : "")
Jakub Vrana's avatar
Jakub Vrana committed
607
		. ($vendor != "server" || $server != "" ? urlencode($vendor) . "=" . urlencode($server) . "&" : "")
Jakub Vrana's avatar
Jakub Vrana committed
608
		. "username=" . urlencode($username)
609
		. ($db != "" ? "&db=" . urlencode($db) : "")
Jakub Vrana's avatar
Jakub Vrana committed
610
611
612
613
		. ($match[2] ? "&$match[2]" : "")
	;
}

614
615
616
617
/** Find whether it is an AJAX request
* @return bool
*/
function is_ajax() {
Jakub Vrana's avatar
Jakub Vrana committed
618
	return ($_SERVER["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest");
619
620
}

jakubvrana's avatar
jakubvrana committed
621
/** Send Location header and exit
jakubvrana's avatar
jakubvrana committed
622
* @param string null to only set a message
jakubvrana's avatar
jakubvrana committed
623
624
625
* @param string
* @return null
*/
jakubvrana's avatar
jakubvrana committed
626
function redirect($location, $message = null) {
627
	if ($message !== null) {
jakubvrana's avatar
jakubvrana committed
628
		restart_session();
629
		$_SESSION["messages"][preg_replace('~^[^?]*~', '', ($location !== null ? $location : $_SERVER["REQUEST_URI"]))][] = $message;
jakubvrana's avatar
jakubvrana committed
630
	}
631
	if ($location !== null) {
632
633
634
		if ($location == "") {
			$location = ".";
		}
Jakub Vrana's avatar
Jakub Vrana committed
635
		header("Location: $location");
Jakub Vrana's avatar
Jakub Vrana committed
636
		exit;
jakubvrana's avatar
jakubvrana committed
637
	}
jakubvrana's avatar
jakubvrana committed
638
639
}

jakubvrana's avatar
jakubvrana committed
640
641
642
643
644
645
646
/** Execute query and redirect if successful
* @param string
* @param string
* @param string
* @param bool
* @param bool
* @param bool
Jakub Vrana's avatar
Jakub Vrana committed
647
* @param string
jakubvrana's avatar
jakubvrana committed
648
649
* @return bool
*/
650
function query_redirect($query, $location, $message, $redirect = true, $execute = true, $failed = false, $time = "") {
jakubvrana's avatar
jakubvrana committed
651
	global $connection, $error, $adminer;
jakubvrana's avatar
jakubvrana committed
652
	if ($execute) {
Jakub Vrana's avatar
Jakub Vrana committed
653
		$start = microtime(true);
jakubvrana's avatar
jakubvrana committed
654
		$failed = !$connection->query($query);
Jakub Vrana's avatar
Jakub Vrana committed
655
		$time = format_time($start);
jakubvrana's avatar
jakubvrana committed
656
	}
jakubvrana's avatar
jakubvrana committed
657
658
	$sql = "";
	if ($query) {
659
		$sql = $adminer->messageQuery($query, $time, $failed);
jakubvrana's avatar
jakubvrana committed
660
	}
661
	if ($failed) {
Jakub Vrana's avatar
Jakub Vrana committed
662
		$error = error() . $sql . script("messagesPrint();");
jakubvrana's avatar
jakubvrana committed
663
		return false;
jakubvrana's avatar
jakubvrana committed
664
	}
jakubvrana's avatar
jakubvrana committed
665
666
667
668
	if ($redirect) {
		redirect($location, $message . $sql);
	}
	return true;
jakubvrana's avatar
jakubvrana committed
669
670
}

jakubvrana's avatar
jakubvrana committed
671
/** Execute and remember query
672
* @param string or null to return remembered queries, end with ';' to use DELIMITER
Jakub Vrana's avatar
Jakub Vrana committed
673
* @return Min_Result or array($queries, $time) if $query = null
jakubvrana's avatar
jakubvrana committed
674
*/
675
function queries($query) {
jakubvrana's avatar
jakubvrana committed
676
	global $connection;
677
	static $queries = array();
678
679
680
681
	static $start;
	if (!$start) {
		$start = microtime(true);
	}
682
	if ($query === null) {
683
		// return executed queries
Jakub Vrana's avatar
Jakub Vrana committed
684
		return array(implode("\n", $queries), format_time($start));
685
	}
686
687
	$queries[] = (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";";
	return $connection->query($query);
688
689
}

Jakub Vrana's avatar
Jakub Vrana committed
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
/** Apply command to all array items
* @param string
* @param array
* @param callback
* @return bool
*/
function apply_queries($query, $tables, $escape = 'table') {
	foreach ($tables as $table) {
		if (!queries("$query " . $escape($table))) {
			return false;
		}
	}
	return true;
}

705
706
707
708
/** Redirect by remembered queries
* @param string
* @param string
* @param bool
Jakub Vrana's avatar
Comment    
Jakub Vrana committed
709
* @return bool
710
711
*/
function queries_redirect($location, $message, $redirect) {
712
713
	list($queries, $time) = queries(null);
	return query_redirect($queries, $location, $message, $redirect, false, !$redirect, $time);
714
715
}

Jakub Vrana's avatar
Jakub Vrana committed
716
717
/** Format elapsed time
* @param float output of microtime(true)
Jakub Vrana's avatar
Jakub Vrana committed
718
719
* @return string HTML code
*/
Jakub Vrana's avatar
Jakub Vrana committed
720
721
function format_time($start) {
	return lang('%.3f s', max(0, microtime(true) - $start));
Jakub Vrana's avatar
Jakub Vrana committed
722
723
}

724
725
726
727
728
729
730
/** Get relative REQUEST_URI
* @return string
*/
function relative_uri() {
	return preg_replace('~^[^?]*/([^?]*)~', '\1', $_SERVER["REQUEST_URI"]);
}

jakubvrana's avatar
jakubvrana committed
731
732
733
734
/** Remove parameter from query string
* @param string
* @return string
*/
jakubvrana's avatar
jakubvrana committed
735
function remove_from_uri($param = "") {
736
	return substr(preg_replace("~(?<=[?&])($param" . (SID ? "" : "|" . session_name()) . ")=[^&]*&~", '', relative_uri() . "&"), 0, -1);
jakubvrana's avatar
jakubvrana committed
737
738
}

jakubvrana's avatar
jakubvrana committed
739
740
/** Generate page number for pagination
* @param int
Jakub Vrana's avatar
Jakub Vrana committed
741
* @param int
jakubvrana's avatar
jakubvrana committed
742
743
* @return string
*/
jakubvrana's avatar
jakubvrana committed
744
function pagination($page, $current) {
Jakub Vrana's avatar
Jakub Vrana committed
745
746
747
748
	return " " . ($page == $current
		? $page + 1
		: '<a href="' . h(remove_from_uri("page") . ($page ? "&page=$page" . ($_GET["next"] ? "&next=" . urlencode($_GET["next"]) : "") : "")) . '">' . ($page + 1) . "</a>"
	);
jakubvrana's avatar
jakubvrana committed
749
750
}

751
/** Get file contents from $_FILES
jakubvrana's avatar
jakubvrana committed
752
753
* @param string
* @param bool
754
* @return mixed int for error, string otherwise
jakubvrana's avatar
jakubvrana committed
755
*/
jakubvrana's avatar
jakubvrana committed
756
757
function get_file($key, $decompress = false) {
	$file = $_FILES[$key];
758
759
	if (!$file) {
		return null;
jakubvrana's avatar
jakubvrana committed
760
	}
761
762
763
	foreach ($file as $key => $val) {
		$file[$key] = (array) $val;
	}
Jakub Vrana's avatar
Jakub Vrana committed
764
	$return = '';
765
766
767
	foreach ($file["error"] as $key => $error) {
		if ($error) {
			return $error;
768
		}
769
770
		$name = $file["name"][$key];
		$tmp_name = $file["tmp_name"][$key];
Jakub Vrana's avatar
Jakub Vrana committed
771
		$content = file_get_contents($decompress && preg_match('~\.gz$~', $name)
Jakub Vrana's avatar
Jakub Vrana committed
772
			? "compress.zlib://$tmp_name"
773
			: $tmp_name
Jakub Vrana's avatar
Jakub Vrana committed
774
		); //! may not be reachable because of open_basedir
775
776
		if ($decompress) {
			$start = substr($content, 0, 3);
Jakub Vrana's avatar
Jakub Vrana committed
777
			if (function_exists("iconv") && preg_match("~^\xFE\xFF|^\xFF\xFE~", $start, $regs)) { // not ternary operator to save memory
778
779
780
781
				$content = iconv("utf-16", "utf-8", $content);
			} elseif ($start == "\xEF\xBB\xBF") { // UTF-8 BOM
				$content = substr($content, 3);
			}
782
			$return .= $content . "\n\n";
Jakub Vrana's avatar
Jakub Vrana committed
783
784
		} else {
			$return .= $content;
785
		}
786
	}
787
	//! support SQL files not ending with semicolon
Jakub Vrana's avatar
Jakub Vrana committed
788
	return $return;
jakubvrana's avatar
jakubvrana committed
789
790
}

jakubvrana's avatar
jakubvrana committed
791
792
793
794
/** Determine upload error
* @param int
* @return string
*/
jakubvrana's avatar
jakubvrana committed
795
function upload_error($error) {
Jakub Vrana's avatar
Jakub Vrana committed
796
	$max_size = ($error == UPLOAD_ERR_INI_SIZE ? ini_get("upload_max_filesize") : 0); // post_max_size is checked in index.php
797
	return ($error ? lang('Unable to upload a file.') . ($max_size ? " " . lang('Maximum allowed file size is %sB.', $max_size) : "") : lang('File does not exist.'));
jakubvrana's avatar
jakubvrana committed
798
799
}

Jakub Vrana's avatar
Jakub Vrana committed
800
/** Create repeat pattern for preg
801
* @param string
Jakub Vrana's avatar
Jakub Vrana committed
802
803
* @param int
* @return string
804
*/
Jakub Vrana's avatar
Jakub Vrana committed
805
806
807
function repeat_pattern($pattern, $length) {
	// fix for Compilation failed: number too big in {} quantifier
	return str_repeat("$pattern{0,65535}", $length / 65535) . "$pattern{0," . ($length % 65535) . "}"; // can create {0,0} which is OK
808
809
}

jakubvrana's avatar
jakubvrana committed
810
811
812
813
/** Check whether the string is in UTF-8
* @param string
* @return bool
*/
jakubvrana's avatar
jakubvrana committed
814
function is_utf8($val) {
jakubvrana's avatar
jakubvrana committed
815
	// don't print control chars except \t\r\n
Jakub Vrana's avatar
Jakub Vrana committed
816
	return (preg_match('~~u', $val) && !preg_match('~[\0-\x8\xB\xC\xE-\x1F]~', $val));
jakubvrana's avatar
jakubvrana committed
817
818
}

jakubvrana's avatar
jakubvrana committed
819
820
821
822
823
824
/** Shorten UTF-8 string
* @param string
* @param int
* @param string
* @return string escaped string with appended ...
*/
jakubvrana's avatar
jakubvrana committed
825
function shorten_utf8($string, $length = 80, $suffix = "") {
826
	if (!preg_match("(^(" . repeat_pattern("[\t\r\n -\x{10FFFF}]", $length) . ")($)?)u", $string, $match)) { // ~s causes trash in $match[2] under some PHP versions, (.|\n) is slow
827
		preg_match("(^(" . repeat_pattern("[\t\r\n -~]", $length) . ")($)?)", $string, $match);
jakubvrana's avatar
jakubvrana committed
828
	}
829
	return h($match[1]) . $suffix . (isset($match[2]) ? "" : "<i>…</i>");
jakubvrana's avatar
jakubvrana committed
830
}
831

Jakub Vrana's avatar
Jakub Vrana committed
832
833
834
835
836
/** Format decimal number
* @param int
* @return string
*/
function format_number($val) {
Jakub Vrana's avatar
Jakub Vrana committed
837
	return strtr(number_format($val, 0, ".", lang(',')), preg_split('~~u', lang('0123456789'), -1, PREG_SPLIT_NO_EMPTY));
Jakub Vrana's avatar
Jakub Vrana committed
838
839
}

jakubvrana's avatar
jakubvrana committed
840
841
842
843
/** Generate friendly URL
* @param string
* @return string
*/
jakubvrana's avatar
jakubvrana committed
844
function friendly_url($val) {
jakubvrana's avatar
jakubvrana committed
845
	// used for blobs and export
jakubvrana's avatar
jakubvrana committed
846
847
848
	return preg_replace('~[^a-z0-9_]~i', '-', $val);
}

jakubvrana's avatar
jakubvrana committed
849
850
851
/** Print hidden fields
* @param array
* @param array
852
* @return bool
jakubvrana's avatar
jakubvrana committed
853
*/
jakubvrana's avatar
jakubvrana committed
854
function hidden_fields($process, $ignore = array()) {
855
	$return = false;
856
	while (list($key, $val) = each($process)) {
857
858
859
860
861
862
		if (!in_array($key, $ignore)) {
			if (is_array($val)) {
				foreach ($val as $k => $v) {
					$process[$key . "[$k]"] = $v;
				}
			} else {
863
				$return = true;
864
				echo '<input type="hidden" name="' . h($key) . '" value="' . h($val) . '">';
865
866
867
			}
		}
	}
868
	return $return;
869
}
jakubvrana's avatar
jakubvrana committed
870

jakubvrana's avatar
jakubvrana committed
871
872
873
874
/** Print hidden fields for GET forms
* @return null
*/
function hidden_fields_get() {
875
	echo (sid() ? '<input type="hidden" name="' . session_name() . '" value="' . h(session_id()) . '">' : '');
jakubvrana's avatar
jakubvrana committed
876
877
878
879
	echo (SERVER !== null ? '<input type="hidden" name="' . DRIVER . '" value="' . h(SERVER) . '">' : "");
	echo '<input type="hidden" name="username" value="' . h($_GET["username"]) . '">';
}

880
881
882
883
884
885
886
887
888
889
/** Get status of a single table and fall back to name on error
* @param string
* @param bool
* @return array
*/
function table_status1($table, $fast = false) {
	$return = table_status($table, $fast);
	return ($return ? $return : array("Name" => $table));
}

jakubvrana's avatar
jakubvrana committed
890
891
892
893
/** Find out foreign keys for each column
* @param string
* @return array array($col => array())
*/
894
function column_foreign_keys($table) {
Jakub Vrana's avatar
Jakub Vrana committed
895
	global $adminer;
896
	$return = array();
Jakub Vrana's avatar
Jakub Vrana committed
897
	foreach ($adminer->foreignKeys($table) as $foreign_key) {
898
899
900
901
902
903
904
		foreach ($foreign_key["source"] as $val) {
			$return[$val][] = $foreign_key;
		}
	}
	return $return;
}

jakubvrana's avatar
jakubvrana committed
905
906
907
908
909
/** Print enum input field
* @param string "radio"|"checkbox"
* @param string
* @param array
* @param mixed int|string|array
Jakub Vrana's avatar
Jakub Vrana committed
910
* @param string
jakubvrana's avatar
jakubvrana committed
911
912
* @return null
*/
Jakub Vrana's avatar
Jakub Vrana committed
913
function enum_input($type, $attrs, $field, $value, $empty = null) {
914
	global $adminer;
jakubvrana's avatar
jakubvrana committed
915
	preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
916
	$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === 0) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
jakubvrana's avatar
jakubvrana committed
917
918
919
	foreach ($matches[1] as $i => $val) {
		$val = stripcslashes(str_replace("''", "'", $val));
		$checked = (is_int($value) ? $value == $i+1 : (is_array($value) ? in_array($i+1, $value) : $value === $val));
920
		$return .= " <label><input type='$type'$attrs value='" . ($i+1) . "'" . ($checked ? ' checked' : '') . '>' . h($adminer->editVal($val, $field)) . '</label>';
jakubvrana's avatar
jakubvrana committed
921
	}
922
	return $return;
jakubvrana's avatar
jakubvrana committed
923
924
}

jakubvrana's avatar
jakubvrana committed
925
926
927
928
929
930
/** Print edit input field
* @param array one field from fields()
* @param mixed
* @param string
* @return null
*/
931
function input($field, $value, $function) {
Jakub Vrana's avatar
Jakub Vrana committed
932
	global $types, $adminer, $jush;
jakubvrana's avatar
jakubvrana committed
933
	$name = h(bracket_escape($field["field"]));
jakubvrana's avatar
jakubvrana committed
934
	echo "<td class='function'>";
935
936
937
938
939
940
941
942
	if (is_array($value) && !$function) {
		$args = array($value);
		if (version_compare(PHP_VERSION, 5.4) >= 0) {
			$args[] = JSON_PRETTY_PRINT;
		}
		$value = call_user_func_array('json_encode', $args); //! requires PHP 5.2
		$function = "json";
	}
Jakub Vrana's avatar
Jakub Vrana committed
943
944
945
946
947
	$reset = ($jush == "mssql" && $field["auto_increment"]);
	if ($reset && !$_POST["save"]) {
		$function = null;
	}
	$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field);
Jakub Vrana's avatar
Jakub Vrana committed
948
	$attrs = " name='fields[$name]'";
jakubvrana's avatar
jakubvrana committed
949
	if ($field["type"] == "enum") {
950
		echo h($functions[""]) . "<td>" . $adminer->editInput($_GET["edit"], $field, $attrs, $value);
jakubvrana's avatar
jakubvrana committed
951
	} else {
952
		$has_function = (in_array($function, $functions) || isset($functions[$function]));
Jakub Vrana's avatar
Jakub Vrana committed
953
		echo (count($functions) > 1
954
955
			? "<select name='function[$name]'>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
				. on_help("getTarget(event).value.replace(/^SQL\$/, '')", 1)
Jakub Vrana's avatar
Jakub Vrana committed
956
				. script("qsl('select').onchange = functionChange;", "")
957
			: h(reset($functions))
Jakub Vrana's avatar
Jakub Vrana committed
958
		) . '<td>';
jakubvrana's avatar
jakubvrana committed
959
		$input = $adminer->editInput($_GET["edit"], $field, $attrs, $value); // usage in call is without a table
jakubvrana's avatar
jakubvrana committed
960
		if ($input != "") {
jakubvrana's avatar
jakubvrana committed
961
			echo $input;