2 /* vim: set expandtab sw=4 ts=4 sts=4: */
11 require_once './libraries/common.inc.php';
14 * Does the common work
16 $GLOBALS['js_include'][] = 'server_privileges.js';
17 $GLOBALS['js_include'][] = 'functions.js';
18 $GLOBALS['js_include'][] = 'jquery/jquery-ui-1.8.custom.js';
19 $_add_user_error = false;
21 require './libraries/server_common.inc.php';
23 if ($GLOBALS['cfg']['AjaxEnable']) {
24 $conditional_class = 'ajax';
26 $conditional_class = '';
30 * Messages are built using the message name
32 $strPrivDescAllPrivileges = __('Includes all privileges except GRANT.');
33 $strPrivDescAlter = __('Allows altering the structure of existing tables.');
34 $strPrivDescAlterRoutine = __('Allows altering and dropping stored routines.');
35 $strPrivDescCreateDb = __('Allows creating new databases and tables.');
36 $strPrivDescCreateRoutine = __('Allows creating stored routines.');
37 $strPrivDescCreateTbl = __('Allows creating new tables.');
38 $strPrivDescCreateTmpTable = __('Allows creating temporary tables.');
39 $strPrivDescCreateUser = __('Allows creating, dropping and renaming user accounts.');
40 $strPrivDescCreateView = __('Allows creating new views.');
41 $strPrivDescDelete = __('Allows deleting data.');
42 $strPrivDescDropDb = __('Allows dropping databases and tables.');
43 $strPrivDescDropTbl = __('Allows dropping tables.');
44 $strPrivDescEvent = __('Allows to set up events for the event scheduler');
45 $strPrivDescExecute = __('Allows executing stored routines.');
46 $strPrivDescFile = __('Allows importing data from and exporting data into files.');
47 $strPrivDescGrant = __('Allows adding users and privileges without reloading the privilege tables.');
48 $strPrivDescIndex = __('Allows creating and dropping indexes.');
49 $strPrivDescInsert = __('Allows inserting and replacing data.');
50 $strPrivDescLockTables = __('Allows locking tables for the current thread.');
51 $strPrivDescMaxConnections = __('Limits the number of new connections the user may open per hour.');
52 $strPrivDescMaxQuestions = __('Limits the number of queries the user may send to the server per hour.');
53 $strPrivDescMaxUpdates = __('Limits the number of commands that change any table or database the user may execute per hour.');
54 $strPrivDescMaxUserConnections = __('Limits the number of simultaneous connections the user may have.');
55 $strPrivDescProcess = __('Allows viewing processes of all users');
56 $strPrivDescReferences = __('Has no effect in this MySQL version.');
57 $strPrivDescReload = __('Allows reloading server settings and flushing the server\'s caches.');
58 $strPrivDescReplClient = __('Allows the user to ask where the slaves / masters are.');
59 $strPrivDescReplSlave = __('Needed for the replication slaves.');
60 $strPrivDescSelect = __('Allows reading data.');
61 $strPrivDescShowDb = __('Gives access to the complete list of databases.');
62 $strPrivDescShowView = __('Allows performing SHOW CREATE VIEW queries.');
63 $strPrivDescShutdown = __('Allows shutting down the server.');
64 $strPrivDescSuper = __('Allows connecting, even if maximum number of connections is reached; required for most administrative operations like setting global variables or killing threads of other users.');
65 $strPrivDescTrigger = __('Allows creating and dropping triggers');
66 $strPrivDescUpdate = __('Allows changing data.');
67 $strPrivDescUsage = __('No privileges.');
70 * Checks if a dropdown box has been used for selecting a database / table
72 if (PMA_isValid($_REQUEST['pred_tablename'])) {
73 $tablename = $_REQUEST['pred_tablename'];
74 unset($pred_tablename);
75 } elseif (PMA_isValid($_REQUEST['tablename'])) {
76 $tablename = $_REQUEST['tablename'];
81 if (PMA_isValid($_REQUEST['pred_dbname'])) {
82 $dbname = $_REQUEST['pred_dbname'];
84 } elseif (PMA_isValid($_REQUEST['dbname'])) {
85 $dbname = $_REQUEST['dbname'];
92 $db_and_table = PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . '.';
93 if (isset($tablename)) {
94 $db_and_table .= PMA_backquote($tablename);
99 $db_and_table = '*.*';
102 // check if given $dbname is a wildcard or not
103 if (isset($dbname)) {
104 //if (preg_match('/\\\\(?:_|%)/i', $dbname)) {
105 if (preg_match('/(?<!\\\\)(?:_|%)/i', $dbname)) {
106 $dbname_is_wildcard = true;
108 $dbname_is_wildcard = false;
113 * Checks if the user is allowed to do what he tries to...
115 if (!$is_superuser) {
116 require './libraries/server_links.inc.php';
118 . PMA_getIcon('b_usrlist.png')
119 . __('Privileges') . "\n"
121 PMA_Message
::error(__('No Privileges'))->display();
122 require './libraries/footer.inc.php';
125 $random_n = mt_rand(0,1000000); // a random number that will be appended to the id of the user forms
128 * Escapes wildcard in a database+table specification
129 * before using it in a GRANT statement.
131 * Escaping a wildcard character in a GRANT is only accepted at the global
132 * or database level, not at table level; this is why I remove
133 * the escaping character. Internally, in mysql.tables_priv.Db there are
134 * no escaping (for example test_db) but in mysql.db you'll see test\_db
135 * for a db-specific privilege.
137 * @param string $db_and_table
138 * @param string $dbname
139 * @param string $tablename
140 * @return string the escaped (if necessary) $db_and_table
142 function PMA_WildcardEscapeForGrant($db_and_table, $dbname, $tablename) {
144 if (! strlen($dbname)) {
145 $db_and_table = '*.*';
147 if (strlen($tablename)) {
148 $db_and_table = PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . '.';
149 $db_and_table .= PMA_backquote($tablename);
151 $db_and_table = PMA_backquote($dbname) . '.';
152 $db_and_table .= '*';
155 return $db_and_table;
159 * Generates a condition on the user name
161 * @param string the user's initial
162 * @return string the generated condition
164 function PMA_RangeOfUsers($initial = '')
166 // strtolower() is used because the User field
167 // might be BINARY, so LIKE would be case sensitive
168 if (!empty($initial)) {
169 $ret = " WHERE `User` LIKE '" . PMA_sqlAddSlashes($initial, true) . "%'"
170 . " OR `User` LIKE '" . PMA_sqlAddSlashes(strtolower($initial), true) . "%'";
178 * Extracts the privilege information of a priv table row
180 * @param array $row the row
181 * @param boolean $enableHTML add <dfn> tag with tooltips
183 * @global resource $user_link the database connection
187 function PMA_extractPrivInfo($row = '', $enableHTML = false)
190 array('Select_priv', 'SELECT', __('Allows reading data.')),
191 array('Insert_priv', 'INSERT', __('Allows inserting and replacing data.')),
192 array('Update_priv', 'UPDATE', __('Allows changing data.')),
193 array('Delete_priv', 'DELETE', __('Allows deleting data.')),
194 array('Create_priv', 'CREATE', __('Allows creating new databases and tables.')),
195 array('Drop_priv', 'DROP', __('Allows dropping databases and tables.')),
196 array('Reload_priv', 'RELOAD', __('Allows reloading server settings and flushing the server\'s caches.')),
197 array('Shutdown_priv', 'SHUTDOWN', __('Allows shutting down the server.')),
198 array('Process_priv', 'PROCESS', __('Allows viewing processes of all users')),
199 array('File_priv', 'FILE', __('Allows importing data from and exporting data into files.')),
200 array('References_priv', 'REFERENCES', __('Has no effect in this MySQL version.')),
201 array('Index_priv', 'INDEX', __('Allows creating and dropping indexes.')),
202 array('Alter_priv', 'ALTER', __('Allows altering the structure of existing tables.')),
203 array('Show_db_priv', 'SHOW DATABASES', __('Gives access to the complete list of databases.')),
204 array('Super_priv', 'SUPER', __('Allows connecting, even if maximum number of connections is reached; required for most administrative operations like setting global variables or killing threads of other users.')),
205 array('Create_tmp_table_priv', 'CREATE TEMPORARY TABLES', __('Allows creating temporary tables.')),
206 array('Lock_tables_priv', 'LOCK TABLES', __('Allows locking tables for the current thread.')),
207 array('Repl_slave_priv', 'REPLICATION SLAVE', __('Needed for the replication slaves.')),
208 array('Repl_client_priv', 'REPLICATION CLIENT', __('Allows the user to ask where the slaves / masters are.')),
209 array('Create_view_priv', 'CREATE VIEW', __('Allows creating new views.')),
210 array('Event_priv', 'EVENT', __('Allows to set up events for the event scheduler')),
211 array('Trigger_priv', 'TRIGGER', __('Allows creating and dropping triggers')),
213 array('Create View_priv', 'CREATE VIEW', __('Allows creating new views.')),
214 array('Show_view_priv', 'SHOW VIEW', __('Allows performing SHOW CREATE VIEW queries.')),
216 array('Show view_priv', 'SHOW VIEW', __('Allows performing SHOW CREATE VIEW queries.')),
217 array('Create_routine_priv', 'CREATE ROUTINE', __('Allows creating stored routines.')),
218 array('Alter_routine_priv', 'ALTER ROUTINE', __('Allows altering and dropping stored routines.')),
219 array('Create_user_priv', 'CREATE USER', __('Allows creating, dropping and renaming user accounts.')),
220 array('Execute_priv', 'EXECUTE', __('Allows executing stored routines.')),
223 if (!empty($row) && isset($row['Table_priv'])) {
224 $row1 = PMA_DBI_fetch_single_row(
225 'SHOW COLUMNS FROM `mysql`.`tables_priv` LIKE \'Table_priv\';',
226 'ASSOC', $GLOBALS['userlink']);
227 $av_grants = explode('\',\'', substr($row1['Type'], 5, strlen($row1['Type']) - 7));
229 $users_grants = explode(',', $row['Table_priv']);
230 foreach ($av_grants as $current_grant) {
231 $row[$current_grant . '_priv'] = in_array($current_grant, $users_grants) ?
'Y' : 'N';
233 unset($current_grant);
235 unset($users_grants);
238 $allPrivileges = true;
239 foreach ($grants as $current_grant) {
240 if ((!empty($row) && isset($row[$current_grant[0]]))
241 ||
(empty($row) && isset($GLOBALS[$current_grant[0]]))) {
242 if ((!empty($row) && $row[$current_grant[0]] == 'Y')
244 && ($GLOBALS[$current_grant[0]] == 'Y'
245 ||
(is_array($GLOBALS[$current_grant[0]])
246 && count($GLOBALS[$current_grant[0]]) == $GLOBALS['column_count']
247 && empty($GLOBALS[$current_grant[0] . '_none'])))))
250 $privs[] = '<dfn title="' . $current_grant[2] . '">' . $current_grant[1] . '</dfn>';
252 $privs[] = $current_grant[1];
254 } elseif (!empty($GLOBALS[$current_grant[0]])
255 && is_array($GLOBALS[$current_grant[0]])
256 && empty($GLOBALS[$current_grant[0] . '_none'])) {
258 $priv_string = '<dfn title="' . $current_grant[2] . '">' . $current_grant[1] . '</dfn>';
260 $priv_string = $current_grant[1];
262 $privs[] = $priv_string . ' (`' . join('`, `', $GLOBALS[$current_grant[0]]) . '`)';
264 $allPrivileges = false;
270 $privs[] = '<dfn title="' . __('No privileges.') . '">USAGE</dfn>';
274 } elseif ($allPrivileges && (! isset($GLOBALS['grant_count']) ||
count($privs) == $GLOBALS['grant_count'])) {
276 $privs = array('<dfn title="' . __('Includes all privileges except GRANT.') . '">ALL PRIVILEGES</dfn>');
278 $privs = array('ALL PRIVILEGES');
282 } // end of the 'PMA_extractPrivInfo()' function
285 * Displays on which column(s) a table-specific privilege is granted
287 function PMA_display_column_privs($columns, $row, $name_for_select,
288 $priv_for_header, $name, $name_for_dfn, $name_for_current)
290 echo ' <div class="item" id="div_item_' . $name . '">' . "\n"
291 . ' <label for="select_' . $name . '_priv">' . "\n"
292 . ' <tt><dfn title="' . $name_for_dfn . '">'
293 . $priv_for_header . '</dfn></tt>' . "\n"
294 . ' </label><br />' . "\n"
295 . ' <select id="select_' . $name . '_priv" name="'
296 . $name_for_select . '[]" multiple="multiple" size="8">' . "\n";
298 foreach ($columns as $current_column => $current_column_privileges) {
299 echo ' <option value="' . htmlspecialchars($current_column) . '"';
300 if ($row[$name_for_select] == 'Y' ||
$current_column_privileges[$name_for_current]) {
301 echo ' selected="selected"';
303 echo '>' . htmlspecialchars($current_column) . '</option>' . "\n";
306 echo ' </select>' . "\n"
307 . ' <i>' . __('Or') . '</i>' . "\n"
308 . ' <label for="checkbox_' . $name_for_select
309 . '_none"><input type="checkbox"'
310 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
311 . ' name="' . $name_for_select . '_none" id="checkbox_'
312 . $name_for_select . '_none" title="' . _pgettext('None privileges', 'None') . '" />'
313 . _pgettext('None privileges', 'None') . '</label>' . "\n"
319 * Displays the privileges form table
321 * @param string $db the database
322 * @param string $table the table
323 * @param boolean $submit wheather to display the submit button or not
324 * @global array $cfg the phpMyAdmin configuration
325 * @global ressource $user_link the database connection
329 function PMA_displayPrivTable($db = '*', $table = '*', $submit = true)
337 if (isset($GLOBALS['username'])) {
338 $username = $GLOBALS['username'];
339 $hostname = $GLOBALS['hostname'];
342 "SELECT * FROM `mysql`.`user`"
343 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
344 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
345 } elseif ($table == '*') {
347 "SELECT * FROM `mysql`.`db`"
348 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
349 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "'"
350 ." AND '" . PMA_unescape_mysql_wildcards($db) . "'"
354 "SELECT `Table_priv`"
355 ." FROM `mysql`.`tables_priv`"
356 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
357 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "'"
358 ." AND `Db` = '" . PMA_unescape_mysql_wildcards($db) . "'"
359 ." AND `Table_name` = '" . PMA_sqlAddSlashes($table) . "';";
361 $row = PMA_DBI_fetch_single_row($sql_query);
366 $sql_query = 'SHOW COLUMNS FROM `mysql`.`user`;';
367 } elseif ($table == '*') {
368 $sql_query = 'SHOW COLUMNS FROM `mysql`.`db`;';
370 $res = PMA_DBI_query($sql_query);
371 while ($row1 = PMA_DBI_fetch_row($res)) {
372 if (substr($row1[0], 0, 4) == 'max_') {
375 $row[$row1[0]] = 'N';
378 PMA_DBI_free_result($res);
380 $row = array('Table_priv' => '');
383 if (isset($row['Table_priv'])) {
384 $row1 = PMA_DBI_fetch_single_row(
385 'SHOW COLUMNS FROM `mysql`.`tables_priv` LIKE \'Table_priv\';',
386 'ASSOC', $GLOBALS['userlink']);
387 // note: in MySQL 5.0.3 we get "Create View', 'Show view';
388 // the View for Create is spelled with uppercase V
389 // the view for Show is spelled with lowercase v
390 // and there is a space between the words
392 $av_grants = explode('\',\'', substr($row1['Type'], strpos($row1['Type'], '(') +
2, strpos($row1['Type'], ')') - strpos($row1['Type'], '(') - 3));
394 $users_grants = explode(',', $row['Table_priv']);
396 foreach ($av_grants as $current_grant) {
397 $row[$current_grant . '_priv'] = in_array($current_grant, $users_grants) ?
'Y' : 'N';
399 unset($row['Table_priv'], $current_grant, $av_grants, $users_grants);
402 $res = PMA_DBI_try_query('SHOW COLUMNS FROM ' . PMA_backquote(PMA_unescape_mysql_wildcards($db)) . '.' . PMA_backquote($table) . ';');
405 while ($row1 = PMA_DBI_fetch_row($res)) {
406 $columns[$row1[0]] = array(
410 'References' => false
413 PMA_DBI_free_result($res);
417 // t a b l e - s p e c i f i c p r i v i l e g e s
418 if (! empty($columns)) {
419 $res = PMA_DBI_query(
420 'SELECT `Column_name`, `Column_priv`'
421 .' FROM `mysql`.`columns_priv`'
423 .' = \'' . PMA_sqlAddSlashes($username) . "'"
425 .' = \'' . PMA_sqlAddSlashes($hostname) . "'"
427 .' = \'' . PMA_sqlAddSlashes(PMA_unescape_mysql_wildcards($db)) . "'"
429 .' = \'' . PMA_sqlAddSlashes($table) . '\';');
431 while ($row1 = PMA_DBI_fetch_row($res)) {
432 $row1[1] = explode(',', $row1[1]);
433 foreach ($row1[1] as $current) {
434 $columns[$row1[0]][$current] = true;
437 PMA_DBI_free_result($res);
438 unset($res, $row1, $current);
440 echo '<input type="hidden" name="grant_count" value="' . count($row) . '" />' . "\n"
441 . '<input type="hidden" name="column_count" value="' . count($columns) . '" />' . "\n"
442 . '<fieldset id="fieldset_user_priv">' . "\n"
443 . ' <legend>' . __('Table-specific privileges')
444 . PMA_showHint(__(' Note: MySQL privilege names are expressed in English '))
445 . '</legend>' . "\n";
449 // privs that are attached to a specific column
450 PMA_display_column_privs($columns, $row, 'Select_priv',
451 'SELECT', 'select', __('Allows reading data.'), 'Select');
453 PMA_display_column_privs($columns, $row, 'Insert_priv',
454 'INSERT', 'insert', __('Allows inserting and replacing data.'), 'Insert');
456 PMA_display_column_privs($columns, $row, 'Update_priv',
457 'UPDATE', 'update', __('Allows changing data.'), 'Update');
459 PMA_display_column_privs($columns, $row, 'References_priv',
460 'REFERENCES', 'references', __('Has no effect in this MySQL version.'), 'References');
462 // privs that are not attached to a specific column
464 echo ' <div class="item">' . "\n";
465 foreach ($row as $current_grant => $current_grant_value) {
466 if (in_array(substr($current_grant, 0, (strlen($current_grant) - 5)),
467 array('Select', 'Insert', 'Update', 'References'))) {
470 // make a substitution to match the messages variables;
471 // also we must substitute the grant we get, because we can't generate
472 // a form variable containing blanks (those would get changed to
473 // an underscore when receiving the POST)
474 if ($current_grant == 'Create View_priv') {
475 $tmp_current_grant = 'CreateView_priv';
476 $current_grant = 'Create_view_priv';
477 } elseif ($current_grant == 'Show view_priv') {
478 $tmp_current_grant = 'ShowView_priv';
479 $current_grant = 'Show_view_priv';
481 $tmp_current_grant = $current_grant;
484 echo ' <div class="item">' . "\n"
485 . ' <input type="checkbox"'
486 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
487 . ' name="' . $current_grant . '" id="checkbox_' . $current_grant
489 . ($current_grant_value == 'Y' ?
'checked="checked" ' : '')
492 echo (isset($GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))])
493 ?
$GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))]
494 : $GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5)) . 'Tbl']) . '"/>' . "\n";
496 echo ' <label for="checkbox_' . $current_grant
497 . '"><tt><dfn title="'
498 . (isset($GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))])
499 ?
$GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))]
500 : $GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5)) . 'Tbl'])
501 . '">' . strtoupper(substr($current_grant, 0, strlen($current_grant) - 5)) . '</dfn></tt></label>' . "\n"
505 echo ' </div>' . "\n";
507 echo ' <div class="clearfloat"></div>' . "\n";
511 // g l o b a l o r d b - s p e c i f i c
513 $privTable_names = array(0 => __('Data'), 1 => __('Structure'), 2 => __('Administration'));
516 $privTable[0] = array(
517 array('Select', 'SELECT', __('Allows reading data.')),
518 array('Insert', 'INSERT', __('Allows inserting and replacing data.')),
519 array('Update', 'UPDATE', __('Allows changing data.')),
520 array('Delete', 'DELETE', __('Allows deleting data.'))
523 $privTable[0][] = array('File', 'FILE', __('Allows importing data from and exporting data into files.'));
527 $privTable[1] = array(
528 array('Create', 'CREATE', ($table == '*' ?
__('Allows creating new databases and tables.') : __('Allows creating new tables.'))),
529 array('Alter', 'ALTER', __('Allows altering the structure of existing tables.')),
530 array('Index', 'INDEX', __('Allows creating and dropping indexes.')),
531 array('Drop', 'DROP', ($table == '*' ?
__('Allows dropping databases and tables.') : __('Allows dropping tables.'))),
532 array('Create_tmp_table', 'CREATE TEMPORARY TABLES', __('Allows creating temporary tables.')),
533 array('Show_view', 'SHOW VIEW', __('Allows performing SHOW CREATE VIEW queries.')),
534 array('Create_routine', 'CREATE ROUTINE', __('Allows creating stored routines.')),
535 array('Alter_routine', 'ALTER ROUTINE', __('Allows altering and dropping stored routines.')),
536 array('Execute', 'EXECUTE', __('Allows executing stored routines.')),
538 // this one is for a db-specific priv: Create_view_priv
539 if (isset($row['Create_view_priv'])) {
540 $privTable[1][] = array('Create_view', 'CREATE VIEW', __('Allows creating new views.'));
542 // this one is for a table-specific priv: Create View_priv
543 if (isset($row['Create View_priv'])) {
544 $privTable[1][] = array('Create View', 'CREATE VIEW', __('Allows creating new views.'));
546 if (isset($row['Event_priv'])) {
548 $privTable[1][] = array('Event', 'EVENT', __('Allows to set up events for the event scheduler'));
549 $privTable[1][] = array('Trigger', 'TRIGGER', __('Allows creating and dropping triggers'));
552 // a d m i n i s t r a t i o n
553 $privTable[2] = array(
554 array('Grant', 'GRANT', __('Allows adding users and privileges without reloading the privilege tables.')),
557 $privTable[2][] = array('Super', 'SUPER', __('Allows connecting, even if maximum number of connections is reached; required for most administrative operations like setting global variables or killing threads of other users.'));
558 $privTable[2][] = array('Process', 'PROCESS', __('Allows viewing processes of all users'));
559 $privTable[2][] = array('Reload', 'RELOAD', __('Allows reloading server settings and flushing the server\'s caches.'));
560 $privTable[2][] = array('Shutdown', 'SHUTDOWN', __('Allows shutting down the server.'));
561 $privTable[2][] = array('Show_db', 'SHOW DATABASES', __('Gives access to the complete list of databases.'));
563 $privTable[2][] = array('Lock_tables', 'LOCK TABLES', __('Allows locking tables for the current thread.'));
564 $privTable[2][] = array('References', 'REFERENCES', __('Has no effect in this MySQL version.'));
566 $privTable[2][] = array('Repl_client', 'REPLICATION CLIENT', __('Allows the user to ask where the slaves / masters are.'));
567 $privTable[2][] = array('Repl_slave', 'REPLICATION SLAVE', __('Needed for the replication slaves.'));
568 $privTable[2][] = array('Create_user', 'CREATE USER', __('Allows creating, dropping and renaming user accounts.'));
570 echo '<input type="hidden" name="grant_count" value="'
571 . (count($privTable[0]) +
count($privTable[1]) +
count($privTable[2]) - (isset($row['Grant_priv']) ?
1 : 0))
573 . '<fieldset id="fieldset_user_global_rights">' . "\n"
577 ?
__('Global privileges')
579 ?
__('Database-specific privileges')
580 : __('Table-specific privileges'))) . "\n"
581 . ' (<a href="server_privileges.php?'
582 . $GLOBALS['url_query'] . '&checkall=1" onclick="setCheckboxes(\'addUsersForm_' . $random_n . '\', true); return false;">'
583 . __('Check All') . '</a> /' . "\n"
584 . ' <a href="server_privileges.php?'
585 . $GLOBALS['url_query'] . '" onclick="setCheckboxes(\'addUsersForm_' . $random_n . '\', false); return false;">'
586 . __('Uncheck All') . '</a>)' . "\n"
587 . ' </legend>' . "\n"
588 . ' <p><small><i>' . __(' Note: MySQL privilege names are expressed in English ') . '</i></small></p>' . "\n";
590 // Output the Global privilege tables with checkboxes
591 foreach($privTable as $i => $table) {
592 echo ' <fieldset>' . "\n"
593 . ' <legend>' . __($privTable_names[$i]) . '</legend>' . "\n";
594 foreach ($table as $priv)
596 echo ' <div class="item">' . "\n"
597 . ' <input type="checkbox"'
598 . ' name="' . $priv[0] . '_priv" id="checkbox_' . $priv[0] . '_priv"'
599 . ' value="Y" title="' . $priv[2] . '"'
600 . ((!empty($GLOBALS['checkall']) ||
$row[$priv[0] . '_priv'] == 'Y') ?
' checked="checked"' : '')
602 . ' <label for="checkbox_' . $priv[0] . '_priv"><tt><dfn title="' . $priv[2] . '">'
603 . $priv[1] . '</dfn></tt></label>' . "\n"
606 echo ' </fieldset>' . "\n";
609 // The "Resource limits" box is not displayed for db-specific privs
611 echo ' <fieldset>' . "\n"
612 . ' <legend>' . __('Resource limits') . '</legend>' . "\n"
613 . ' <p><small><i>' . __('Note: Setting these options to 0 (zero) removes the limit.') . '</i></small></p>' . "\n"
614 . ' <div class="item">' . "\n"
615 . ' <label for="text_max_questions"><tt><dfn title="'
616 . __('Limits the number of queries the user may send to the server per hour.') . '">MAX QUERIES PER HOUR</dfn></tt></label>' . "\n"
617 . ' <input type="text" name="max_questions" id="text_max_questions" value="'
618 . $row['max_questions'] . '" size="11" maxlength="11" title="' . __('Limits the number of queries the user may send to the server per hour.') . '" />' . "\n"
620 . ' <div class="item">' . "\n"
621 . ' <label for="text_max_updates"><tt><dfn title="'
622 . __('Limits the number of commands that change any table or database the user may execute per hour.') . '">MAX UPDATES PER HOUR</dfn></tt></label>' . "\n"
623 . ' <input type="text" name="max_updates" id="text_max_updates" value="'
624 . $row['max_updates'] . '" size="11" maxlength="11" title="' . __('Limits the number of commands that change any table or database the user may execute per hour.') . '" />' . "\n"
626 . ' <div class="item">' . "\n"
627 . ' <label for="text_max_connections"><tt><dfn title="'
628 . __('Limits the number of new connections the user may open per hour.') . '">MAX CONNECTIONS PER HOUR</dfn></tt></label>' . "\n"
629 . ' <input type="text" name="max_connections" id="text_max_connections" value="'
630 . $row['max_connections'] . '" size="11" maxlength="11" title="' . __('Limits the number of new connections the user may open per hour.') . '" />' . "\n"
632 . ' <div class="item">' . "\n"
633 . ' <label for="text_max_user_connections"><tt><dfn title="'
634 . __('Limits the number of simultaneous connections the user may have.') . '">MAX USER_CONNECTIONS</dfn></tt></label>' . "\n"
635 . ' <input type="text" name="max_user_connections" id="text_max_user_connections" value="'
636 . $row['max_user_connections'] . '" size="11" maxlength="11" title="' . __('Limits the number of simultaneous connections the user may have.') . '" />' . "\n"
638 . ' </fieldset>' . "\n";
641 echo ' <div class="clearfloat"></div>' . "\n";
643 echo '</fieldset>' . "\n";
645 echo '<fieldset id="fieldset_user_privtable_footer" class="tblFooters">' . "\n"
646 . ' <input type="submit" name="update_privs" value="' . __('Go') . '" />' . "\n"
647 . '</fieldset>' . "\n";
649 } // end of the 'PMA_displayPrivTable()' function
653 * Displays the fields used by the "new user" form as well as the
654 * "change login information / copy user" form.
656 * @param string $mode are we creating a new user or are we just
657 * changing one? (allowed values: 'new', 'change')
658 * @global array $cfg the phpMyAdmin configuration
659 * @global ressource $user_link the database connection
663 function PMA_displayLoginInformationFields($mode = 'new')
665 // Get user/host name lengths
666 $fields_info = PMA_DBI_get_columns('mysql', 'user', null, true);
667 $username_length = 16;
668 $hostname_length = 41;
669 foreach ($fields_info as $val) {
670 if ($val['Field'] == 'User') {
671 strtok($val['Type'], '()');
674 $username_length = $v;
676 } elseif ($val['Field'] == 'Host') {
677 strtok($val['Type'], '()');
680 $hostname_length = $v;
686 if (isset($GLOBALS['username']) && strlen($GLOBALS['username']) === 0) {
687 $GLOBALS['pred_username'] = 'any';
689 echo '<fieldset id="fieldset_add_user_login">' . "\n"
690 . '<legend>' . __('Login Information') . '</legend>' . "\n"
691 . '<div class="item">' . "\n"
692 . '<label for="select_pred_username">' . "\n"
693 . ' ' . __('User name') . ':' . "\n"
695 . '<span class="options">' . "\n"
696 . ' <select name="pred_username" id="select_pred_username" title="' . __('User name') . '"' . "\n"
697 . ' onchange="if (this.value == \'any\') { username.value = \'\'; } else if (this.value == \'userdefined\') { username.focus(); username.select(); }">' . "\n"
698 . ' <option value="any"' . ((isset($GLOBALS['pred_username']) && $GLOBALS['pred_username'] == 'any') ?
' selected="selected"' : '') . '>' . __('Any user') . '</option>' . "\n"
699 . ' <option value="userdefined"' . ((! isset($GLOBALS['pred_username']) ||
$GLOBALS['pred_username'] == 'userdefined') ?
' selected="selected"' : '') . '>' . __('Use text field') . ':</option>' . "\n"
700 . ' </select>' . "\n"
702 . '<input type="text" name="username" maxlength="'
703 . $username_length . '" title="' . __('User name') . '"'
704 . (empty($GLOBALS['username'])
706 : ' value="' . htmlspecialchars(isset($GLOBALS['new_username'])
707 ?
$GLOBALS['new_username']
708 : $GLOBALS['username']) . '"')
709 . ' onchange="pred_username.value = \'userdefined\';" />' . "\n"
711 . '<div class="item">' . "\n"
712 . '<label for="select_pred_hostname">' . "\n"
713 . ' ' . __('Host') . ':' . "\n"
715 . '<span class="options">' . "\n"
716 . ' <select name="pred_hostname" id="select_pred_hostname" title="' . __('Host') . '"' . "\n";
717 $_current_user = PMA_DBI_fetch_value('SELECT USER();');
718 if (! empty($_current_user)) {
719 $thishost = str_replace("'", '', substr($_current_user, (strrpos($_current_user, '@') +
1)));
720 if ($thishost == 'localhost' ||
$thishost == '127.0.0.1') {
724 echo ' onchange="if (this.value == \'any\') { hostname.value = \'%\'; } else if (this.value == \'localhost\') { hostname.value = \'localhost\'; } '
725 . (empty($thishost) ?
'' : 'else if (this.value == \'thishost\') { hostname.value = \'' . addslashes(htmlspecialchars($thishost)) . '\'; } ')
726 . 'else if (this.value == \'hosttable\') { hostname.value = \'\'; } else if (this.value == \'userdefined\') { hostname.focus(); hostname.select(); }">' . "\n";
727 unset($_current_user);
729 // when we start editing a user, $GLOBALS['pred_hostname'] is not defined
730 if (! isset($GLOBALS['pred_hostname']) && isset($GLOBALS['hostname'])) {
731 switch (strtolower($GLOBALS['hostname'])) {
734 $GLOBALS['pred_hostname'] = 'localhost';
737 $GLOBALS['pred_hostname'] = 'any';
740 $GLOBALS['pred_hostname'] = 'userdefined';
744 echo ' <option value="any"'
745 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'any')
746 ?
' selected="selected"' : '') . '>' . __('Any host')
748 . ' <option value="localhost"'
749 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'localhost')
750 ?
' selected="selected"' : '') . '>' . __('Local')
751 . '</option>' . "\n";
752 if (!empty($thishost)) {
753 echo ' <option value="thishost"'
754 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'thishost')
755 ?
' selected="selected"' : '') . '>' . __('This Host')
756 . '</option>' . "\n";
759 echo ' <option value="hosttable"'
760 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'hosttable')
761 ?
' selected="selected"' : '') . '>' . __('Use Host Table')
763 . ' <option value="userdefined"'
764 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'userdefined')
765 ?
' selected="selected"' : '')
766 . '>' . __('Use text field') . ':</option>' . "\n"
767 . ' </select>' . "\n"
769 . '<input type="text" name="hostname" maxlength="'
770 . $hostname_length . '" value="'
771 . htmlspecialchars(isset($GLOBALS['hostname']) ?
$GLOBALS['hostname'] : '')
772 . '" title="' . __('Host')
773 . '" onchange="pred_hostname.value = \'userdefined\';" />' . "\n"
774 . PMA_showHint(__('When Host table is used, this field is ignored and values stored in Host table are used instead.'))
776 . '<div class="item">' . "\n"
777 . '<label for="select_pred_password">' . "\n"
778 . ' ' . __('Password') . ':' . "\n"
780 . '<span class="options">' . "\n"
781 . ' <select name="pred_password" id="select_pred_password" title="'
782 . __('Password') . '"' . "\n"
783 . ' onchange="if (this.value == \'none\') { pma_pw.value = \'\'; pma_pw2.value = \'\'; } else if (this.value == \'userdefined\') { pma_pw.focus(); pma_pw.select(); }">' . "\n"
784 . ($mode == 'change' ?
' <option value="keep" selected="selected">' . __('Do not change the password') . '</option>' . "\n" : '')
785 . ' <option value="none"';
786 if (isset($GLOBALS['username']) && $mode != 'change') {
787 echo ' selected="selected"';
789 echo '>' . __('No Password') . '</option>' . "\n"
790 . ' <option value="userdefined"' . (isset($GLOBALS['username']) ?
'' : ' selected="selected"') . '>' . __('Use text field') . ':</option>' . "\n"
791 . ' </select>' . "\n"
793 . '<input type="password" id="text_pma_pw" name="pma_pw" title="' . __('Password') . '" onchange="pred_password.value = \'userdefined\';" />' . "\n"
795 . '<div class="item" id="div_element_before_generate_password">' . "\n"
796 . '<label for="text_pma_pw2">' . "\n"
797 . ' ' . __('Re-type') . ':' . "\n"
799 . '<span class="options"> </span>' . "\n"
800 . '<input type="password" name="pma_pw2" id="text_pma_pw2" title="' . __('Re-type') . '" onchange="pred_password.value = \'userdefined\';" />' . "\n"
802 // Generate password added here via jQuery
803 . '</fieldset>' . "\n";
804 } // end of the 'PMA_displayUserAndHostFields()' function
807 * Changes / copies a user, part I
809 if (isset($_REQUEST['change_copy'])) {
810 $user_host_condition =
812 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
814 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\';';
815 $row = PMA_DBI_fetch_single_row('SELECT * FROM `mysql`.`user` ' . $user_host_condition);
817 PMA_Message
::notice(__('No user found.'))->display();
818 unset($_REQUEST['change_copy']);
820 extract($row, EXTR_OVERWRITE
);
821 // Recent MySQL versions have the field "Password" in mysql.user,
822 // so the previous extract creates $Password but this script
824 if (! isset($password) && isset($Password)) {
825 $password = $Password;
834 * (Changes / copies a user, part II)
836 if (isset($_REQUEST['adduser_submit']) ||
isset($_REQUEST['change_copy'])) {
838 if ($pred_username == 'any') {
841 switch ($pred_hostname) {
846 $hostname = 'localhost';
852 $_user_name = PMA_DBI_fetch_value('SELECT USER()');
853 $hostname = substr($_user_name, (strrpos($_user_name, '@') +
1));
857 $sql = "SELECT '1' FROM `mysql`.`user`"
858 . " WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
859 . " AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
860 if (PMA_DBI_fetch_value($sql) == 1) {
861 $message = PMA_Message
::error(__('The user %s already exists!'));
862 $message->addParam('[i]\'' . $username . '\'@\'' . $hostname . '\'[/i]');
863 $_REQUEST['adduser'] = true;
864 $_add_user_error = true;
867 $create_user_real = 'CREATE USER \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
870 'GRANT ' . join(', ', PMA_extractPrivInfo()) . ' ON *.* TO \''
871 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
872 if ($pred_password != 'none' && $pred_password != 'keep') {
873 $sql_query = $real_sql_query . ' IDENTIFIED BY \'***\'';
874 $real_sql_query .= ' IDENTIFIED BY \'' . PMA_sqlAddSlashes($pma_pw) . '\'';
875 if (isset($create_user_real)) {
876 $create_user_show = $create_user_real . ' IDENTIFIED BY \'***\'';
877 $create_user_real .= ' IDENTIFIED BY \'' . PMA_sqlAddSlashes($pma_pw) . '\'';
880 if ($pred_password == 'keep' && !empty($password)) {
881 $real_sql_query .= ' IDENTIFIED BY PASSWORD \'' . $password . '\'';
882 if (isset($create_user_real)) {
883 $create_user_real .= ' IDENTIFIED BY PASSWORD \'' . $password . '\'';
886 $sql_query = $real_sql_query;
887 if (isset($create_user_real)) {
888 $create_user_show = $create_user_real;
892 * @todo similar code appears twice in this script
894 if ((isset($Grant_priv) && $Grant_priv == 'Y')
895 ||
(isset($max_questions) ||
isset($max_connections)
896 ||
isset($max_updates) ||
isset($max_user_connections))) {
897 $real_sql_query .= ' WITH';
898 $sql_query .= ' WITH';
899 if (isset($Grant_priv) && $Grant_priv == 'Y') {
900 $real_sql_query .= ' GRANT OPTION';
901 $sql_query .= ' GRANT OPTION';
903 if (isset($max_questions)) {
904 // avoid negative values
905 $max_questions = max(0, (int)$max_questions);
906 $real_sql_query .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
907 $sql_query .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
909 if (isset($max_connections)) {
910 $max_connections = max(0, (int)$max_connections);
911 $real_sql_query .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
912 $sql_query .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
914 if (isset($max_updates)) {
915 $max_updates = max(0, (int)$max_updates);
916 $real_sql_query .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
917 $sql_query .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
919 if (isset($max_user_connections)) {
920 $max_user_connections = max(0, (int)$max_user_connections);
921 $real_sql_query .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
922 $sql_query .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
925 if (isset($create_user_real)) {
926 $create_user_real .= ';';
927 $create_user_show .= ';';
929 $real_sql_query .= ';';
931 if (empty($_REQUEST['change_copy'])) {
934 if (isset($create_user_real)) {
935 if (! PMA_DBI_try_query($create_user_real)) {
938 $sql_query = $create_user_show . $sql_query;
941 if ($_error ||
! PMA_DBI_try_query($real_sql_query)) {
942 $_REQUEST['createdb'] = false;
943 $message = PMA_Message
::rawError(PMA_DBI_getError());
945 $message = PMA_Message
::success(__('You have added a new user.'));
948 switch (PMA_ifSetOr($_REQUEST['createdb'], '0')) {
950 // Create database with same name and grant all privileges
951 $q = 'CREATE DATABASE IF NOT EXISTS '
952 . PMA_backquote(PMA_sqlAddSlashes($username)) . ';';
954 if (! PMA_DBI_try_query($q)) {
955 $message = PMA_Message
::rawError(PMA_DBI_getError());
961 * If we are not in an Ajax request, we can't reload navigation now
963 if($GLOBALS['is_ajax_request'] != true) {
964 // this is needed in case tracking is on:
965 $GLOBALS['db'] = $username;
966 $GLOBALS['reload'] = true;
967 PMA_reloadNavigation();
970 $q = 'GRANT ALL PRIVILEGES ON '
971 . PMA_backquote(PMA_sqlAddSlashes($username)) . '.* TO \''
972 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
974 if (! PMA_DBI_try_query($q)) {
975 $message = PMA_Message
::rawError(PMA_DBI_getError());
979 // Grant all privileges on wildcard name (username\_%)
980 $q = 'GRANT ALL PRIVILEGES ON '
981 . PMA_backquote(PMA_sqlAddSlashes($username) . '\_%') . '.* TO \''
982 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
984 if (! PMA_DBI_try_query($q)) {
985 $message = PMA_Message
::rawError(PMA_DBI_getError());
989 // Grant all privileges on the specified database to the new user
990 $q = 'GRANT ALL PRIVILEGES ON '
991 . PMA_backquote(PMA_sqlAddSlashes($dbname)) . '.* TO \''
992 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
994 if (! PMA_DBI_try_query($q)) {
995 $message = PMA_Message
::rawError(PMA_DBI_getError());
1003 if (isset($create_user_real)) {
1004 $queries[] = $create_user_real;
1006 $queries[] = $real_sql_query;
1007 // we put the query containing the hidden password in
1008 // $queries_for_display, at the same position occupied
1009 // by the real query in $queries
1010 $tmp_count = count($queries);
1011 if (isset($create_user_real)) {
1012 $queries_for_display[$tmp_count - 2] = $create_user_show;
1014 $queries_for_display[$tmp_count - 1] = $sql_query;
1016 unset($res, $real_sql_query);
1022 * Changes / copies a user, part III
1024 if (isset($_REQUEST['change_copy'])) {
1025 $user_host_condition =
1027 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
1029 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\';';
1030 $res = PMA_DBI_query('SELECT * FROM `mysql`.`db`' . $user_host_condition);
1031 while ($row = PMA_DBI_fetch_assoc($res)) {
1033 'GRANT ' . join(', ', PMA_extractPrivInfo($row))
1034 .' ON ' . PMA_backquote($row['Db']) . '.*'
1035 .' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\''
1036 . ($row['Grant_priv'] == 'Y' ?
' WITH GRANT OPTION;' : ';');
1038 PMA_DBI_free_result($res);
1039 $res = PMA_DBI_query(
1040 'SELECT `Db`, `Table_name`, `Table_priv`'
1041 .' FROM `mysql`.`tables_priv`' . $user_host_condition,
1042 $GLOBALS['userlink'], PMA_DBI_QUERY_STORE
);
1043 while ($row = PMA_DBI_fetch_assoc($res)) {
1045 $res2 = PMA_DBI_QUERY(
1046 'SELECT `Column_name`, `Column_priv`'
1047 .' FROM `mysql`.`columns_priv`'
1049 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
1051 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\''
1053 .' = \'' . PMA_sqlAddSlashes($row['Db']) . "'"
1054 .' AND `Table_name`'
1055 .' = \'' . PMA_sqlAddSlashes($row['Table_name']) . "'"
1057 null, PMA_DBI_QUERY_STORE
);
1059 $tmp_privs1 = PMA_extractPrivInfo($row);
1060 $tmp_privs2 = array(
1061 'Select' => array(),
1062 'Insert' => array(),
1063 'Update' => array(),
1064 'References' => array()
1067 while ($row2 = PMA_DBI_fetch_assoc($res2)) {
1068 $tmp_array = explode(',', $row2['Column_priv']);
1069 if (in_array('Select', $tmp_array)) {
1070 $tmp_privs2['Select'][] = $row2['Column_name'];
1072 if (in_array('Insert', $tmp_array)) {
1073 $tmp_privs2['Insert'][] = $row2['Column_name'];
1075 if (in_array('Update', $tmp_array)) {
1076 $tmp_privs2['Update'][] = $row2['Column_name'];
1078 if (in_array('References', $tmp_array)) {
1079 $tmp_privs2['References'][] = $row2['Column_name'];
1083 if (count($tmp_privs2['Select']) > 0 && !in_array('SELECT', $tmp_privs1)) {
1084 $tmp_privs1[] = 'SELECT (`' . join('`, `', $tmp_privs2['Select']) . '`)';
1086 if (count($tmp_privs2['Insert']) > 0 && !in_array('INSERT', $tmp_privs1)) {
1087 $tmp_privs1[] = 'INSERT (`' . join('`, `', $tmp_privs2['Insert']) . '`)';
1089 if (count($tmp_privs2['Update']) > 0 && !in_array('UPDATE', $tmp_privs1)) {
1090 $tmp_privs1[] = 'UPDATE (`' . join('`, `', $tmp_privs2['Update']) . '`)';
1092 if (count($tmp_privs2['References']) > 0 && !in_array('REFERENCES', $tmp_privs1)) {
1093 $tmp_privs1[] = 'REFERENCES (`' . join('`, `', $tmp_privs2['References']) . '`)';
1097 'GRANT ' . join(', ', $tmp_privs1)
1098 . ' ON ' . PMA_backquote($row['Db']) . '.' . PMA_backquote($row['Table_name'])
1099 . ' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\''
1100 . (in_array('Grant', explode(',', $row['Table_priv'])) ?
' WITH GRANT OPTION;' : ';');
1106 * Updates privileges
1108 if (!empty($update_privs)) {
1109 $db_and_table = PMA_WildcardEscapeForGrant($db_and_table, $dbname, (isset($tablename) ?
$tablename : ''));
1112 'REVOKE ALL PRIVILEGES ON ' . $db_and_table
1113 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1114 if (! isset($Grant_priv) ||
$Grant_priv != 'Y') {
1116 'REVOKE GRANT OPTION ON ' . $db_and_table
1117 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1122 // Should not do a GRANT USAGE for a table-specific privilege, it
1123 // causes problems later (cannot revoke it)
1124 if (! (isset($tablename) && 'USAGE' == implode('', PMA_extractPrivInfo()))) {
1126 'GRANT ' . join(', ', PMA_extractPrivInfo())
1127 . ' ON ' . $db_and_table
1128 . ' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
1131 * @todo similar code appears twice in this script
1133 if ((isset($Grant_priv) && $Grant_priv == 'Y')
1134 ||
(! isset($dbname)
1135 && (isset($max_questions) ||
isset($max_connections)
1136 ||
isset($max_updates) ||
isset($max_user_connections))))
1138 $sql_query2 .= 'WITH';
1139 if (isset($Grant_priv) && $Grant_priv == 'Y') {
1140 $sql_query2 .= ' GRANT OPTION';
1142 if (isset($max_questions)) {
1143 $max_questions = max(0, (int)$max_questions);
1144 $sql_query2 .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
1146 if (isset($max_connections)) {
1147 $max_connections = max(0, (int)$max_connections);
1148 $sql_query2 .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
1150 if (isset($max_updates)) {
1151 $max_updates = max(0, (int)$max_updates);
1152 $sql_query2 .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
1154 if (isset($max_user_connections)) {
1155 $max_user_connections = max(0, (int)$max_user_connections);
1156 $sql_query2 .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
1161 if (! PMA_DBI_try_query($sql_query0)) {
1162 // This might fail when the executing user does not have ALL PRIVILEGES himself.
1163 // See https://sourceforge.net/tracker/index.php?func=detail&aid=3285929&group_id=23067&atid=377408
1166 if (isset($sql_query1) && !PMA_DBI_try_query($sql_query1)) {
1167 // this one may fail, too...
1170 if (isset($sql_query2)) {
1171 PMA_DBI_query($sql_query2);
1175 $sql_query = $sql_query0 . ' ' . $sql_query1 . ' ' . $sql_query2;
1176 $message = PMA_Message
::success(__('You have updated the privileges for %s.'));
1177 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1182 * Revokes Privileges
1184 if (isset($_REQUEST['revokeall'])) {
1185 $db_and_table = PMA_WildcardEscapeForGrant($db_and_table, $dbname, isset($tablename) ?
$tablename : '');
1188 'REVOKE ALL PRIVILEGES ON ' . $db_and_table
1189 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1191 'REVOKE GRANT OPTION ON ' . $db_and_table
1192 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1194 PMA_DBI_query($sql_query0);
1195 if (! PMA_DBI_try_query($sql_query1)) {
1196 // this one may fail, too...
1199 $sql_query = $sql_query0 . ' ' . $sql_query1;
1200 $message = PMA_Message
::success(__('You have revoked the privileges for %s'));
1201 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1202 if (! isset($tablename)) {
1211 * Updates the password
1213 if (isset($_REQUEST['change_pw'])) {
1214 // similar logic in user_password.php
1217 if ($nopass == 0 && isset($pma_pw) && isset($pma_pw2)) {
1218 if ($pma_pw != $pma_pw2) {
1219 $message = PMA_Message
::error(__('The passwords aren\'t the same!'));
1220 } elseif (empty($pma_pw) ||
empty($pma_pw2)) {
1221 $message = PMA_Message
::error(__('The password is empty!'));
1225 // here $nopass could be == 1
1226 if (empty($message)) {
1228 $hashing_function = (!empty($pw_hash) && $pw_hash == 'old' ?
'OLD_' : '')
1231 // in $sql_query which will be displayed, hide the password
1232 $sql_query = 'SET PASSWORD FOR \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\' = ' . (($pma_pw == '') ?
'\'\'' : $hashing_function . '(\'' . preg_replace('@.@s', '*', $pma_pw) . '\')');
1233 $local_query = 'SET PASSWORD FOR \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\' = ' . (($pma_pw == '') ?
'\'\'' : $hashing_function . '(\'' . PMA_sqlAddSlashes($pma_pw) . '\')');
1234 PMA_DBI_try_query($local_query)
1235 or PMA_mysqlDie(PMA_DBI_getError(), $sql_query, false, $err_url);
1236 $message = PMA_Message
::success(__('The password for %s was changed successfully.'));
1237 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1244 * (Changes / copies a user, part IV)
1247 if (isset($_REQUEST['delete']) ||
(isset($_REQUEST['change_copy']) && $_REQUEST['mode'] < 4)) {
1248 if (isset($_REQUEST['change_copy'])) {
1249 $selected_usr = array($old_username . '&#27;' . $old_hostname);
1251 $selected_usr = $_REQUEST['selected_usr'];
1254 foreach ($selected_usr as $each_user) {
1255 list($this_user, $this_host) = explode('&#27;', $each_user);
1256 $queries[] = '# ' . sprintf(__('Deleting %s'), '\'' . $this_user . '\'@\'' . $this_host . '\'') . ' ...';
1257 $queries[] = 'DROP USER \'' . PMA_sqlAddSlashes($this_user) . '\'@\'' . PMA_sqlAddSlashes($this_host) . '\';';
1259 if (isset($_REQUEST['drop_users_db'])) {
1260 $queries[] = 'DROP DATABASE IF EXISTS ' . PMA_backquote($this_user) . ';';
1261 $GLOBALS['reload'] = true;
1263 if($GLOBALS['is_ajax_request'] != true) {
1264 PMA_reloadNavigation();
1268 if (empty($_REQUEST['change_copy'])) {
1269 if (empty($queries)) {
1270 $message = PMA_Message
::error(__('No users selected for deleting!'));
1272 if ($_REQUEST['mode'] == 3) {
1273 $queries[] = '# ' . __('Reloading the privileges') . ' ...';
1274 $queries[] = 'FLUSH PRIVILEGES;';
1276 $drop_user_error = '';
1277 foreach ($queries as $sql_query) {
1278 if ($sql_query{0} != '#') {
1279 if (! PMA_DBI_try_query($sql_query, $GLOBALS['userlink'])) {
1280 $drop_user_error .= PMA_DBI_getError() . "\n";
1284 // tracking sets this, causing the deleted db to be shown in navi
1285 unset($GLOBALS['db']);
1287 $sql_query = join("\n", $queries);
1288 if (! empty($drop_user_error)) {
1289 $message = PMA_Message
::rawError($drop_user_error);
1291 $message = PMA_Message
::success(__('The selected users have been deleted successfully.'));
1300 * Changes / copies a user, part V
1302 if (isset($_REQUEST['change_copy'])) {
1304 foreach ($queries as $sql_query) {
1305 if ($sql_query{0} != '#') {
1306 PMA_DBI_query($sql_query);
1308 // when there is a query containing a hidden password, take it
1309 // instead of the real query sent
1310 if (isset($queries_for_display[$tmp_count])) {
1311 $queries[$tmp_count] = $queries_for_display[$tmp_count];
1315 $message = PMA_Message
::success();
1316 $sql_query = join("\n", $queries);
1321 * Reloads the privilege tables into memory
1323 if (isset($_REQUEST['flush_privileges'])) {
1324 $sql_query = 'FLUSH PRIVILEGES;';
1325 PMA_DBI_query($sql_query);
1326 $message = PMA_Message
::success(__('The privileges were reloaded successfully.'));
1330 * defines some standard links
1332 $link_edit = '<a class="edit_user_anchor ' . $conditional_class . '" href="server_privileges.php?' . $GLOBALS['url_query']
1333 . '&username=%s'
1334 . '&hostname=%s'
1336 . '&tablename=%s">'
1337 . PMA_getIcon('b_usredit.png', __('Edit Privileges'))
1340 $link_revoke = '<a href="server_privileges.php?' . $GLOBALS['url_query']
1341 . '&username=%s'
1342 . '&hostname=%s'
1344 . '&tablename=%s'
1345 . '&revokeall=1">'
1346 . PMA_getIcon('b_usrdrop.png', __('Revoke'))
1349 $link_export = '<a class="export_user_anchor ' . $conditional_class . '" href="server_privileges.php?' . $GLOBALS['url_query']
1350 . '&username=%s'
1351 . '&hostname=%s'
1354 . PMA_getIcon('b_tblexport.png', __('Export'))
1358 * If we are in an Ajax request for Create User/Edit User/Revoke User/Flush Privileges,
1359 * show $message and exit.
1361 if( $GLOBALS['is_ajax_request'] && ! isset($_REQUEST['export']) && (! isset($_REQUEST['adduser']) ||
$_add_user_error) && ! isset($_REQUEST['initial']) && ! isset($_REQUEST['showall']) && ! isset($_REQUEST['edit_user_dialog']) && ! isset($_REQUEST['db_specific'])) {
1363 if(isset($sql_query)) {
1364 $extra_data['sql_query'] = PMA_showMessage(NULL, $sql_query);
1367 if(isset($_REQUEST['adduser_submit']) ||
isset($_REQUEST['change_copy'])) {
1369 * generate html on the fly for the new user that was just created.
1371 $new_user_string = '<tr>'."\n"
1372 .'<td> <input type="checkbox" name="selected_usr[]" id="checkbox_sel_users_" value="' . htmlspecialchars($username) . '&#27;' . htmlspecialchars($hostname) . '" /> </td>'."\n"
1373 .'<td><label for="checkbox_sel_users_">' . (empty($username) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($username) ) . '</label></td>' . "\n"
1374 .'<td>' . htmlspecialchars($hostname) . '</td>' . "\n";
1375 $new_user_string .= '<td>';
1377 if(!empty($password) ||
isset($pma_pw)) {
1378 $new_user_string .= __('Yes');
1381 $new_user_string .= '<span style="color: #FF0000">' . __('No') . '</span>';
1384 $new_user_string .= '</td>'."\n";
1385 $new_user_string .= '<td><tt>' . join(', ', PMA_extractPrivInfo('', true)) . '</tt></td>'; //Fill in privileges here
1386 $new_user_string .= '<td>';
1388 if((isset($Grant_priv) && $Grant_priv == 'Y')) {
1389 $new_user_string .= __('Yes');
1392 $new_user_string .= __('No');
1395 $new_user_string .='</td>';
1397 $new_user_string .= '<td>'.sprintf($link_edit, urlencode($username), urlencode($hostname), '', '' ).'</td>'."\n";
1398 $new_user_string .= '<td>'.sprintf($link_export, urlencode($username), urlencode($hostname), (isset($initial) ?
$initial : '')).'</td>'."\n";
1400 $new_user_string .= '</tr>';
1402 $extra_data['new_user_string'] = $new_user_string;
1405 * Generate the string for this alphabet's initial, to update the user
1408 $new_user_initial = strtoupper(substr($username, 0, 1));
1409 $new_user_initial_string = '<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&initial=' . $new_user_initial
1410 .'">' . $new_user_initial . '</a>';
1411 $extra_data['new_user_initial'] = $new_user_initial;
1412 $extra_data['new_user_initial_string'] = $new_user_initial_string;
1415 if(isset($update_privs)) {
1416 $extra_data['db_specific_privs'] = false;
1417 if (isset($dbname_is_wildcard)) {
1418 $extra_data['db_specific_privs'] = !$dbname_is_wildcard;
1420 $new_privileges = join(', ', PMA_extractPrivInfo('', true));
1422 $extra_data['new_privileges'] = $new_privileges;
1425 if ($message instanceof PMA_Message
) {
1426 PMA_ajaxResponse($message, $message->isSuccess(), $extra_data);
1431 * Displays the links
1433 if (isset($viewing_mode) && $viewing_mode == 'db') {
1435 $url_query .= '&goto=db_operations.php';
1437 // Gets the database structure
1438 $sub_part = '_structure';
1439 require './libraries/db_info.inc.php';
1442 require './libraries/server_links.inc.php';
1450 // export user definition
1451 if (isset($_REQUEST['export'])) {
1452 echo '<h2>' . __('User') . ' \'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'</h2>';
1453 echo '<textarea cols="' . $GLOBALS['cfg']['TextareaCols'] . '" rows="' . $GLOBALS['cfg']['TextareaRows'] . '">';
1454 $grants = PMA_DBI_fetch_result("SHOW GRANTS FOR '" . PMA_sqlAddSlashes($username) . "'@'" . PMA_sqlAddSlashes($hostname) . "'");
1455 foreach($grants as $one_grant) {
1456 echo $one_grant . ";\n\n";
1459 unset($username, $hostname, $grants, $one_grant);
1460 if( $GLOBALS['is_ajax_request']) {
1465 if (empty($_REQUEST['adduser']) && (! isset($checkprivs) ||
! strlen($checkprivs))) {
1466 if (! isset($username)) {
1467 // No username is given --> display the overview
1469 . PMA_getIcon('b_usrlist.png')
1470 . __('User overview') . "\n"
1475 " IF(`Password` = _latin1 '', 'N', 'Y') AS 'Password'" .
1476 ' FROM `mysql`.`user`';
1478 $sql_query .= (isset($initial) ?
PMA_RangeOfUsers($initial) : '');
1480 $sql_query .= ' ORDER BY `User` ASC, `Host` ASC;';
1481 $res = PMA_DBI_try_query($sql_query, null, PMA_DBI_QUERY_STORE
);
1484 // the query failed! This may have two reasons:
1485 // - the user does not have enough privileges
1486 // - the privilege tables use a structure of an earlier version.
1487 // so let's try a more simple query
1489 $sql_query = 'SELECT * FROM `mysql`.`user`';
1490 $res = PMA_DBI_try_query($sql_query, null, PMA_DBI_QUERY_STORE
);
1493 PMA_Message
::error(__('No Privileges'))->display();
1494 PMA_DBI_free_result($res);
1497 // This message is hardcoded because I will replace it by
1498 // a automatic repair feature soon.
1499 $raw = 'Your privilege table structure seems to be older than'
1500 . ' this MySQL version!<br />'
1501 . 'Please run the <tt>mysql_upgrade</tt> command'
1502 . '(<tt>mysql_fix_privilege_tables</tt> on older systems)'
1503 . ' that should be included in your MySQL server distribution'
1504 . ' to solve this problem!';
1505 PMA_Message
::rawError($raw)->display();
1509 // we also want users not in table `user` but in other table
1510 $tables = PMA_DBI_fetch_result('SHOW TABLES FROM `mysql`;');
1512 $tables_to_search_for_users = array(
1513 'user', 'db', 'tables_priv', 'columns_priv', 'procs_priv',
1516 $db_rights_sqls = array();
1517 foreach ($tables_to_search_for_users as $table_search_in) {
1518 if (in_array($table_search_in, $tables)) {
1519 $db_rights_sqls[] = 'SELECT DISTINCT `User`, `Host` FROM `mysql`.`' . $table_search_in . '` ' . (isset($initial) ?
PMA_RangeOfUsers($initial) : '');
1523 $user_defaults = array(
1527 'Grant_priv' => 'N',
1528 'privs' => array('USAGE'),
1531 // for all initials, even non A-Z
1532 $array_initials = array();
1534 $db_rights = array();
1536 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
1537 .' ORDER BY `User` ASC, `Host` ASC';
1539 $db_rights_result = PMA_DBI_query($db_rights_sql);
1541 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
1542 $db_rights_row = array_merge($user_defaults, $db_rights_row);
1543 $db_rights[$db_rights_row['User']][$db_rights_row['Host']] =
1546 PMA_DBI_free_result($db_rights_result);
1547 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
1551 * Displays the initials
1552 * In an Ajax request, we don't need to show this. Also not necassary if there is less than 20 privileges
1555 if( $GLOBALS['is_ajax_request'] != true && PMA_DBI_num_rows($res) > 20 ) {
1557 // initialize to false the letters A-Z
1558 for ($letter_counter = 1; $letter_counter < 27; $letter_counter++
) {
1559 if (! isset($array_initials[chr($letter_counter +
64)])) {
1560 $array_initials[chr($letter_counter +
64)] = false;
1564 $initials = PMA_DBI_try_query('SELECT DISTINCT UPPER(LEFT(`User`,1)) FROM `user` ORDER BY `User` ASC', null, PMA_DBI_QUERY_STORE
);
1565 while (list($tmp_initial) = PMA_DBI_fetch_row($initials)) {
1566 $array_initials[$tmp_initial] = true;
1569 // Display the initials, which can be any characters, not
1570 // just letters. For letters A-Z, we add the non-used letters
1573 uksort($array_initials, "strnatcasecmp");
1575 echo '<table id="initials_table" class="' . $conditional_class . '" <cellspacing="5"><tr>';
1576 foreach ($array_initials as $tmp_initial => $initial_was_found) {
1577 if ($initial_was_found) {
1578 echo '<td><a href="server_privileges.php?' . $GLOBALS['url_query'] . '&initial=' . urlencode($tmp_initial) . '">' . $tmp_initial . '</a></td>' . "\n";
1580 echo '<td>' . $tmp_initial . '</td>';
1583 echo '<td><a href="server_privileges.php?' . $GLOBALS['url_query'] . '&showall=1" class="nowrap">[' . __('Show all') . ']</a></td>' . "\n";
1584 echo '</tr></table>';
1588 * Display the user overview
1589 * (if less than 50 users, display them immediately)
1592 if (isset($initial) ||
isset($showall) ||
PMA_DBI_num_rows($res) < 50) {
1594 while ($row = PMA_DBI_fetch_assoc($res)) {
1595 $row['privs'] = PMA_extractPrivInfo($row, true);
1596 $db_rights[$row['User']][$row['Host']] = $row;
1598 @PMA_DBI_free_result
($res);
1601 echo '<form name="usersForm" id="usersForm" action="server_privileges.php" method="post">' . "\n"
1602 . PMA_generate_common_hidden_inputs('', '')
1603 . ' <table id="tableuserrights" class="data">' . "\n"
1605 . ' <tr><th></th>' . "\n"
1606 . ' <th>' . __('User') . '</th>' . "\n"
1607 . ' <th>' . __('Host') . '</th>' . "\n"
1608 . ' <th>' . __('Password') . '</th>' . "\n"
1609 . ' <th>' . __('Global privileges') . ' '
1610 . PMA_showHint(__(' Note: MySQL privilege names are expressed in English ')) . '</th>' . "\n"
1611 . ' <th>' . __('Grant') . '</th>' . "\n"
1612 . ' <th colspan="2">' . __('Action') . '</th>' . "\n";
1613 echo ' </tr>' . "\n";
1614 echo ' </thead>' . "\n";
1615 echo ' <tbody>' . "\n";
1617 $index_checkbox = -1;
1618 foreach ($db_rights as $user) {
1621 foreach ($user as $host) {
1623 echo ' <tr class="' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
1624 . ' <td><input type="checkbox" name="selected_usr[]" id="checkbox_sel_users_'
1625 . $index_checkbox . '" value="'
1626 . htmlspecialchars($host['User'] . '&#27;' . $host['Host'])
1628 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
1630 . ' <td><label for="checkbox_sel_users_' . $index_checkbox . '">' . (empty($host['User']) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($host['User'])) . '</label></td>' . "\n"
1631 . ' <td>' . htmlspecialchars($host['Host']) . '</td>' . "\n";
1633 switch ($host['Password']) {
1638 echo '<span style="color: #FF0000">' . __('No') . '</span>';
1640 // this happens if this is a definition not coming from mysql.user
1642 echo '--'; // in future version, replace by "not present"
1646 . ' <td><tt>' . "\n"
1647 . ' ' . implode(',' . "\n" . ' ', $host['privs']) . "\n"
1648 . ' </tt></td>' . "\n"
1649 . ' <td>' . ($host['Grant_priv'] == 'Y' ?
__('Yes') : __('No')) . '</td>' . "\n"
1650 . ' <td align="center">';
1651 printf($link_edit, urlencode($host['User']), urlencode($host['Host']), '', '');
1653 echo '<td align="center">';
1654 printf($link_export, urlencode($host['User']), urlencode($host['Host']), (isset($initial) ?
$initial : ''));
1657 $odd_row = ! $odd_row;
1661 unset($user, $host, $odd_row);
1662 echo ' </tbody></table>' . "\n"
1663 .'<img class="selectallarrow"'
1664 .' src="' . $pmaThemeImage . 'arrow_' . $text_dir . '.png"'
1665 .' width="38" height="22"'
1666 .' alt="' . __('With selected:') . '" />' . "\n"
1667 .'<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&checkall=1"'
1668 .' onclick="if (markAllRows(\'usersForm\')) return false;">'
1669 . __('Check All') . '</a>' . "\n"
1671 .'<a href="server_privileges.php?' . $GLOBALS['url_query'] . '"'
1672 .' onclick="if (unMarkAllRows(\'usersForm\')) return false;">'
1673 . __('Uncheck All') . '</a>' . "\n";
1675 // add/delete user fieldset
1676 echo ' <fieldset id="fieldset_add_user">' . "\n"
1677 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1" class="' . $conditional_class . '">' . "\n"
1678 . PMA_getIcon('b_usradd.png')
1679 . ' ' . __('Add user') . '</a>' . "\n"
1680 . ' </fieldset>' . "\n"
1681 . ' <fieldset id="fieldset_delete_user">'
1682 . ' <legend>' . "\n"
1683 . PMA_getIcon('b_usrdrop.png')
1684 . ' ' . __('Remove selected users') . '' . "\n"
1685 . ' </legend>' . "\n"
1686 . ' <input type="hidden" name="mode" value="2" />' . "\n"
1687 . '(' . __('Revoke all active privileges from the users and delete them afterwards.') . ')<br />' . "\n"
1688 . ' <input type="checkbox" title="' . __('Drop the databases that have the same names as the users.') . '" name="drop_users_db" id="checkbox_drop_users_db" />' . "\n"
1689 . ' <label for="checkbox_drop_users_db" title="' . __('Drop the databases that have the same names as the users.') . '">' . "\n"
1690 . ' ' . __('Drop the databases that have the same names as the users.') . "\n"
1691 . ' </label>' . "\n"
1692 . ' </fieldset>' . "\n"
1693 . ' <fieldset id="fieldset_delete_user_footer" class="tblFooters">' . "\n"
1694 . ' <input type="submit" name="delete" value="' . __('Go') . '" id="buttonGo" class="' . $conditional_class . '"/>' . "\n"
1695 . ' </fieldset>' . "\n"
1700 echo ' <fieldset id="fieldset_add_user">' . "\n"
1701 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1" class="' . $conditional_class . '">' . "\n"
1702 . PMA_getIcon('b_usradd.png')
1703 . ' ' . __('Add user') . '</a>' . "\n"
1704 . ' </fieldset>' . "\n";
1705 } // end if (display overview)
1707 if( $GLOBALS['is_ajax_request'] ) {
1711 $flushnote = new PMA_Message(__('Note: phpMyAdmin gets the users\' privileges directly from MySQL\'s privilege tables. The content of these tables may differ from the privileges the server uses, if they have been changed manually. In this case, you should %sreload the privileges%s before you continue.'), PMA_Message
::NOTICE
);
1712 $flushnote->addParam('<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&flush_privileges=1" id="reload_privileges_anchor" class="' . $conditional_class . '">', false);
1713 $flushnote->addParam('</a>', false);
1714 $flushnote->display();
1720 // A user was selected -> display the user's properties
1722 // In an Ajax request, prevent cached values from showing
1723 if($GLOBALS['is_ajax_request'] == true) {
1724 header('Cache-Control: no-cache');
1728 . PMA_getIcon('b_usredit.png')
1729 . __('Edit Privileges') . ': '
1732 if (isset($dbname)) {
1733 echo ' <i><a href="server_privileges.php?'
1734 . $GLOBALS['url_query'] . '&username=' . htmlspecialchars(urlencode($username))
1735 . '&hostname=' . htmlspecialchars(urlencode($hostname)) . '&dbname=&tablename=">\''
1736 . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname)
1737 . '\'</a></i>' . "\n";
1738 $url_dbname = urlencode(str_replace(array('\_', '\%'), array('_', '%'), $dbname));
1740 echo ' - ' . ($dbname_is_wildcard ?
__('Databases') : __('Database') );
1741 if (isset($tablename)) {
1742 echo ' <i><a href="server_privileges.php?' . $GLOBALS['url_query']
1743 . '&username=' . htmlspecialchars(urlencode($username)) . '&hostname=' . htmlspecialchars(urlencode($hostname))
1744 . '&dbname=' . htmlspecialchars($url_dbname) . '&tablename=">' . htmlspecialchars($dbname) . '</a></i>';
1745 echo ' - ' . __('Table') . ' <i>' . htmlspecialchars($tablename) . '</i>';
1747 echo ' <i>' . htmlspecialchars($dbname) . '</i>';
1751 echo ' <i>\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname)
1755 echo '</h2>' . "\n";
1758 $sql = "SELECT '1' FROM `mysql`.`user`"
1759 . " WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
1760 . " AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
1761 $user_does_not_exists = (bool) ! PMA_DBI_fetch_value($sql);
1763 if ($user_does_not_exists) {
1764 PMA_Message
::error(__('The selected user was not found in the privilege table.'))->display();
1765 PMA_displayLoginInformationFields();
1766 //require './libraries/footer.inc.php';
1769 echo '<form name="usersForm" id="addUsersForm_' . $random_n . '" action="server_privileges.php" method="post">' . "\n";
1771 'username' => $username,
1772 'hostname' => $hostname,
1774 if (isset($dbname)) {
1775 $_params['dbname'] = $dbname;
1776 if (isset($tablename)) {
1777 $_params['tablename'] = $tablename;
1780 echo PMA_generate_common_hidden_inputs($_params);
1782 PMA_displayPrivTable(PMA_ifSetOr($dbname, '*', 'length'),
1783 PMA_ifSetOr($tablename, '*', 'length'));
1785 echo '</form>' . "\n";
1787 if (! isset($tablename) && empty($dbname_is_wildcard)) {
1789 // no table name was given, display all table specific rights
1790 // but only if $dbname contains no wildcards
1793 echo '<form action="server_privileges.php" method="post">' . "\n"
1794 . PMA_generate_common_hidden_inputs('', '')
1795 . '<input type="hidden" name="username" value="' . htmlspecialchars($username) . '" />' . "\n"
1796 . '<input type="hidden" name="hostname" value="' . htmlspecialchars($hostname) . '" />' . "\n"
1797 . '<fieldset>' . "\n"
1798 . '<legend>' . (! isset($dbname) ?
__('Database-specific privileges') : __('Table-specific privileges')) . '</legend>' . "\n"
1799 . '<table class="data">' . "\n"
1801 . '<tr><th>' . (! isset($dbname) ?
__('Database') : __('Table')) . '</th>' . "\n"
1802 . ' <th>' . __('Privileges') . '</th>' . "\n"
1803 . ' <th>' . __('Grant') . '</th>' . "\n"
1804 . ' <th>' . (! isset($dbname) ?
__('Table-specific privileges') : __('Column-specific privileges')) . '</th>' . "\n"
1805 . ' <th colspan="2">' . __('Action') . '</th>' . "\n"
1810 $user_host_condition =
1812 . ' = \'' . PMA_sqlAddSlashes($username) . "'"
1814 . ' = \'' . PMA_sqlAddSlashes($hostname) . "'";
1819 // we also want privielgs for this user not in table `db` but in other table
1820 $tables = PMA_DBI_fetch_result('SHOW TABLES FROM `mysql`;');
1821 if (! isset($dbname)) {
1823 // no db name given, so we want all privs for the given user
1825 $tables_to_search_for_users = array(
1826 'tables_priv', 'columns_priv',
1829 $db_rights_sqls = array();
1830 foreach ($tables_to_search_for_users as $table_search_in) {
1831 if (in_array($table_search_in, $tables)) {
1832 $db_rights_sqls[] = '
1833 SELECT DISTINCT `Db`
1834 FROM `mysql`.' . PMA_backquote($table_search_in)
1835 . $user_host_condition;
1839 $user_defaults = array(
1841 'Grant_priv' => 'N',
1842 'privs' => array('USAGE'),
1843 'Table_privs' => true,
1847 $db_rights = array();
1849 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
1850 .' ORDER BY `Db` ASC';
1852 $db_rights_result = PMA_DBI_query($db_rights_sql);
1854 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
1855 $db_rights_row = array_merge($user_defaults, $db_rights_row);
1856 // only Db names in the table `mysql`.`db` uses wildcards
1857 // as we are in the db specific rights display we want
1858 // all db names escaped, also from other sources
1859 $db_rights_row['Db'] = PMA_escape_mysql_wildcards(
1860 $db_rights_row['Db']);
1861 $db_rights[$db_rights_row['Db']] = $db_rights_row;
1864 PMA_DBI_free_result($db_rights_result);
1865 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
1867 $sql_query = 'SELECT * FROM `mysql`.`db`' . $user_host_condition . ' ORDER BY `Db` ASC';
1868 $res = PMA_DBI_query($sql_query);
1871 while ($row = PMA_DBI_fetch_assoc($res)) {
1872 if (isset($db_rights[$row['Db']])) {
1873 $db_rights[$row['Db']] = array_merge($db_rights[$row['Db']], $row);
1875 $db_rights[$row['Db']] = $row;
1877 // there are db specific rights for this user
1878 // so we can drop this db rights
1879 $db_rights[$row['Db']]['can_delete'] = true;
1881 PMA_DBI_free_result($res);
1886 // db name was given,
1887 // so we want all user specific rights for this db
1889 $user_host_condition .=
1891 .' LIKE \'' . PMA_sqlAddSlashes($dbname, true) . "'";
1893 $tables_to_search_for_users = array(
1897 $db_rights_sqls = array();
1898 foreach ($tables_to_search_for_users as $table_search_in) {
1899 if (in_array($table_search_in, $tables)) {
1900 $db_rights_sqls[] = '
1901 SELECT DISTINCT `Table_name`
1902 FROM `mysql`.' . PMA_backquote($table_search_in)
1903 . $user_host_condition;
1907 $user_defaults = array(
1909 'Grant_priv' => 'N',
1910 'privs' => array('USAGE'),
1911 'Column_priv' => true,
1915 $db_rights = array();
1917 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
1918 .' ORDER BY `Table_name` ASC';
1920 $db_rights_result = PMA_DBI_query($db_rights_sql);
1922 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
1923 $db_rights_row = array_merge($user_defaults, $db_rights_row);
1924 $db_rights[$db_rights_row['Table_name']] = $db_rights_row;
1926 PMA_DBI_free_result($db_rights_result);
1927 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
1930 'SELECT `Table_name`,'
1932 .' IF(`Column_priv` = _latin1 \'\', 0, 1)'
1933 .' AS \'Column_priv\''
1934 .' FROM `mysql`.`tables_priv`'
1935 . $user_host_condition
1936 .' ORDER BY `Table_name` ASC;';
1937 $res = PMA_DBI_query($sql_query);
1940 while ($row = PMA_DBI_fetch_assoc($res)) {
1941 if (isset($db_rights[$row['Table_name']])) {
1942 $db_rights[$row['Table_name']] = array_merge($db_rights[$row['Table_name']], $row);
1944 $db_rights[$row['Table_name']] = $row;
1947 PMA_DBI_free_result($res);
1953 if (count($db_rights) < 1) {
1954 echo '<tr class="odd">' . "\n"
1955 . ' <td colspan="6"><center><i>' . __('None') . '</i></center></td>' . "\n"
1959 $found_rows = array();
1960 //while ($row = PMA_DBI_fetch_assoc($res)) {
1961 foreach ($db_rights as $row) {
1962 $found_rows[] = (! isset($dbname)) ?
$row['Db'] : $row['Table_name'];
1964 echo '<tr class="' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
1965 . ' <td>' . htmlspecialchars((! isset($dbname)) ?
$row['Db'] : $row['Table_name']) . '</td>' . "\n"
1966 . ' <td><tt>' . "\n"
1967 . ' ' . join(',' . "\n" . ' ', PMA_extractPrivInfo($row, true)) . "\n"
1968 . ' </tt></td>' . "\n"
1969 . ' <td>' . ((((! isset($dbname)) && $row['Grant_priv'] == 'Y') ||
(isset($dbname) && in_array('Grant', explode(',', $row['Table_priv'])))) ?
__('Yes') : __('No')) . '</td>' . "\n"
1971 if (! empty($row['Table_privs']) ||
! empty ($row['Column_priv'])) {
1978 printf($link_edit, htmlspecialchars(urlencode($username)),
1979 urlencode(htmlspecialchars($hostname)),
1980 urlencode((! isset($dbname)) ?
$row['Db'] : htmlspecialchars($dbname)),
1981 urlencode((! isset($dbname)) ?
'' : $row['Table_name']));
1984 if (! empty($row['can_delete']) ||
isset($row['Table_name']) && strlen($row['Table_name'])) {
1985 printf($link_revoke, htmlspecialchars(urlencode($username)),
1986 urlencode(htmlspecialchars($hostname)),
1987 urlencode((! isset($dbname)) ?
$row['Db'] : htmlspecialchars($dbname)),
1988 urlencode((! isset($dbname)) ?
'' : $row['Table_name']));
1992 $odd_row = ! $odd_row;
1996 echo '</tbody>' . "\n"
1997 . '</table>' . "\n";
1999 if (! isset($dbname)) {
2001 // no database name was given, display select db
2003 $pred_db_array =PMA_DBI_fetch_result('SHOW DATABASES;');
2005 echo ' <label for="text_dbname">' . __('Add privileges on the following database') . ':</label>' . "\n";
2006 if (!empty($pred_db_array)) {
2007 echo ' <select name="pred_dbname" onchange="this.form.submit();">' . "\n"
2008 . ' <option value="" selected="selected">' . __('Use text field') . ':</option>' . "\n";
2009 foreach ($pred_db_array as $current_db) {
2010 $current_db = PMA_escape_mysql_wildcards($current_db);
2011 // cannot use array_diff() once, outside of the loop,
2012 // because the list of databases has special characters
2013 // already escaped in $found_rows,
2014 // contrary to the output of SHOW DATABASES
2015 if (empty($found_rows) ||
! in_array($current_db, $found_rows)) {
2016 echo ' <option value="' . htmlspecialchars($current_db) . '">'
2017 . htmlspecialchars($current_db) . '</option>' . "\n";
2020 echo ' </select>' . "\n";
2022 echo ' <input type="text" id="text_dbname" name="dbname" />' . "\n"
2023 . PMA_showHint(__('Wildcards % and _ should be escaped with a \ to use them literally'));
2025 echo ' <input type="hidden" name="dbname" value="' . htmlspecialchars($dbname) . '"/>' . "\n"
2026 . ' <label for="text_tablename">' . __('Add privileges on the following table') . ':</label>' . "\n";
2027 if ($res = @PMA_DBI_try_query
('SHOW TABLES FROM ' . PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . ';', null, PMA_DBI_QUERY_STORE
)) {
2028 $pred_tbl_array = array();
2029 while ($row = PMA_DBI_fetch_row($res)) {
2030 if (! isset($found_rows) ||
!in_array($row[0], $found_rows)) {
2031 $pred_tbl_array[] = $row[0];
2034 PMA_DBI_free_result($res);
2036 if (!empty($pred_tbl_array)) {
2037 echo ' <select name="pred_tablename" onchange="this.form.submit();">' . "\n"
2038 . ' <option value="" selected="selected">' . __('Use text field') . ':</option>' . "\n";
2039 foreach ($pred_tbl_array as $current_table) {
2040 echo ' <option value="' . htmlspecialchars($current_table) . '">' . htmlspecialchars($current_table) . '</option>' . "\n";
2042 echo ' </select>' . "\n";
2047 echo ' <input type="text" id="text_tablename" name="tablename" />' . "\n";
2049 echo '</fieldset>' . "\n";
2050 echo '<fieldset class="tblFooters">' . "\n"
2051 . ' <input type="submit" value="' . __('Go') . '" />'
2052 . '</fieldset>' . "\n"
2057 // Provide a line with links to the relevant database and table
2058 if (isset($dbname) && empty($dbname_is_wildcard)) {
2059 echo '[ ' . __('Database')
2060 . ' <a href="' . $GLOBALS['cfg']['DefaultTabDatabase'] . '?'
2061 . $GLOBALS['url_query'] . '&db=' . $url_dbname . '&reload=1">'
2062 . htmlspecialchars($dbname) . ': ' . PMA_getTitleForTarget($GLOBALS['cfg']['DefaultTabDatabase']) . "</a> ]\n";
2064 if (isset($tablename)) {
2065 echo ' [ ' . __('Table') . ' <a href="'
2066 . $GLOBALS['cfg']['DefaultTabTable'] . '?' . $GLOBALS['url_query']
2067 . '&db=' . $url_dbname . '&table=' . htmlspecialchars(urlencode($tablename))
2068 . '&reload=1">' . htmlspecialchars($tablename) . ': '
2069 . PMA_getTitleForTarget($GLOBALS['cfg']['DefaultTabTable'])
2075 if (! isset($dbname) && ! $user_does_not_exists) {
2076 require_once './libraries/display_change_password.lib.php';
2078 echo '<form action="server_privileges.php" method="post" onsubmit="return checkPassword(this);">' . "\n"
2079 . PMA_generate_common_hidden_inputs('', '')
2080 . '<input type="hidden" name="old_username" value="' . htmlspecialchars($username) . '" />' . "\n"
2081 . '<input type="hidden" name="old_hostname" value="' . htmlspecialchars($hostname) . '" />' . "\n"
2082 . '<fieldset id="fieldset_change_copy_user">' . "\n"
2083 . ' <legend>' . __('Change Login Information / Copy User') . '</legend>' . "\n";
2084 PMA_displayLoginInformationFields('change');
2085 echo ' <fieldset>' . "\n"
2086 . ' <legend>' . __('Create a new user with the same privileges and ...') . '</legend>' . "\n";
2088 '4' => __('... keep the old one.'),
2089 '1' => __(' ... delete the old one from the user tables.'),
2090 '2' => __(' ... revoke all active privileges from the old one and delete it afterwards.'),
2091 '3' => __(' ... delete the old one from the user tables and reload the privileges afterwards.'));
2092 PMA_display_html_radio('mode', $choices, '4', true);
2095 echo ' </fieldset>' . "\n"
2096 . '</fieldset>' . "\n"
2097 . '<fieldset id="fieldset_change_copy_user_footer" class="tblFooters">' . "\n"
2098 . ' <input type="submit" name="change_copy" value="' . __('Go') . '" />' . "\n"
2099 . '</fieldset>' . "\n"
2103 } elseif (isset($_REQUEST['adduser'])) {
2106 $GLOBALS['url_query'] .= '&adduser=1';
2108 . PMA_getIcon('b_usradd.png') . __('Add user') . "\n"
2110 . '<form name="usersForm" id="addUsersForm_' . $random_n . '" action="server_privileges.php" method="post">' . "\n"
2111 . PMA_generate_common_hidden_inputs('', '');
2112 PMA_displayLoginInformationFields('new');
2113 echo '<fieldset id="fieldset_add_user_database">' . "\n"
2114 . '<legend>' . __('Database for user') . '</legend>' . "\n";
2116 $default_choice = 0;
2118 '0' => _pgettext('Create none database for user', 'None'),
2119 '1' => __('Create database with same name and grant all privileges'),
2120 '2' => __('Grant all privileges on wildcard name (username\\_%)'));
2122 if ( !empty($dbname) ) {
2123 $choices['3'] = sprintf( __('Grant all privileges on database "%s"'), htmlspecialchars($dbname));
2124 $default_choice = 3;
2125 echo '<input type="hidden" name="dbname" value="' . htmlspecialchars($dbname) . '" />' . "\n";
2128 // 4th parameter set to true to add line breaks
2129 // 5th parameter set to false to avoid htmlspecialchars() escaping in the label
2130 // since we have some HTML in some labels
2131 PMA_display_html_radio('createdb', $choices, $default_choice, true, false);
2133 unset($default_choice);
2135 echo '</fieldset>' . "\n";
2136 PMA_displayPrivTable('*', '*', false);
2137 echo ' <fieldset id="fieldset_add_user_footer" class="tblFooters">' . "\n"
2138 . ' <input type="submit" name="adduser_submit" value="' . __('Go') . '" />' . "\n"
2139 . ' </fieldset>' . "\n"
2142 // check the privileges for a particular database.
2143 $user_form = '<form id="usersForm"><table id="dbspecificuserrights" class="data">' . "\n"
2144 . '<caption class="tblHeaders">' . "\n"
2145 . PMA_getIcon('b_usrcheck.png')
2146 . ' ' . sprintf(__('Users having access to "%s"'), '<a href="' . $GLOBALS['cfg']['DefaultTabDatabase'] . '?' . PMA_generate_common_url($checkprivs) . '">' . htmlspecialchars($checkprivs) . '</a>') . "\n"
2147 . '</caption>' . "\n"
2149 . ' <tr><th>' . __('User') . '</th>' . "\n"
2150 . ' <th>' . __('Host') . '</th>' . "\n"
2151 . ' <th>' . __('Type') . '</th>' . "\n"
2152 . ' <th>' . __('Privileges') . '</th>' . "\n"
2153 . ' <th>' . __('Grant') . '</th>' . "\n"
2154 . ' <th>' . __('Action') . '</th>' . "\n"
2159 unset($row, $row1, $row2);
2161 // now, we build the table...
2162 $list_of_privileges =
2174 . '`References_priv`, '
2175 . '`Create_tmp_table_priv`, '
2176 . '`Lock_tables_priv`, '
2177 . '`Create_view_priv`, '
2178 . '`Show_view_priv`, '
2179 . '`Create_routine_priv`, '
2180 . '`Alter_routine_priv`, '
2183 $list_of_compared_privileges =
2184 '`Select_priv` = \'N\''
2185 . ' AND `Insert_priv` = \'N\''
2186 . ' AND `Update_priv` = \'N\''
2187 . ' AND `Delete_priv` = \'N\''
2188 . ' AND `Create_priv` = \'N\''
2189 . ' AND `Drop_priv` = \'N\''
2190 . ' AND `Grant_priv` = \'N\''
2191 . ' AND `References_priv` = \'N\''
2192 . ' AND `Create_tmp_table_priv` = \'N\''
2193 . ' AND `Lock_tables_priv` = \'N\''
2194 . ' AND `Create_view_priv` = \'N\''
2195 . ' AND `Show_view_priv` = \'N\''
2196 . ' AND `Create_routine_priv` = \'N\''
2197 . ' AND `Alter_routine_priv` = \'N\''
2198 . ' AND `Execute_priv` = \'N\'';
2200 if (PMA_MYSQL_INT_VERSION
>= 50106) {
2201 $list_of_privileges .=
2204 $list_of_compared_privileges .=
2205 ' AND `Event_priv` = \'N\''
2206 . ' AND `Trigger_priv` = \'N\'';
2210 '(SELECT ' . $list_of_privileges . ', `Db`'
2211 .' FROM `mysql`.`db`'
2212 .' WHERE \'' . PMA_sqlAddSlashes($checkprivs) . "'"
2214 .' AND NOT (' . $list_of_compared_privileges. ')) '
2216 .'(SELECT ' . $list_of_privileges . ', \'*\' AS `Db`'
2217 .' FROM `mysql`.`user` '
2218 .' WHERE NOT (' . $list_of_compared_privileges . ')) '
2219 .' ORDER BY `User` ASC,'
2222 $res = PMA_DBI_query($sql_query);
2223 $row = PMA_DBI_fetch_assoc($res);
2230 // prepare the current user
2231 $current_privileges = array();
2232 $current_user = $row['User'];
2233 $current_host = $row['Host'];
2234 while ($row && $current_user == $row['User'] && $current_host == $row['Host']) {
2235 $current_privileges[] = $row;
2236 $row = PMA_DBI_fetch_assoc($res);
2238 $user_form .= ' <tr class="noclick ' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
2240 if (count($current_privileges) > 1) {
2241 $user_form .= ' rowspan="' . count($current_privileges) . '"';
2243 $user_form .= '>' . (empty($current_user) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($current_user)) . "\n"
2246 if (count($current_privileges) > 1) {
2247 $user_form .= ' rowspan="' . count($current_privileges) . '"';
2249 $user_form .= '>' . htmlspecialchars($current_host) . '</td>' . "\n";
2250 foreach ($current_privileges as $current) {
2251 $user_form .= ' <td>' . "\n"
2253 if (! isset($current['Db']) ||
$current['Db'] == '*') {
2254 $user_form .= __('global');
2255 } elseif ($current['Db'] == PMA_escape_mysql_wildcards($checkprivs)) {
2256 $user_form .= __('database-specific');
2258 $user_form .= __('wildcard'). ': <tt>' . htmlspecialchars($current['Db']) . '</tt>';
2264 . ' ' . join(',' . "\n" . ' ', PMA_extractPrivInfo($current, true)) . "\n"
2268 . ' ' . ($current['Grant_priv'] == 'Y' ?
__('Yes') : __('No')) . "\n"
2271 $user_form .= sprintf($link_edit, urlencode($current_user),
2272 urlencode($current_host),
2273 urlencode(! isset($current['Db']) ||
$current['Db'] == '*' ?
'' : $current['Db']),
2275 $user_form .= '</td>' . "\n"
2278 if (empty($row) && empty($row1) && empty($row2)) {
2281 $odd_row = ! $odd_row;
2284 $user_form .= ' <tr class="odd">' . "\n"
2285 . ' <td colspan="6">' . "\n"
2286 . ' ' . __('No user found.') . "\n"
2290 $user_form .= '</tbody>' . "\n"
2291 . '</table></form>' . "\n";
2293 if($GLOBALS['is_ajax_request'] == true){
2294 $extra_data['user_form'] = $user_form;
2295 $message = PMA_Message
::success(__('User has been added.'));
2296 PMA_ajaxResponse($message, $message->isSuccess(), $extra_data);
2298 // Offer to create a new user for the current database
2299 $user_form .= '<fieldset id="fieldset_add_user">' . "\n"
2300 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1&dbname=' . htmlspecialchars($checkprivs) .'" val="'.'checkprivs='.htmlspecialchars($checkprivs). '&'.$GLOBALS['url_query'] . '" class="'.$conditional_class.'" name="db_specific">' . "\n"
2301 . PMA_getIcon('b_usradd.png')
2302 . ' ' . __('Add user') . '</a>' . "\n"
2303 . '</fieldset>' . "\n";
2307 } // end if (empty($_REQUEST['adduser']) && empty($checkprivs)) ... elseif ... else ...
2311 * Displays the footer
2314 require './libraries/footer.inc.php';