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.16.custom.js';
19 $GLOBALS['js_include'][] = 'codemirror/lib/codemirror.js';
20 $GLOBALS['js_include'][] = 'codemirror/mode/mysql/mysql.js';
21 $_add_user_error = false;
23 require './libraries/server_common.inc.php';
25 if ($GLOBALS['cfg']['AjaxEnable']) {
26 $conditional_class = 'ajax';
28 $conditional_class = '';
32 * Messages are built using the message name
34 $strPrivDescAllPrivileges = __('Includes all privileges except GRANT.');
35 $strPrivDescAlter = __('Allows altering the structure of existing tables.');
36 $strPrivDescAlterRoutine = __('Allows altering and dropping stored routines.');
37 $strPrivDescCreateDb = __('Allows creating new databases and tables.');
38 $strPrivDescCreateRoutine = __('Allows creating stored routines.');
39 $strPrivDescCreateTbl = __('Allows creating new tables.');
40 $strPrivDescCreateTmpTable = __('Allows creating temporary tables.');
41 $strPrivDescCreateUser = __('Allows creating, dropping and renaming user accounts.');
42 $strPrivDescCreateView = __('Allows creating new views.');
43 $strPrivDescDelete = __('Allows deleting data.');
44 $strPrivDescDropDb = __('Allows dropping databases and tables.');
45 $strPrivDescDropTbl = __('Allows dropping tables.');
46 $strPrivDescEvent = __('Allows to set up events for the event scheduler');
47 $strPrivDescExecute = __('Allows executing stored routines.');
48 $strPrivDescFile = __('Allows importing data from and exporting data into files.');
49 $strPrivDescGrant = __('Allows adding users and privileges without reloading the privilege tables.');
50 $strPrivDescIndex = __('Allows creating and dropping indexes.');
51 $strPrivDescInsert = __('Allows inserting and replacing data.');
52 $strPrivDescLockTables = __('Allows locking tables for the current thread.');
53 $strPrivDescMaxConnections = __('Limits the number of new connections the user may open per hour.');
54 $strPrivDescMaxQuestions = __('Limits the number of queries the user may send to the server per hour.');
55 $strPrivDescMaxUpdates = __('Limits the number of commands that change any table or database the user may execute per hour.');
56 $strPrivDescMaxUserConnections = __('Limits the number of simultaneous connections the user may have.');
57 $strPrivDescProcess = __('Allows viewing processes of all users');
58 $strPrivDescReferences = __('Has no effect in this MySQL version.');
59 $strPrivDescReload = __('Allows reloading server settings and flushing the server\'s caches.');
60 $strPrivDescReplClient = __('Allows the user to ask where the slaves / masters are.');
61 $strPrivDescReplSlave = __('Needed for the replication slaves.');
62 $strPrivDescSelect = __('Allows reading data.');
63 $strPrivDescShowDb = __('Gives access to the complete list of databases.');
64 $strPrivDescShowView = __('Allows performing SHOW CREATE VIEW queries.');
65 $strPrivDescShutdown = __('Allows shutting down the server.');
66 $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.');
67 $strPrivDescTrigger = __('Allows creating and dropping triggers');
68 $strPrivDescUpdate = __('Allows changing data.');
69 $strPrivDescUsage = __('No privileges.');
72 * Checks if a dropdown box has been used for selecting a database / table
74 if (PMA_isValid($_REQUEST['pred_tablename'])) {
75 $tablename = $_REQUEST['pred_tablename'];
76 unset($pred_tablename);
77 } elseif (PMA_isValid($_REQUEST['tablename'])) {
78 $tablename = $_REQUEST['tablename'];
83 if (PMA_isValid($_REQUEST['pred_dbname'])) {
84 $dbname = $_REQUEST['pred_dbname'];
86 } elseif (PMA_isValid($_REQUEST['dbname'])) {
87 $dbname = $_REQUEST['dbname'];
94 $db_and_table = PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . '.';
95 if (isset($tablename)) {
96 $db_and_table .= PMA_backquote($tablename);
101 $db_and_table = '*.*';
104 // check if given $dbname is a wildcard or not
105 if (isset($dbname)) {
106 //if (preg_match('/\\\\(?:_|%)/i', $dbname)) {
107 if (preg_match('/(?<!\\\\)(?:_|%)/i', $dbname)) {
108 $dbname_is_wildcard = true;
110 $dbname_is_wildcard = false;
115 * Checks if the user is allowed to do what he tries to...
117 if (! $is_superuser) {
118 include './libraries/server_links.inc.php';
120 . PMA_getIcon('b_usrlist.png')
121 . __('Privileges') . "\n"
123 PMA_Message
::error(__('No Privileges'))->display();
124 include './libraries/footer.inc.php';
127 $random_n = mt_rand(0, 1000000); // a random number that will be appended to the id of the user forms
130 * Escapes wildcard in a database+table specification
131 * before using it in a GRANT statement.
133 * Escaping a wildcard character in a GRANT is only accepted at the global
134 * or database level, not at table level; this is why I remove
135 * the escaping character. Internally, in mysql.tables_priv.Db there are
136 * no escaping (for example test_db) but in mysql.db you'll see test\_db
137 * for a db-specific privilege.
139 * @param string $dbname Database name
140 * @param string $tablename Table name
142 * @return string the escaped (if necessary) database.table
144 function PMA_wildcardEscapeForGrant($dbname, $tablename)
147 if (! strlen($dbname)) {
148 $db_and_table = '*.*';
150 if (strlen($tablename)) {
151 $db_and_table = PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . '.';
152 $db_and_table .= PMA_backquote($tablename);
154 $db_and_table = PMA_backquote($dbname) . '.';
155 $db_and_table .= '*';
158 return $db_and_table;
162 * Generates a condition on the user name
164 * @param string $initial the user's initial
166 * @return string the generated condition
168 function PMA_rangeOfUsers($initial = '')
170 // strtolower() is used because the User field
171 // might be BINARY, so LIKE would be case sensitive
172 if (! empty($initial)) {
173 $ret = " WHERE `User` LIKE '" . PMA_sqlAddSlashes($initial, true) . "%'"
174 . " OR `User` LIKE '" . PMA_sqlAddSlashes(strtolower($initial), true) . "%'";
182 * Extracts the privilege information of a priv table row
184 * @param array $row the row
185 * @param boolean $enableHTML add <dfn> tag with tooltips
187 * @global resource $user_link the database connection
191 function PMA_extractPrivInfo($row = '', $enableHTML = false)
197 __('Allows reading data.')),
201 __('Allows inserting and replacing data.')),
205 __('Allows changing data.')),
209 __('Allows deleting data.')),
213 __('Allows creating new databases and tables.')),
217 __('Allows dropping databases and tables.')),
221 __('Allows reloading server settings and flushing the server\'s caches.')),
225 __('Allows shutting down the server.')),
229 __('Allows viewing processes of all users')),
233 __('Allows importing data from and exporting data into files.')),
237 __('Has no effect in this MySQL version.')),
241 __('Allows creating and dropping indexes.')),
245 __('Allows altering the structure of existing tables.')),
249 __('Gives access to the complete list of databases.')),
253 __('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.')),
255 'Create_tmp_table_priv',
256 'CREATE TEMPORARY TABLES',
257 __('Allows creating temporary tables.')),
261 __('Allows locking tables for the current thread.')),
265 __('Needed for the replication slaves.')),
268 'REPLICATION CLIENT',
269 __('Allows the user to ask where the slaves / masters are.')),
273 __('Allows creating new views.')),
277 __('Allows to set up events for the event scheduler')),
281 __('Allows creating and dropping triggers')),
286 __('Allows creating new views.')),
290 __('Allows performing SHOW CREATE VIEW queries.')),
295 __('Allows performing SHOW CREATE VIEW queries.')),
297 'Create_routine_priv',
299 __('Allows creating stored routines.')),
301 'Alter_routine_priv',
303 __('Allows altering and dropping stored routines.')),
307 __('Allows creating, dropping and renaming user accounts.')),
311 __('Allows executing stored routines.')),
314 if (! empty($row) && isset($row['Table_priv'])) {
315 $row1 = PMA_DBI_fetch_single_row(
316 'SHOW COLUMNS FROM `mysql`.`tables_priv` LIKE \'Table_priv\';',
317 'ASSOC', $GLOBALS['userlink']
319 $av_grants = explode('\',\'', substr($row1['Type'], 5, strlen($row1['Type']) - 7));
321 $users_grants = explode(',', $row['Table_priv']);
322 foreach ($av_grants as $current_grant) {
323 $row[$current_grant . '_priv'] = in_array($current_grant, $users_grants) ?
'Y' : 'N';
325 unset($current_grant);
327 unset($users_grants);
330 $allPrivileges = true;
331 foreach ($grants as $current_grant) {
332 if ((! empty($row) && isset($row[$current_grant[0]]))
333 ||
(empty($row) && isset($GLOBALS[$current_grant[0]]))
335 if ((! empty($row) && $row[$current_grant[0]] == 'Y')
337 && ($GLOBALS[$current_grant[0]] == 'Y'
338 ||
(is_array($GLOBALS[$current_grant[0]])
339 && count($GLOBALS[$current_grant[0]]) == $GLOBALS['column_count']
340 && empty($GLOBALS[$current_grant[0] . '_none']))))
343 $privs[] = '<dfn title="' . $current_grant[2] . '">' . $current_grant[1] . '</dfn>';
345 $privs[] = $current_grant[1];
347 } elseif (! empty($GLOBALS[$current_grant[0]])
348 && is_array($GLOBALS[$current_grant[0]])
349 && empty($GLOBALS[$current_grant[0] . '_none'])) {
351 $priv_string = '<dfn title="' . $current_grant[2] . '">' . $current_grant[1] . '</dfn>';
353 $priv_string = $current_grant[1];
355 $privs[] = $priv_string . ' (`' . join('`, `', $GLOBALS[$current_grant[0]]) . '`)';
357 $allPrivileges = false;
363 $privs[] = '<dfn title="' . __('No privileges.') . '">USAGE</dfn>';
367 } elseif ($allPrivileges && (! isset($GLOBALS['grant_count']) ||
count($privs) == $GLOBALS['grant_count'])) {
369 $privs = array('<dfn title="' . __('Includes all privileges except GRANT.') . '">ALL PRIVILEGES</dfn>');
371 $privs = array('ALL PRIVILEGES');
375 } // end of the 'PMA_extractPrivInfo()' function
378 * Displays on which column(s) a table-specific privilege is granted
380 function PMA_display_column_privs($columns, $row, $name_for_select,
381 $priv_for_header, $name, $name_for_dfn, $name_for_current)
383 echo ' <div class="item" id="div_item_' . $name . '">' . "\n"
384 . ' <label for="select_' . $name . '_priv">' . "\n"
385 . ' <tt><dfn title="' . $name_for_dfn . '">'
386 . $priv_for_header . '</dfn></tt>' . "\n"
387 . ' </label><br />' . "\n"
388 . ' <select id="select_' . $name . '_priv" name="'
389 . $name_for_select . '[]" multiple="multiple" size="8">' . "\n";
391 foreach ($columns as $current_column => $current_column_privileges) {
392 echo ' <option value="' . htmlspecialchars($current_column) . '"';
393 if ($row[$name_for_select] == 'Y' ||
$current_column_privileges[$name_for_current]) {
394 echo ' selected="selected"';
396 echo '>' . htmlspecialchars($current_column) . '</option>' . "\n";
399 echo ' </select>' . "\n"
400 . ' <i>' . __('Or') . '</i>' . "\n"
401 . ' <label for="checkbox_' . $name_for_select
402 . '_none"><input type="checkbox"'
403 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
404 . ' name="' . $name_for_select . '_none" id="checkbox_'
405 . $name_for_select . '_none" title="' . _pgettext('None privileges', 'None') . '" />'
406 . _pgettext('None privileges', 'None') . '</label>' . "\n"
412 * Displays the privileges form table
414 * @param string $db the database
415 * @param string $table the table
416 * @param boolean $submit wheather to display the submit button or not
418 * @global array $cfg the phpMyAdmin configuration
419 * @global ressource $user_link the database connection
423 function PMA_displayPrivTable($db = '*', $table = '*', $submit = true)
431 if (isset($GLOBALS['username'])) {
432 $username = $GLOBALS['username'];
433 $hostname = $GLOBALS['hostname'];
435 $sql_query = "SELECT * FROM `mysql`.`user`"
436 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
437 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
438 } elseif ($table == '*') {
439 $sql_query = "SELECT * FROM `mysql`.`db`"
440 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
441 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "'"
442 ." AND '" . PMA_unescape_mysql_wildcards($db) . "'"
445 $sql_query = "SELECT `Table_priv`"
446 ." FROM `mysql`.`tables_priv`"
447 ." WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
448 ." AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "'"
449 ." AND `Db` = '" . PMA_unescape_mysql_wildcards($db) . "'"
450 ." AND `Table_name` = '" . PMA_sqlAddSlashes($table) . "';";
452 $row = PMA_DBI_fetch_single_row($sql_query);
457 $sql_query = 'SHOW COLUMNS FROM `mysql`.`user`;';
458 } elseif ($table == '*') {
459 $sql_query = 'SHOW COLUMNS FROM `mysql`.`db`;';
461 $res = PMA_DBI_query($sql_query);
462 while ($row1 = PMA_DBI_fetch_row($res)) {
463 if (substr($row1[0], 0, 4) == 'max_') {
466 $row[$row1[0]] = 'N';
469 PMA_DBI_free_result($res);
471 $row = array('Table_priv' => '');
474 if (isset($row['Table_priv'])) {
475 $row1 = PMA_DBI_fetch_single_row(
476 'SHOW COLUMNS FROM `mysql`.`tables_priv` LIKE \'Table_priv\';',
477 'ASSOC', $GLOBALS['userlink']
479 // note: in MySQL 5.0.3 we get "Create View', 'Show view';
480 // the View for Create is spelled with uppercase V
481 // the view for Show is spelled with lowercase v
482 // and there is a space between the words
484 $av_grants = explode('\',\'', substr($row1['Type'], strpos($row1['Type'], '(') +
2, strpos($row1['Type'], ')') - strpos($row1['Type'], '(') - 3));
486 $users_grants = explode(',', $row['Table_priv']);
488 foreach ($av_grants as $current_grant) {
489 $row[$current_grant . '_priv'] = in_array($current_grant, $users_grants) ?
'Y' : 'N';
491 unset($row['Table_priv'], $current_grant, $av_grants, $users_grants);
494 $res = PMA_DBI_try_query('SHOW COLUMNS FROM ' . PMA_backquote(PMA_unescape_mysql_wildcards($db)) . '.' . PMA_backquote($table) . ';');
497 while ($row1 = PMA_DBI_fetch_row($res)) {
498 $columns[$row1[0]] = array(
502 'References' => false
505 PMA_DBI_free_result($res);
509 // t a b l e - s p e c i f i c p r i v i l e g e s
510 if (! empty($columns)) {
511 $res = PMA_DBI_query(
512 'SELECT `Column_name`, `Column_priv`'
513 .' FROM `mysql`.`columns_priv`'
515 .' = \'' . PMA_sqlAddSlashes($username) . "'"
517 .' = \'' . PMA_sqlAddSlashes($hostname) . "'"
519 .' = \'' . PMA_sqlAddSlashes(PMA_unescape_mysql_wildcards($db)) . "'"
521 .' = \'' . PMA_sqlAddSlashes($table) . '\';'
524 while ($row1 = PMA_DBI_fetch_row($res)) {
525 $row1[1] = explode(',', $row1[1]);
526 foreach ($row1[1] as $current) {
527 $columns[$row1[0]][$current] = true;
530 PMA_DBI_free_result($res);
531 unset($res, $row1, $current);
533 echo '<input type="hidden" name="grant_count" value="' . count($row) . '" />' . "\n"
534 . '<input type="hidden" name="column_count" value="' . count($columns) . '" />' . "\n"
535 . '<fieldset id="fieldset_user_priv">' . "\n"
536 . ' <legend>' . __('Table-specific privileges')
537 . PMA_showHint(__('Note: MySQL privilege names are expressed in English'))
538 . '</legend>' . "\n";
542 // privs that are attached to a specific column
543 PMA_display_column_privs(
544 $columns, $row, 'Select_priv', 'SELECT',
545 'select', __('Allows reading data.'), 'Select'
548 PMA_display_column_privs(
549 $columns, $row, 'Insert_priv', 'INSERT',
550 'insert', __('Allows inserting and replacing data.'), 'Insert'
553 PMA_display_column_privs(
554 $columns, $row, 'Update_priv', 'UPDATE',
555 'update', __('Allows changing data.'), 'Update'
558 PMA_display_column_privs(
559 $columns, $row, 'References_priv', 'REFERENCES', 'references',
560 __('Has no effect in this MySQL version.'), 'References'
563 // privs that are not attached to a specific column
565 echo ' <div class="item">' . "\n";
566 foreach ($row as $current_grant => $current_grant_value) {
567 if (in_array(substr($current_grant, 0, (strlen($current_grant) - 5)),
568 array('Select', 'Insert', 'Update', 'References'))) {
571 // make a substitution to match the messages variables;
572 // also we must substitute the grant we get, because we can't generate
573 // a form variable containing blanks (those would get changed to
574 // an underscore when receiving the POST)
575 if ($current_grant == 'Create View_priv') {
576 $tmp_current_grant = 'CreateView_priv';
577 $current_grant = 'Create_view_priv';
578 } elseif ($current_grant == 'Show view_priv') {
579 $tmp_current_grant = 'ShowView_priv';
580 $current_grant = 'Show_view_priv';
582 $tmp_current_grant = $current_grant;
585 echo ' <div class="item">' . "\n"
586 . ' <input type="checkbox"'
587 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
588 . ' name="' . $current_grant . '" id="checkbox_' . $current_grant
590 . ($current_grant_value == 'Y' ?
'checked="checked" ' : '')
593 echo (isset($GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))])
594 ?
$GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))]
595 : $GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5)) . 'Tbl']) . '"/>' . "\n";
597 echo ' <label for="checkbox_' . $current_grant
598 . '"><tt><dfn title="'
599 . (isset($GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))])
600 ?
$GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5))]
601 : $GLOBALS['strPrivDesc' . substr($tmp_current_grant, 0, (strlen($tmp_current_grant) - 5)) . 'Tbl'])
602 . '">' . strtoupper(substr($current_grant, 0, strlen($current_grant) - 5)) . '</dfn></tt></label>' . "\n"
606 echo ' </div>' . "\n";
608 echo ' <div class="clearfloat"></div>' . "\n";
612 // g l o b a l o r d b - s p e c i f i c
614 $privTable_names = array(0 => __('Data'), 1 => __('Structure'), 2 => __('Administration'));
617 $privTable[0] = array(
618 array('Select', 'SELECT', __('Allows reading data.')),
619 array('Insert', 'INSERT', __('Allows inserting and replacing data.')),
620 array('Update', 'UPDATE', __('Allows changing data.')),
621 array('Delete', 'DELETE', __('Allows deleting data.'))
624 $privTable[0][] = array('File', 'FILE', __('Allows importing data from and exporting data into files.'));
628 $privTable[1] = array(
629 array('Create', 'CREATE', ($table == '*' ?
__('Allows creating new databases and tables.') : __('Allows creating new tables.'))),
630 array('Alter', 'ALTER', __('Allows altering the structure of existing tables.')),
631 array('Index', 'INDEX', __('Allows creating and dropping indexes.')),
632 array('Drop', 'DROP', ($table == '*' ?
__('Allows dropping databases and tables.') : __('Allows dropping tables.'))),
633 array('Create_tmp_table', 'CREATE TEMPORARY TABLES', __('Allows creating temporary tables.')),
634 array('Show_view', 'SHOW VIEW', __('Allows performing SHOW CREATE VIEW queries.')),
635 array('Create_routine', 'CREATE ROUTINE', __('Allows creating stored routines.')),
636 array('Alter_routine', 'ALTER ROUTINE', __('Allows altering and dropping stored routines.')),
637 array('Execute', 'EXECUTE', __('Allows executing stored routines.')),
639 // this one is for a db-specific priv: Create_view_priv
640 if (isset($row['Create_view_priv'])) {
641 $privTable[1][] = array('Create_view', 'CREATE VIEW', __('Allows creating new views.'));
643 // this one is for a table-specific priv: Create View_priv
644 if (isset($row['Create View_priv'])) {
645 $privTable[1][] = array('Create View', 'CREATE VIEW', __('Allows creating new views.'));
647 if (isset($row['Event_priv'])) {
649 $privTable[1][] = array('Event', 'EVENT', __('Allows to set up events for the event scheduler'));
650 $privTable[1][] = array('Trigger', 'TRIGGER', __('Allows creating and dropping triggers'));
653 // a d m i n i s t r a t i o n
654 $privTable[2] = array(
655 array('Grant', 'GRANT', __('Allows adding users and privileges without reloading the privilege tables.')),
658 $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.'));
659 $privTable[2][] = array('Process', 'PROCESS', __('Allows viewing processes of all users'));
660 $privTable[2][] = array('Reload', 'RELOAD', __('Allows reloading server settings and flushing the server\'s caches.'));
661 $privTable[2][] = array('Shutdown', 'SHUTDOWN', __('Allows shutting down the server.'));
662 $privTable[2][] = array('Show_db', 'SHOW DATABASES', __('Gives access to the complete list of databases.'));
664 $privTable[2][] = array('Lock_tables', 'LOCK TABLES', __('Allows locking tables for the current thread.'));
665 $privTable[2][] = array('References', 'REFERENCES', __('Has no effect in this MySQL version.'));
667 $privTable[2][] = array('Repl_client', 'REPLICATION CLIENT', __('Allows the user to ask where the slaves / masters are.'));
668 $privTable[2][] = array('Repl_slave', 'REPLICATION SLAVE', __('Needed for the replication slaves.'));
669 $privTable[2][] = array('Create_user', 'CREATE USER', __('Allows creating, dropping and renaming user accounts.'));
671 echo '<input type="hidden" name="grant_count" value="'
672 . (count($privTable[0]) +
count($privTable[1]) +
count($privTable[2]) - (isset($row['Grant_priv']) ?
1 : 0))
674 . '<fieldset id="fieldset_user_global_rights">' . "\n"
678 ?
__('Global privileges')
680 ?
__('Database-specific privileges')
681 : __('Table-specific privileges'))) . "\n"
682 . ' (<a href="server_privileges.php?'
683 . $GLOBALS['url_query'] . '&checkall=1" onclick="setCheckboxes(\'addUsersForm_' . $random_n . '\', true); return false;">'
684 . __('Check All') . '</a> /' . "\n"
685 . ' <a href="server_privileges.php?'
686 . $GLOBALS['url_query'] . '" onclick="setCheckboxes(\'addUsersForm_' . $random_n . '\', false); return false;">'
687 . __('Uncheck All') . '</a>)' . "\n"
688 . ' </legend>' . "\n"
689 . ' <p><small><i>' . __('Note: MySQL privilege names are expressed in English') . '</i></small></p>' . "\n";
691 // Output the Global privilege tables with checkboxes
692 foreach ($privTable as $i => $table) {
693 echo ' <fieldset>' . "\n"
694 . ' <legend>' . __($privTable_names[$i]) . '</legend>' . "\n";
695 foreach ($table as $priv) {
696 echo ' <div class="item">' . "\n"
697 . ' <input type="checkbox"'
698 . ' name="' . $priv[0] . '_priv" id="checkbox_' . $priv[0] . '_priv"'
699 . ' value="Y" title="' . $priv[2] . '"'
700 . ((! empty($GLOBALS['checkall']) ||
$row[$priv[0] . '_priv'] == 'Y') ?
' checked="checked"' : '')
702 . ' <label for="checkbox_' . $priv[0] . '_priv"><tt><dfn title="' . $priv[2] . '">'
703 . $priv[1] . '</dfn></tt></label>' . "\n"
706 echo ' </fieldset>' . "\n";
709 // The "Resource limits" box is not displayed for db-specific privs
711 echo ' <fieldset>' . "\n"
712 . ' <legend>' . __('Resource limits') . '</legend>' . "\n"
713 . ' <p><small><i>' . __('Note: Setting these options to 0 (zero) removes the limit.') . '</i></small></p>' . "\n"
714 . ' <div class="item">' . "\n"
715 . ' <label for="text_max_questions"><tt><dfn title="'
716 . __('Limits the number of queries the user may send to the server per hour.') . '">MAX QUERIES PER HOUR</dfn></tt></label>' . "\n"
717 . ' <input type="text" name="max_questions" id="text_max_questions" value="'
718 . $row['max_questions'] . '" size="11" maxlength="11" title="' . __('Limits the number of queries the user may send to the server per hour.') . '" />' . "\n"
720 . ' <div class="item">' . "\n"
721 . ' <label for="text_max_updates"><tt><dfn title="'
722 . __('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"
723 . ' <input type="text" name="max_updates" id="text_max_updates" value="'
724 . $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"
726 . ' <div class="item">' . "\n"
727 . ' <label for="text_max_connections"><tt><dfn title="'
728 . __('Limits the number of new connections the user may open per hour.') . '">MAX CONNECTIONS PER HOUR</dfn></tt></label>' . "\n"
729 . ' <input type="text" name="max_connections" id="text_max_connections" value="'
730 . $row['max_connections'] . '" size="11" maxlength="11" title="' . __('Limits the number of new connections the user may open per hour.') . '" />' . "\n"
732 . ' <div class="item">' . "\n"
733 . ' <label for="text_max_user_connections"><tt><dfn title="'
734 . __('Limits the number of simultaneous connections the user may have.') . '">MAX USER_CONNECTIONS</dfn></tt></label>' . "\n"
735 . ' <input type="text" name="max_user_connections" id="text_max_user_connections" value="'
736 . $row['max_user_connections'] . '" size="11" maxlength="11" title="' . __('Limits the number of simultaneous connections the user may have.') . '" />' . "\n"
738 . ' </fieldset>' . "\n";
741 echo ' <div class="clearfloat"></div>' . "\n";
743 echo '</fieldset>' . "\n";
745 echo '<fieldset id="fieldset_user_privtable_footer" class="tblFooters">' . "\n"
746 . ' <input type="submit" name="update_privs" value="' . __('Go') . '" />' . "\n"
747 . '</fieldset>' . "\n";
749 } // end of the 'PMA_displayPrivTable()' function
753 * Displays the fields used by the "new user" form as well as the
754 * "change login information / copy user" form.
756 * @param string $mode are we creating a new user or are we just
757 * changing one? (allowed values: 'new', 'change')
759 * @global array $cfg the phpMyAdmin configuration
760 * @global ressource $user_link the database connection
764 function PMA_displayLoginInformationFields($mode = 'new')
766 // Get user/host name lengths
767 $fields_info = PMA_DBI_get_columns('mysql', 'user', null, true);
768 $username_length = 16;
769 $hostname_length = 41;
770 foreach ($fields_info as $val) {
771 if ($val['Field'] == 'User') {
772 strtok($val['Type'], '()');
775 $username_length = $v;
777 } elseif ($val['Field'] == 'Host') {
778 strtok($val['Type'], '()');
781 $hostname_length = $v;
787 if (isset($GLOBALS['username']) && strlen($GLOBALS['username']) === 0) {
788 $GLOBALS['pred_username'] = 'any';
790 echo '<fieldset id="fieldset_add_user_login">' . "\n"
791 . '<legend>' . __('Login Information') . '</legend>' . "\n"
792 . '<div class="item">' . "\n"
793 . '<label for="select_pred_username">' . "\n"
794 . ' ' . __('User name') . ':' . "\n"
796 . '<span class="options">' . "\n"
797 . ' <select name="pred_username" id="select_pred_username" title="' . __('User name') . '"' . "\n"
798 . ' onchange="if (this.value == \'any\') { username.value = \'\'; } else if (this.value == \'userdefined\') { username.focus(); username.select(); }">' . "\n"
799 . ' <option value="any"' . ((isset($GLOBALS['pred_username']) && $GLOBALS['pred_username'] == 'any') ?
' selected="selected"' : '') . '>' . __('Any user') . '</option>' . "\n"
800 . ' <option value="userdefined"' . ((! isset($GLOBALS['pred_username']) ||
$GLOBALS['pred_username'] == 'userdefined') ?
' selected="selected"' : '') . '>' . __('Use text field') . ':</option>' . "\n"
801 . ' </select>' . "\n"
803 . '<input type="text" name="username" maxlength="'
804 . $username_length . '" title="' . __('User name') . '"'
805 . (empty($GLOBALS['username'])
807 : ' value="' . htmlspecialchars(isset($GLOBALS['new_username'])
808 ?
$GLOBALS['new_username']
809 : $GLOBALS['username']) . '"')
810 . ' onchange="pred_username.value = \'userdefined\';" />' . "\n"
812 . '<div class="item">' . "\n"
813 . '<label for="select_pred_hostname">' . "\n"
814 . ' ' . __('Host') . ':' . "\n"
816 . '<span class="options">' . "\n"
817 . ' <select name="pred_hostname" id="select_pred_hostname" title="' . __('Host') . '"' . "\n";
818 $_current_user = PMA_DBI_fetch_value('SELECT USER();');
819 if (! empty($_current_user)) {
820 $thishost = str_replace("'", '', substr($_current_user, (strrpos($_current_user, '@') +
1)));
821 if ($thishost == 'localhost' ||
$thishost == '127.0.0.1') {
825 echo ' onchange="if (this.value == \'any\') { hostname.value = \'%\'; } else if (this.value == \'localhost\') { hostname.value = \'localhost\'; } '
826 . (empty($thishost) ?
'' : 'else if (this.value == \'thishost\') { hostname.value = \'' . addslashes(htmlspecialchars($thishost)) . '\'; } ')
827 . 'else if (this.value == \'hosttable\') { hostname.value = \'\'; } else if (this.value == \'userdefined\') { hostname.focus(); hostname.select(); }">' . "\n";
828 unset($_current_user);
830 // when we start editing a user, $GLOBALS['pred_hostname'] is not defined
831 if (! isset($GLOBALS['pred_hostname']) && isset($GLOBALS['hostname'])) {
832 switch (strtolower($GLOBALS['hostname'])) {
835 $GLOBALS['pred_hostname'] = 'localhost';
838 $GLOBALS['pred_hostname'] = 'any';
841 $GLOBALS['pred_hostname'] = 'userdefined';
845 echo ' <option value="any"'
846 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'any')
847 ?
' selected="selected"' : '') . '>' . __('Any host')
849 . ' <option value="localhost"'
850 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'localhost')
851 ?
' selected="selected"' : '') . '>' . __('Local')
852 . '</option>' . "\n";
853 if (! empty($thishost)) {
854 echo ' <option value="thishost"'
855 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'thishost')
856 ?
' selected="selected"' : '') . '>' . __('This Host')
857 . '</option>' . "\n";
860 echo ' <option value="hosttable"'
861 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'hosttable')
862 ?
' selected="selected"' : '') . '>' . __('Use Host Table')
864 . ' <option value="userdefined"'
865 . ((isset($GLOBALS['pred_hostname']) && $GLOBALS['pred_hostname'] == 'userdefined')
866 ?
' selected="selected"' : '')
867 . '>' . __('Use text field') . ':</option>' . "\n"
868 . ' </select>' . "\n"
870 . '<input type="text" name="hostname" maxlength="'
871 . $hostname_length . '" value="'
872 . htmlspecialchars(isset($GLOBALS['hostname']) ?
$GLOBALS['hostname'] : '')
873 . '" title="' . __('Host')
874 . '" onchange="pred_hostname.value = \'userdefined\';" />' . "\n"
875 . PMA_showHint(__('When Host table is used, this field is ignored and values stored in Host table are used instead.'))
877 . '<div class="item">' . "\n"
878 . '<label for="select_pred_password">' . "\n"
879 . ' ' . __('Password') . ':' . "\n"
881 . '<span class="options">' . "\n"
882 . ' <select name="pred_password" id="select_pred_password" title="'
883 . __('Password') . '"' . "\n"
884 . ' onchange="if (this.value == \'none\') { pma_pw.value = \'\'; pma_pw2.value = \'\'; } else if (this.value == \'userdefined\') { pma_pw.focus(); pma_pw.select(); }">' . "\n"
885 . ($mode == 'change' ?
' <option value="keep" selected="selected">' . __('Do not change the password') . '</option>' . "\n" : '')
886 . ' <option value="none"';
887 if (isset($GLOBALS['username']) && $mode != 'change') {
888 echo ' selected="selected"';
890 echo '>' . __('No Password') . '</option>' . "\n"
891 . ' <option value="userdefined"' . (isset($GLOBALS['username']) ?
'' : ' selected="selected"') . '>' . __('Use text field') . ':</option>' . "\n"
892 . ' </select>' . "\n"
894 . '<input type="password" id="text_pma_pw" name="pma_pw" title="' . __('Password') . '" onchange="pred_password.value = \'userdefined\';" />' . "\n"
896 . '<div class="item" id="div_element_before_generate_password">' . "\n"
897 . '<label for="text_pma_pw2">' . "\n"
898 . ' ' . __('Re-type') . ':' . "\n"
900 . '<span class="options"> </span>' . "\n"
901 . '<input type="password" name="pma_pw2" id="text_pma_pw2" title="' . __('Re-type') . '" onchange="pred_password.value = \'userdefined\';" />' . "\n"
903 // Generate password added here via jQuery
904 . '</fieldset>' . "\n";
905 } // end of the 'PMA_displayUserAndHostFields()' function
908 * Changes / copies a user, part I
910 if (isset($_REQUEST['change_copy'])) {
911 $user_host_condition = ' WHERE `User`'
912 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
914 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\';';
915 $row = PMA_DBI_fetch_single_row('SELECT * FROM `mysql`.`user` ' . $user_host_condition);
917 PMA_Message
::notice(__('No user found.'))->display();
918 unset($_REQUEST['change_copy']);
920 extract($row, EXTR_OVERWRITE
);
921 // Recent MySQL versions have the field "Password" in mysql.user,
922 // so the previous extract creates $Password but this script
924 if (! isset($password) && isset($Password)) {
925 $password = $Password;
934 * (Changes / copies a user, part II)
936 if (isset($_REQUEST['adduser_submit']) ||
isset($_REQUEST['change_copy'])) {
938 if ($pred_username == 'any') {
941 switch ($pred_hostname) {
946 $hostname = 'localhost';
952 $_user_name = PMA_DBI_fetch_value('SELECT USER()');
953 $hostname = substr($_user_name, (strrpos($_user_name, '@') +
1));
957 $sql = "SELECT '1' FROM `mysql`.`user`"
958 . " WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
959 . " AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
960 if (PMA_DBI_fetch_value($sql) == 1) {
961 $message = PMA_Message
::error(__('The user %s already exists!'));
962 $message->addParam('[i]\'' . $username . '\'@\'' . $hostname . '\'[/i]');
963 $_REQUEST['adduser'] = true;
964 $_add_user_error = true;
967 $create_user_real = 'CREATE USER \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
969 $real_sql_query = 'GRANT ' . join(', ', PMA_extractPrivInfo()) . ' ON *.* TO \''
970 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
971 if ($pred_password != 'none' && $pred_password != 'keep') {
972 $sql_query = $real_sql_query . ' IDENTIFIED BY \'***\'';
973 $real_sql_query .= ' IDENTIFIED BY \'' . PMA_sqlAddSlashes($pma_pw) . '\'';
974 if (isset($create_user_real)) {
975 $create_user_show = $create_user_real . ' IDENTIFIED BY \'***\'';
976 $create_user_real .= ' IDENTIFIED BY \'' . PMA_sqlAddSlashes($pma_pw) . '\'';
979 if ($pred_password == 'keep' && ! empty($password)) {
980 $real_sql_query .= ' IDENTIFIED BY PASSWORD \'' . $password . '\'';
981 if (isset($create_user_real)) {
982 $create_user_real .= ' IDENTIFIED BY PASSWORD \'' . $password . '\'';
985 $sql_query = $real_sql_query;
986 if (isset($create_user_real)) {
987 $create_user_show = $create_user_real;
991 * @todo similar code appears twice in this script
993 if ((isset($Grant_priv) && $Grant_priv == 'Y')
994 ||
(isset($max_questions) ||
isset($max_connections)
995 ||
isset($max_updates) ||
isset($max_user_connections))
997 $real_sql_query .= ' WITH';
998 $sql_query .= ' WITH';
999 if (isset($Grant_priv) && $Grant_priv == 'Y') {
1000 $real_sql_query .= ' GRANT OPTION';
1001 $sql_query .= ' GRANT OPTION';
1003 if (isset($max_questions)) {
1004 // avoid negative values
1005 $max_questions = max(0, (int)$max_questions);
1006 $real_sql_query .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
1007 $sql_query .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
1009 if (isset($max_connections)) {
1010 $max_connections = max(0, (int)$max_connections);
1011 $real_sql_query .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
1012 $sql_query .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
1014 if (isset($max_updates)) {
1015 $max_updates = max(0, (int)$max_updates);
1016 $real_sql_query .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
1017 $sql_query .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
1019 if (isset($max_user_connections)) {
1020 $max_user_connections = max(0, (int)$max_user_connections);
1021 $real_sql_query .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
1022 $sql_query .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
1025 if (isset($create_user_real)) {
1026 $create_user_real .= ';';
1027 $create_user_show .= ';';
1029 $real_sql_query .= ';';
1031 if (empty($_REQUEST['change_copy'])) {
1034 if (isset($create_user_real)) {
1035 if (! PMA_DBI_try_query($create_user_real)) {
1038 $sql_query = $create_user_show . $sql_query;
1041 if ($_error ||
! PMA_DBI_try_query($real_sql_query)) {
1042 $_REQUEST['createdb'] = false;
1043 $message = PMA_Message
::rawError(PMA_DBI_getError());
1045 $message = PMA_Message
::success(__('You have added a new user.'));
1048 switch (PMA_ifSetOr($_REQUEST['createdb'], '0')) {
1050 // Create database with same name and grant all privileges
1051 $q = 'CREATE DATABASE IF NOT EXISTS '
1052 . PMA_backquote(PMA_sqlAddSlashes($username)) . ';';
1054 if (! PMA_DBI_try_query($q)) {
1055 $message = PMA_Message
::rawError(PMA_DBI_getError());
1061 * If we are not in an Ajax request, we can't reload navigation now
1063 if ($GLOBALS['is_ajax_request'] != true) {
1064 // this is needed in case tracking is on:
1065 $GLOBALS['db'] = $username;
1066 $GLOBALS['reload'] = true;
1067 PMA_reloadNavigation();
1070 $q = 'GRANT ALL PRIVILEGES ON '
1071 . PMA_backquote(PMA_escape_mysql_wildcards(PMA_sqlAddSlashes($username))) . '.* TO \''
1072 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1074 if (! PMA_DBI_try_query($q)) {
1075 $message = PMA_Message
::rawError(PMA_DBI_getError());
1079 // Grant all privileges on wildcard name (username\_%)
1080 $q = 'GRANT ALL PRIVILEGES ON '
1081 . PMA_backquote(PMA_sqlAddSlashes($username) . '\_%') . '.* TO \''
1082 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1084 if (! PMA_DBI_try_query($q)) {
1085 $message = PMA_Message
::rawError(PMA_DBI_getError());
1089 // Grant all privileges on the specified database to the new user
1090 $q = 'GRANT ALL PRIVILEGES ON '
1091 . PMA_backquote(PMA_sqlAddSlashes($dbname)) . '.* TO \''
1092 . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1094 if (! PMA_DBI_try_query($q)) {
1095 $message = PMA_Message
::rawError(PMA_DBI_getError());
1103 if (isset($create_user_real)) {
1104 $queries[] = $create_user_real;
1106 $queries[] = $real_sql_query;
1107 // we put the query containing the hidden password in
1108 // $queries_for_display, at the same position occupied
1109 // by the real query in $queries
1110 $tmp_count = count($queries);
1111 if (isset($create_user_real)) {
1112 $queries_for_display[$tmp_count - 2] = $create_user_show;
1114 $queries_for_display[$tmp_count - 1] = $sql_query;
1116 unset($res, $real_sql_query);
1122 * Changes / copies a user, part III
1124 if (isset($_REQUEST['change_copy'])) {
1125 $user_host_condition = ' WHERE `User`'
1126 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
1128 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\';';
1129 $res = PMA_DBI_query('SELECT * FROM `mysql`.`db`' . $user_host_condition);
1130 while ($row = PMA_DBI_fetch_assoc($res)) {
1131 $queries[] = 'GRANT ' . join(', ', PMA_extractPrivInfo($row))
1132 .' ON ' . PMA_backquote($row['Db']) . '.*'
1133 .' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\''
1134 . ($row['Grant_priv'] == 'Y' ?
' WITH GRANT OPTION;' : ';');
1136 PMA_DBI_free_result($res);
1137 $res = PMA_DBI_query(
1138 'SELECT `Db`, `Table_name`, `Table_priv` FROM `mysql`.`tables_priv`' . $user_host_condition,
1139 $GLOBALS['userlink'],
1142 while ($row = PMA_DBI_fetch_assoc($res)) {
1144 $res2 = PMA_DBI_QUERY(
1145 'SELECT `Column_name`, `Column_priv`'
1146 .' FROM `mysql`.`columns_priv`'
1148 .' = \'' . PMA_sqlAddSlashes($old_username) . "'"
1150 .' = \'' . PMA_sqlAddSlashes($old_hostname) . '\''
1152 .' = \'' . PMA_sqlAddSlashes($row['Db']) . "'"
1153 .' AND `Table_name`'
1154 .' = \'' . PMA_sqlAddSlashes($row['Table_name']) . "'"
1160 $tmp_privs1 = PMA_extractPrivInfo($row);
1161 $tmp_privs2 = array(
1162 'Select' => array(),
1163 'Insert' => array(),
1164 'Update' => array(),
1165 'References' => array()
1168 while ($row2 = PMA_DBI_fetch_assoc($res2)) {
1169 $tmp_array = explode(',', $row2['Column_priv']);
1170 if (in_array('Select', $tmp_array)) {
1171 $tmp_privs2['Select'][] = $row2['Column_name'];
1173 if (in_array('Insert', $tmp_array)) {
1174 $tmp_privs2['Insert'][] = $row2['Column_name'];
1176 if (in_array('Update', $tmp_array)) {
1177 $tmp_privs2['Update'][] = $row2['Column_name'];
1179 if (in_array('References', $tmp_array)) {
1180 $tmp_privs2['References'][] = $row2['Column_name'];
1184 if (count($tmp_privs2['Select']) > 0 && ! in_array('SELECT', $tmp_privs1)) {
1185 $tmp_privs1[] = 'SELECT (`' . join('`, `', $tmp_privs2['Select']) . '`)';
1187 if (count($tmp_privs2['Insert']) > 0 && ! in_array('INSERT', $tmp_privs1)) {
1188 $tmp_privs1[] = 'INSERT (`' . join('`, `', $tmp_privs2['Insert']) . '`)';
1190 if (count($tmp_privs2['Update']) > 0 && ! in_array('UPDATE', $tmp_privs1)) {
1191 $tmp_privs1[] = 'UPDATE (`' . join('`, `', $tmp_privs2['Update']) . '`)';
1193 if (count($tmp_privs2['References']) > 0 && ! in_array('REFERENCES', $tmp_privs1)) {
1194 $tmp_privs1[] = 'REFERENCES (`' . join('`, `', $tmp_privs2['References']) . '`)';
1197 $queries[] = 'GRANT ' . join(', ', $tmp_privs1)
1198 . ' ON ' . PMA_backquote($row['Db']) . '.' . PMA_backquote($row['Table_name'])
1199 . ' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\''
1200 . (in_array('Grant', explode(',', $row['Table_priv'])) ?
' WITH GRANT OPTION;' : ';');
1206 * Updates privileges
1208 if (! empty($update_privs)) {
1209 $db_and_table = PMA_wildcardEscapeForGrant($dbname, (isset($tablename) ?
$tablename : ''));
1211 $sql_query0 = 'REVOKE ALL PRIVILEGES ON ' . $db_and_table
1212 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1213 if (! isset($Grant_priv) ||
$Grant_priv != 'Y') {
1214 $sql_query1 = 'REVOKE GRANT OPTION ON ' . $db_and_table
1215 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1220 // Should not do a GRANT USAGE for a table-specific privilege, it
1221 // causes problems later (cannot revoke it)
1222 if (! (isset($tablename) && 'USAGE' == implode('', PMA_extractPrivInfo()))) {
1223 $sql_query2 = 'GRANT ' . join(', ', PMA_extractPrivInfo())
1224 . ' ON ' . $db_and_table
1225 . ' TO \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\'';
1228 * @todo similar code appears twice in this script
1230 if ((isset($Grant_priv) && $Grant_priv == 'Y')
1231 ||
(! isset($dbname)
1232 && (isset($max_questions) ||
isset($max_connections)
1233 ||
isset($max_updates) ||
isset($max_user_connections)))
1235 $sql_query2 .= 'WITH';
1236 if (isset($Grant_priv) && $Grant_priv == 'Y') {
1237 $sql_query2 .= ' GRANT OPTION';
1239 if (isset($max_questions)) {
1240 $max_questions = max(0, (int)$max_questions);
1241 $sql_query2 .= ' MAX_QUERIES_PER_HOUR ' . $max_questions;
1243 if (isset($max_connections)) {
1244 $max_connections = max(0, (int)$max_connections);
1245 $sql_query2 .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections;
1247 if (isset($max_updates)) {
1248 $max_updates = max(0, (int)$max_updates);
1249 $sql_query2 .= ' MAX_UPDATES_PER_HOUR ' . $max_updates;
1251 if (isset($max_user_connections)) {
1252 $max_user_connections = max(0, (int)$max_user_connections);
1253 $sql_query2 .= ' MAX_USER_CONNECTIONS ' . $max_user_connections;
1258 if (! PMA_DBI_try_query($sql_query0)) {
1259 // This might fail when the executing user does not have ALL PRIVILEGES himself.
1260 // See https://sourceforge.net/tracker/index.php?func=detail&aid=3285929&group_id=23067&atid=377408
1263 if (isset($sql_query1) && ! PMA_DBI_try_query($sql_query1)) {
1264 // this one may fail, too...
1267 if (isset($sql_query2)) {
1268 PMA_DBI_query($sql_query2);
1272 $sql_query = $sql_query0 . ' ' . $sql_query1 . ' ' . $sql_query2;
1273 $message = PMA_Message
::success(__('You have updated the privileges for %s.'));
1274 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1279 * Revokes Privileges
1281 if (isset($_REQUEST['revokeall'])) {
1282 $db_and_table = PMA_wildcardEscapeForGrant($dbname, isset($tablename) ?
$tablename : '');
1284 $sql_query0 = 'REVOKE ALL PRIVILEGES ON ' . $db_and_table
1285 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1286 $sql_query1 = 'REVOKE GRANT OPTION ON ' . $db_and_table
1287 . ' FROM \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\';';
1289 PMA_DBI_query($sql_query0);
1290 if (! PMA_DBI_try_query($sql_query1)) {
1291 // this one may fail, too...
1294 $sql_query = $sql_query0 . ' ' . $sql_query1;
1295 $message = PMA_Message
::success(__('You have revoked the privileges for %s'));
1296 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1297 if (! isset($tablename)) {
1306 * Updates the password
1308 if (isset($_REQUEST['change_pw'])) {
1309 // similar logic in user_password.php
1312 if ($nopass == 0 && isset($pma_pw) && isset($pma_pw2)) {
1313 if ($pma_pw != $pma_pw2) {
1314 $message = PMA_Message
::error(__('The passwords aren\'t the same!'));
1315 } elseif (empty($pma_pw) ||
empty($pma_pw2)) {
1316 $message = PMA_Message
::error(__('The password is empty!'));
1320 // here $nopass could be == 1
1321 if (empty($message)) {
1323 $hashing_function = (! empty($pw_hash) && $pw_hash == 'old' ?
'OLD_' : '')
1326 // in $sql_query which will be displayed, hide the password
1327 $sql_query = 'SET PASSWORD FOR \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\' = ' . (($pma_pw == '') ?
'\'\'' : $hashing_function . '(\'' . preg_replace('@.@s', '*', $pma_pw) . '\')');
1328 $local_query = 'SET PASSWORD FOR \'' . PMA_sqlAddSlashes($username) . '\'@\'' . PMA_sqlAddSlashes($hostname) . '\' = ' . (($pma_pw == '') ?
'\'\'' : $hashing_function . '(\'' . PMA_sqlAddSlashes($pma_pw) . '\')');
1329 PMA_DBI_try_query($local_query)
1330 or PMA_mysqlDie(PMA_DBI_getError(), $sql_query, false, $err_url);
1331 $message = PMA_Message
::success(__('The password for %s was changed successfully.'));
1332 $message->addParam('\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname) . '\'');
1339 * (Changes / copies a user, part IV)
1342 if (isset($_REQUEST['delete']) ||
(isset($_REQUEST['change_copy']) && $_REQUEST['mode'] < 4)) {
1343 if (isset($_REQUEST['change_copy'])) {
1344 $selected_usr = array($old_username . '&#27;' . $old_hostname);
1346 $selected_usr = $_REQUEST['selected_usr'];
1349 foreach ($selected_usr as $each_user) {
1350 list($this_user, $this_host) = explode('&#27;', $each_user);
1351 $queries[] = '# ' . sprintf(__('Deleting %s'), '\'' . $this_user . '\'@\'' . $this_host . '\'') . ' ...';
1352 $queries[] = 'DROP USER \'' . PMA_sqlAddSlashes($this_user) . '\'@\'' . PMA_sqlAddSlashes($this_host) . '\';';
1354 if (isset($_REQUEST['drop_users_db'])) {
1355 $queries[] = 'DROP DATABASE IF EXISTS ' . PMA_backquote($this_user) . ';';
1356 $GLOBALS['reload'] = true;
1358 if ($GLOBALS['is_ajax_request'] != true) {
1359 PMA_reloadNavigation();
1363 if (empty($_REQUEST['change_copy'])) {
1364 if (empty($queries)) {
1365 $message = PMA_Message
::error(__('No users selected for deleting!'));
1367 if ($_REQUEST['mode'] == 3) {
1368 $queries[] = '# ' . __('Reloading the privileges') . ' ...';
1369 $queries[] = 'FLUSH PRIVILEGES;';
1371 $drop_user_error = '';
1372 foreach ($queries as $sql_query) {
1373 if ($sql_query{0} != '#') {
1374 if (! PMA_DBI_try_query($sql_query, $GLOBALS['userlink'])) {
1375 $drop_user_error .= PMA_DBI_getError() . "\n";
1379 // tracking sets this, causing the deleted db to be shown in navi
1380 unset($GLOBALS['db']);
1382 $sql_query = join("\n", $queries);
1383 if (! empty($drop_user_error)) {
1384 $message = PMA_Message
::rawError($drop_user_error);
1386 $message = PMA_Message
::success(__('The selected users have been deleted successfully.'));
1395 * Changes / copies a user, part V
1397 if (isset($_REQUEST['change_copy'])) {
1399 foreach ($queries as $sql_query) {
1400 if ($sql_query{0} != '#') {
1401 PMA_DBI_query($sql_query);
1403 // when there is a query containing a hidden password, take it
1404 // instead of the real query sent
1405 if (isset($queries_for_display[$tmp_count])) {
1406 $queries[$tmp_count] = $queries_for_display[$tmp_count];
1410 $message = PMA_Message
::success();
1411 $sql_query = join("\n", $queries);
1416 * Reloads the privilege tables into memory
1418 if (isset($_REQUEST['flush_privileges'])) {
1419 $sql_query = 'FLUSH PRIVILEGES;';
1420 PMA_DBI_query($sql_query);
1421 $message = PMA_Message
::success(__('The privileges were reloaded successfully.'));
1425 * defines some standard links
1427 $link_edit = '<a class="edit_user_anchor ' . $conditional_class . '" href="server_privileges.php?' . str_replace('%', '%%', $GLOBALS['url_query'])
1428 . '&username=%s'
1429 . '&hostname=%s'
1431 . '&tablename=%s">'
1432 . PMA_getIcon('b_usredit.png', __('Edit Privileges'))
1435 $link_revoke = '<a href="server_privileges.php?' . str_replace('%', '%%', $GLOBALS['url_query'])
1436 . '&username=%s'
1437 . '&hostname=%s'
1439 . '&tablename=%s'
1440 . '&revokeall=1">'
1441 . PMA_getIcon('b_usrdrop.png', __('Revoke'))
1444 $link_export = '<a class="export_user_anchor ' . $conditional_class . '" href="server_privileges.php?' . str_replace('%', '%%', $GLOBALS['url_query'])
1445 . '&username=%s'
1446 . '&hostname=%s'
1449 . PMA_getIcon('b_tblexport.png', __('Export'))
1453 * If we are in an Ajax request for Create User/Edit User/Revoke User/
1454 * Flush Privileges, show $message and exit.
1456 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'])) {
1458 if (isset($sql_query)) {
1459 $extra_data['sql_query'] = PMA_showMessage(null, $sql_query);
1462 if (isset($_REQUEST['adduser_submit']) ||
isset($_REQUEST['change_copy'])) {
1464 * generate html on the fly for the new user that was just created.
1466 $new_user_string = '<tr>'."\n"
1467 .'<td> <input type="checkbox" name="selected_usr[]" id="checkbox_sel_users_" value="' . htmlspecialchars($username) . '&#27;' . htmlspecialchars($hostname) . '" /> </td>' . "\n"
1468 .'<td><label for="checkbox_sel_users_">' . (empty($username) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($username) ) . '</label></td>' . "\n"
1469 .'<td>' . htmlspecialchars($hostname) . '</td>' . "\n";
1470 $new_user_string .= '<td>';
1472 if (! empty($password) ||
isset($pma_pw)) {
1473 $new_user_string .= __('Yes');
1475 $new_user_string .= '<span style="color: #FF0000">' . __('No') . '</span>';
1478 $new_user_string .= '</td>'."\n";
1479 $new_user_string .= '<td><tt>' . join(', ', PMA_extractPrivInfo('', true)) . '</tt></td>'; //Fill in privileges here
1480 $new_user_string .= '<td>';
1482 if ((isset($Grant_priv) && $Grant_priv == 'Y')) {
1483 $new_user_string .= __('Yes');
1485 $new_user_string .= __('No');
1488 $new_user_string .='</td>';
1490 $new_user_string .= '<td>' . sprintf($link_edit, urlencode($username), urlencode($hostname), '', '') . '</td>' . "\n";
1491 $new_user_string .= '<td>' . sprintf($link_export, urlencode($username), urlencode($hostname), (isset($initial) ?
$initial : '')) . '</td>' . "\n";
1493 $new_user_string .= '</tr>';
1495 $extra_data['new_user_string'] = $new_user_string;
1498 * Generate the string for this alphabet's initial, to update the user
1501 $new_user_initial = strtoupper(substr($username, 0, 1));
1502 $new_user_initial_string = '<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&initial=' . $new_user_initial
1503 .'">' . $new_user_initial . '</a>';
1504 $extra_data['new_user_initial'] = $new_user_initial;
1505 $extra_data['new_user_initial_string'] = $new_user_initial_string;
1508 if (isset($update_privs)) {
1509 $extra_data['db_specific_privs'] = false;
1510 $extra_data['db_wildcard_privs'] = false;
1511 if (isset($dbname_is_wildcard)) {
1512 $extra_data['db_specific_privs'] = ! $dbname_is_wildcard;
1513 $extra_data['db_wildcard_privs'] = $dbname_is_wildcard;
1515 $new_privileges = join(', ', PMA_extractPrivInfo('', true));
1517 $extra_data['new_privileges'] = $new_privileges;
1520 if ($message instanceof PMA_Message
) {
1521 PMA_ajaxResponse($message, $message->isSuccess(), $extra_data);
1526 * Displays the links
1528 if (isset($viewing_mode) && $viewing_mode == 'db') {
1530 $url_query .= '&goto=db_operations.php';
1532 // Gets the database structure
1533 $sub_part = '_structure';
1534 include './libraries/db_info.inc.php';
1537 include './libraries/server_links.inc.php';
1545 // export user definition
1546 if (isset($_REQUEST['export'])) {
1547 $title = __('User') . ' `' . htmlspecialchars($username) . '`@`' . htmlspecialchars($hostname) . '`';
1548 $response = '<textarea cols="' . $GLOBALS['cfg']['TextareaCols'] . '" rows="' . $GLOBALS['cfg']['TextareaRows'] . '">';
1549 $grants = PMA_DBI_fetch_result("SHOW GRANTS FOR '" . PMA_sqlAddSlashes($username) . "'@'" . PMA_sqlAddSlashes($hostname) . "'");
1550 foreach ($grants as $one_grant) {
1551 $response .= $one_grant . ";\n\n";
1553 $response .= '</textarea>';
1554 unset($username, $hostname, $grants, $one_grant);
1555 if ($GLOBALS['is_ajax_request']) {
1556 PMA_ajaxResponse($response, 1, array('title' => $title));
1558 echo "<h2>$title</h2>$response";
1562 if (empty($_REQUEST['adduser']) && (! isset($checkprivs) ||
! strlen($checkprivs))) {
1563 if (! isset($username)) {
1564 // No username is given --> display the overview
1566 . PMA_getIcon('b_usrlist.png')
1567 . __('Users overview') . "\n"
1570 $sql_query = 'SELECT *,' .
1571 " IF(`Password` = _latin1 '', 'N', 'Y') AS 'Password'" .
1572 ' FROM `mysql`.`user`';
1574 $sql_query .= (isset($initial) ?
PMA_rangeOfUsers($initial) : '');
1576 $sql_query .= ' ORDER BY `User` ASC, `Host` ASC;';
1577 $res = PMA_DBI_try_query($sql_query, null, PMA_DBI_QUERY_STORE
);
1580 // the query failed! This may have two reasons:
1581 // - the user does not have enough privileges
1582 // - the privilege tables use a structure of an earlier version.
1583 // so let's try a more simple query
1585 $sql_query = 'SELECT * FROM `mysql`.`user`';
1586 $res = PMA_DBI_try_query($sql_query, null, PMA_DBI_QUERY_STORE
);
1589 PMA_Message
::error(__('No Privileges'))->display();
1590 PMA_DBI_free_result($res);
1593 // This message is hardcoded because I will replace it by
1594 // a automatic repair feature soon.
1595 $raw = 'Your privilege table structure seems to be older than'
1596 . ' this MySQL version!<br />'
1597 . 'Please run the <tt>mysql_upgrade</tt> command'
1598 . '(<tt>mysql_fix_privilege_tables</tt> on older systems)'
1599 . ' that should be included in your MySQL server distribution'
1600 . ' to solve this problem!';
1601 PMA_Message
::rawError($raw)->display();
1605 // we also want users not in table `user` but in other table
1606 $tables = PMA_DBI_fetch_result('SHOW TABLES FROM `mysql`;');
1608 $tables_to_search_for_users = array(
1609 'user', 'db', 'tables_priv', 'columns_priv', 'procs_priv',
1612 $db_rights_sqls = array();
1613 foreach ($tables_to_search_for_users as $table_search_in) {
1614 if (in_array($table_search_in, $tables)) {
1615 $db_rights_sqls[] = 'SELECT DISTINCT `User`, `Host` FROM `mysql`.`' . $table_search_in . '` ' . (isset($initial) ?
PMA_rangeOfUsers($initial) : '');
1619 $user_defaults = array(
1623 'Grant_priv' => 'N',
1624 'privs' => array('USAGE'),
1627 // for all initials, even non A-Z
1628 $array_initials = array();
1630 $db_rights = array();
1632 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
1633 .' ORDER BY `User` ASC, `Host` ASC';
1635 $db_rights_result = PMA_DBI_query($db_rights_sql);
1637 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
1638 $db_rights_row = array_merge($user_defaults, $db_rights_row);
1639 $db_rights[$db_rights_row['User']][$db_rights_row['Host']]
1642 PMA_DBI_free_result($db_rights_result);
1643 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
1647 * Displays the initials
1648 * In an Ajax request, we don't need to show this.
1649 * Also not necassary if there is less than 20 privileges
1651 if ($GLOBALS['is_ajax_request'] != true && PMA_DBI_num_rows($res) > 20 ) {
1653 // initialize to false the letters A-Z
1654 for ($letter_counter = 1; $letter_counter < 27; $letter_counter++
) {
1655 if (! isset($array_initials[chr($letter_counter +
64)])) {
1656 $array_initials[chr($letter_counter +
64)] = false;
1660 $initials = PMA_DBI_try_query('SELECT DISTINCT UPPER(LEFT(`User`,1)) FROM `user` ORDER BY `User` ASC', null, PMA_DBI_QUERY_STORE
);
1661 while (list($tmp_initial) = PMA_DBI_fetch_row($initials)) {
1662 $array_initials[$tmp_initial] = true;
1665 // Display the initials, which can be any characters, not
1666 // just letters. For letters A-Z, we add the non-used letters
1669 uksort($array_initials, "strnatcasecmp");
1671 echo '<table id="initials_table" class="' . $conditional_class . '" <cellspacing="5"><tr>';
1672 foreach ($array_initials as $tmp_initial => $initial_was_found) {
1673 if ($initial_was_found) {
1674 echo '<td><a href="server_privileges.php?' . $GLOBALS['url_query'] . '&initial=' . urlencode($tmp_initial) . '">' . $tmp_initial . '</a></td>' . "\n";
1676 echo '<td>' . $tmp_initial . '</td>';
1679 echo '<td><a href="server_privileges.php?' . $GLOBALS['url_query'] . '&showall=1" class="nowrap">[' . __('Show all') . ']</a></td>' . "\n";
1680 echo '</tr></table>';
1684 * Display the user overview
1685 * (if less than 50 users, display them immediately)
1688 if (isset($initial) ||
isset($showall) ||
PMA_DBI_num_rows($res) < 50) {
1690 while ($row = PMA_DBI_fetch_assoc($res)) {
1691 $row['privs'] = PMA_extractPrivInfo($row, true);
1692 $db_rights[$row['User']][$row['Host']] = $row;
1694 @PMA_DBI_free_result
($res);
1697 echo '<form name="usersForm" id="usersForm" action="server_privileges.php" method="post">' . "\n"
1698 . PMA_generate_common_hidden_inputs('', '')
1699 . ' <table id="tableuserrights" class="data">' . "\n"
1701 . ' <tr><th></th>' . "\n"
1702 . ' <th>' . __('User') . '</th>' . "\n"
1703 . ' <th>' . __('Host') . '</th>' . "\n"
1704 . ' <th>' . __('Password') . '</th>' . "\n"
1705 . ' <th>' . __('Global privileges') . ' '
1706 . PMA_showHint(__('Note: MySQL privilege names are expressed in English')) . '</th>' . "\n"
1707 . ' <th>' . __('Grant') . '</th>' . "\n"
1708 . ' <th colspan="2">' . __('Action') . '</th>' . "\n";
1709 echo ' </tr>' . "\n";
1710 echo ' </thead>' . "\n";
1711 echo ' <tbody>' . "\n";
1713 $index_checkbox = -1;
1714 foreach ($db_rights as $user) {
1717 foreach ($user as $host) {
1719 echo ' <tr class="' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
1720 . ' <td><input type="checkbox" name="selected_usr[]" id="checkbox_sel_users_'
1721 . $index_checkbox . '" value="'
1722 . htmlspecialchars($host['User'] . '&#27;' . $host['Host'])
1724 . (empty($GLOBALS['checkall']) ?
'' : ' checked="checked"')
1726 . ' <td><label for="checkbox_sel_users_' . $index_checkbox . '">' . (empty($host['User']) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($host['User'])) . '</label></td>' . "\n"
1727 . ' <td>' . htmlspecialchars($host['Host']) . '</td>' . "\n";
1729 switch ($host['Password']) {
1734 echo '<span style="color: #FF0000">' . __('No') . '</span>';
1736 // this happens if this is a definition not coming from mysql.user
1738 echo '--'; // in future version, replace by "not present"
1742 . ' <td><tt>' . "\n"
1743 . ' ' . implode(',' . "\n" . ' ', $host['privs']) . "\n"
1744 . ' </tt></td>' . "\n"
1745 . ' <td>' . ($host['Grant_priv'] == 'Y' ?
__('Yes') : __('No')) . '</td>' . "\n"
1746 . ' <td align="center">';
1747 printf($link_edit, urlencode($host['User']), urlencode($host['Host']), '', '');
1749 echo '<td align="center">';
1750 printf($link_export, urlencode($host['User']), urlencode($host['Host']), (isset($initial) ?
$initial : ''));
1753 $odd_row = ! $odd_row;
1757 unset($user, $host, $odd_row);
1758 echo ' </tbody></table>' . "\n"
1759 .'<img class="selectallarrow"'
1760 .' src="' . $pmaThemeImage . 'arrow_' . $text_dir . '.png"'
1761 .' width="38" height="22"'
1762 .' alt="' . __('With selected:') . '" />' . "\n"
1763 .'<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&checkall=1"'
1764 .' onclick="if (markAllRows(\'usersForm\')) return false;">'
1765 . __('Check All') . '</a>' . "\n"
1767 .'<a href="server_privileges.php?' . $GLOBALS['url_query'] . '"'
1768 .' onclick="if (unMarkAllRows(\'usersForm\')) return false;">'
1769 . __('Uncheck All') . '</a>' . "\n";
1771 // add/delete user fieldset
1772 echo ' <fieldset id="fieldset_add_user">' . "\n"
1773 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1" class="' . $conditional_class . '">' . "\n"
1774 . PMA_getIcon('b_usradd.png')
1775 . ' ' . __('Add user') . '</a>' . "\n"
1776 . ' </fieldset>' . "\n"
1777 . ' <fieldset id="fieldset_delete_user">'
1778 . ' <legend>' . "\n"
1779 . PMA_getIcon('b_usrdrop.png')
1780 . ' ' . __('Remove selected users') . '' . "\n"
1781 . ' </legend>' . "\n"
1782 . ' <input type="hidden" name="mode" value="2" />' . "\n"
1783 . '(' . __('Revoke all active privileges from the users and delete them afterwards.') . ')<br />' . "\n"
1784 . ' <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"
1785 . ' <label for="checkbox_drop_users_db" title="' . __('Drop the databases that have the same names as the users.') . '">' . "\n"
1786 . ' ' . __('Drop the databases that have the same names as the users.') . "\n"
1787 . ' </label>' . "\n"
1788 . ' </fieldset>' . "\n"
1789 . ' <fieldset id="fieldset_delete_user_footer" class="tblFooters">' . "\n"
1790 . ' <input type="submit" name="delete" value="' . __('Go') . '" id="buttonGo" class="' . $conditional_class . '"/>' . "\n"
1791 . ' </fieldset>' . "\n"
1796 echo ' <fieldset id="fieldset_add_user">' . "\n"
1797 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1" class="' . $conditional_class . '">' . "\n"
1798 . PMA_getIcon('b_usradd.png')
1799 . ' ' . __('Add user') . '</a>' . "\n"
1800 . ' </fieldset>' . "\n";
1801 } // end if (display overview)
1803 if ($GLOBALS['is_ajax_request']) {
1807 $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
);
1808 $flushnote->addParam('<a href="server_privileges.php?' . $GLOBALS['url_query'] . '&flush_privileges=1" id="reload_privileges_anchor" class="' . $conditional_class . '">', false);
1809 $flushnote->addParam('</a>', false);
1810 $flushnote->display();
1816 // A user was selected -> display the user's properties
1818 // In an Ajax request, prevent cached values from showing
1819 if ($GLOBALS['is_ajax_request'] == true) {
1820 header('Cache-Control: no-cache');
1824 . PMA_getIcon('b_usredit.png')
1825 . __('Edit Privileges') . ': '
1828 if (isset($dbname)) {
1829 echo ' <i><a href="server_privileges.php?'
1830 . $GLOBALS['url_query'] . '&username=' . htmlspecialchars(urlencode($username))
1831 . '&hostname=' . htmlspecialchars(urlencode($hostname)) . '&dbname=&tablename=">\''
1832 . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname)
1833 . '\'</a></i>' . "\n";
1834 $url_dbname = urlencode(str_replace(array('\_', '\%'), array('_', '%'), $dbname));
1836 echo ' - ' . ($dbname_is_wildcard ?
__('Databases') : __('Database') );
1837 if (isset($tablename)) {
1838 echo ' <i><a href="server_privileges.php?' . $GLOBALS['url_query']
1839 . '&username=' . htmlspecialchars(urlencode($username)) . '&hostname=' . htmlspecialchars(urlencode($hostname))
1840 . '&dbname=' . htmlspecialchars($url_dbname) . '&tablename=">' . htmlspecialchars($dbname) . '</a></i>';
1841 echo ' - ' . __('Table') . ' <i>' . htmlspecialchars($tablename) . '</i>';
1843 echo ' <i>' . htmlspecialchars($dbname) . '</i>';
1847 echo ' <i>\'' . htmlspecialchars($username) . '\'@\'' . htmlspecialchars($hostname)
1851 echo '</h2>' . "\n";
1854 $sql = "SELECT '1' FROM `mysql`.`user`"
1855 . " WHERE `User` = '" . PMA_sqlAddSlashes($username) . "'"
1856 . " AND `Host` = '" . PMA_sqlAddSlashes($hostname) . "';";
1857 $user_does_not_exists = (bool) ! PMA_DBI_fetch_value($sql);
1859 if ($user_does_not_exists) {
1860 PMA_Message
::error(__('The selected user was not found in the privilege table.'))->display();
1861 PMA_displayLoginInformationFields();
1862 //require './libraries/footer.inc.php';
1865 echo '<form name="usersForm" id="addUsersForm_' . $random_n . '" action="server_privileges.php" method="post">' . "\n";
1867 'username' => $username,
1868 'hostname' => $hostname,
1870 if (isset($dbname)) {
1871 $_params['dbname'] = $dbname;
1872 if (isset($tablename)) {
1873 $_params['tablename'] = $tablename;
1876 echo PMA_generate_common_hidden_inputs($_params);
1878 PMA_displayPrivTable(
1879 PMA_ifSetOr($dbname, '*', 'length'),
1880 PMA_ifSetOr($tablename, '*', 'length')
1883 echo '</form>' . "\n";
1885 if (! isset($tablename) && empty($dbname_is_wildcard)) {
1887 // no table name was given, display all table specific rights
1888 // but only if $dbname contains no wildcards
1891 echo '<form action="server_privileges.php" id="db_or_table_specific_priv" method="post">' . "\n"
1892 . PMA_generate_common_hidden_inputs('', '')
1893 . '<input type="hidden" name="username" value="' . htmlspecialchars($username) . '" />' . "\n"
1894 . '<input type="hidden" name="hostname" value="' . htmlspecialchars($hostname) . '" />' . "\n"
1895 . '<fieldset>' . "\n"
1896 . '<legend>' . (! isset($dbname) ?
__('Database-specific privileges') : __('Table-specific privileges')) . '</legend>' . "\n"
1897 . '<table class="data">' . "\n"
1899 . '<tr><th>' . (! isset($dbname) ?
__('Database') : __('Table')) . '</th>' . "\n"
1900 . ' <th>' . __('Privileges') . '</th>' . "\n"
1901 . ' <th>' . __('Grant') . '</th>' . "\n"
1902 . ' <th>' . (! isset($dbname) ?
__('Table-specific privileges') : __('Column-specific privileges')) . '</th>' . "\n"
1903 . ' <th colspan="2">' . __('Action') . '</th>' . "\n"
1908 $user_host_condition = ' WHERE `User`'
1909 . ' = \'' . PMA_sqlAddSlashes($username) . "'"
1911 . ' = \'' . PMA_sqlAddSlashes($hostname) . "'";
1916 // we also want privielgs for this user not in table `db` but in other table
1917 $tables = PMA_DBI_fetch_result('SHOW TABLES FROM `mysql`;');
1918 if (! isset($dbname)) {
1920 // no db name given, so we want all privs for the given user
1922 $tables_to_search_for_users = array(
1923 'tables_priv', 'columns_priv',
1926 $db_rights_sqls = array();
1927 foreach ($tables_to_search_for_users as $table_search_in) {
1928 if (in_array($table_search_in, $tables)) {
1929 $db_rights_sqls[] = '
1930 SELECT DISTINCT `Db`
1931 FROM `mysql`.' . PMA_backquote($table_search_in)
1932 . $user_host_condition;
1936 $user_defaults = array(
1938 'Grant_priv' => 'N',
1939 'privs' => array('USAGE'),
1940 'Table_privs' => true,
1944 $db_rights = array();
1946 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
1947 .' ORDER BY `Db` ASC';
1949 $db_rights_result = PMA_DBI_query($db_rights_sql);
1951 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
1952 $db_rights_row = array_merge($user_defaults, $db_rights_row);
1953 // only Db names in the table `mysql`.`db` uses wildcards
1954 // as we are in the db specific rights display we want
1955 // all db names escaped, also from other sources
1956 $db_rights_row['Db'] = PMA_escape_mysql_wildcards(
1957 $db_rights_row['Db']
1959 $db_rights[$db_rights_row['Db']] = $db_rights_row;
1962 PMA_DBI_free_result($db_rights_result);
1963 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
1965 $sql_query = 'SELECT * FROM `mysql`.`db`' . $user_host_condition . ' ORDER BY `Db` ASC';
1966 $res = PMA_DBI_query($sql_query);
1969 while ($row = PMA_DBI_fetch_assoc($res)) {
1970 if (isset($db_rights[$row['Db']])) {
1971 $db_rights[$row['Db']] = array_merge($db_rights[$row['Db']], $row);
1973 $db_rights[$row['Db']] = $row;
1975 // there are db specific rights for this user
1976 // so we can drop this db rights
1977 $db_rights[$row['Db']]['can_delete'] = true;
1979 PMA_DBI_free_result($res);
1984 // db name was given,
1985 // so we want all user specific rights for this db
1987 $user_host_condition .=
1989 .' LIKE \'' . PMA_sqlAddSlashes($dbname, true) . "'";
1991 $tables_to_search_for_users = array(
1995 $db_rights_sqls = array();
1996 foreach ($tables_to_search_for_users as $table_search_in) {
1997 if (in_array($table_search_in, $tables)) {
1998 $db_rights_sqls[] = '
1999 SELECT DISTINCT `Table_name`
2000 FROM `mysql`.' . PMA_backquote($table_search_in)
2001 . $user_host_condition;
2005 $user_defaults = array(
2007 'Grant_priv' => 'N',
2008 'privs' => array('USAGE'),
2009 'Column_priv' => true,
2013 $db_rights = array();
2015 $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')'
2016 .' ORDER BY `Table_name` ASC';
2018 $db_rights_result = PMA_DBI_query($db_rights_sql);
2020 while ($db_rights_row = PMA_DBI_fetch_assoc($db_rights_result)) {
2021 $db_rights_row = array_merge($user_defaults, $db_rights_row);
2022 $db_rights[$db_rights_row['Table_name']] = $db_rights_row;
2024 PMA_DBI_free_result($db_rights_result);
2025 unset($db_rights_sql, $db_rights_sqls, $db_rights_result, $db_rights_row);
2027 $sql_query = 'SELECT `Table_name`,'
2029 .' IF(`Column_priv` = _latin1 \'\', 0, 1)'
2030 .' AS \'Column_priv\''
2031 .' FROM `mysql`.`tables_priv`'
2032 . $user_host_condition
2033 .' ORDER BY `Table_name` ASC;';
2034 $res = PMA_DBI_query($sql_query);
2037 while ($row = PMA_DBI_fetch_assoc($res)) {
2038 if (isset($db_rights[$row['Table_name']])) {
2039 $db_rights[$row['Table_name']] = array_merge($db_rights[$row['Table_name']], $row);
2041 $db_rights[$row['Table_name']] = $row;
2044 PMA_DBI_free_result($res);
2050 if (count($db_rights) < 1) {
2051 echo '<tr class="odd">' . "\n"
2052 . ' <td colspan="6"><center><i>' . __('None') . '</i></center></td>' . "\n"
2056 $found_rows = array();
2057 //while ($row = PMA_DBI_fetch_assoc($res)) {
2058 foreach ($db_rights as $row) {
2059 $found_rows[] = (! isset($dbname)) ?
$row['Db'] : $row['Table_name'];
2061 echo '<tr class="' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
2062 . ' <td>' . htmlspecialchars((! isset($dbname)) ?
$row['Db'] : $row['Table_name']) . '</td>' . "\n"
2063 . ' <td><tt>' . "\n"
2064 . ' ' . join(',' . "\n" . ' ', PMA_extractPrivInfo($row, true)) . "\n"
2065 . ' </tt></td>' . "\n"
2066 . ' <td>' . ((((! isset($dbname)) && $row['Grant_priv'] == 'Y') ||
(isset($dbname) && in_array('Grant', explode(',', $row['Table_priv'])))) ?
__('Yes') : __('No')) . '</td>' . "\n"
2068 if (! empty($row['Table_privs']) ||
! empty ($row['Column_priv'])) {
2077 htmlspecialchars(urlencode($username)),
2078 urlencode(htmlspecialchars($hostname)),
2079 urlencode((! isset($dbname)) ?
$row['Db'] : htmlspecialchars($dbname)),
2080 urlencode((! isset($dbname)) ?
'' : $row['Table_name'])
2084 if (! empty($row['can_delete']) ||
isset($row['Table_name']) && strlen($row['Table_name'])) {
2087 htmlspecialchars(urlencode($username)),
2088 urlencode(htmlspecialchars($hostname)),
2089 urlencode((! isset($dbname)) ?
$row['Db'] : htmlspecialchars($dbname)),
2090 urlencode((! isset($dbname)) ?
'' : $row['Table_name'])
2095 $odd_row = ! $odd_row;
2099 echo '</tbody>' . "\n"
2100 . '</table>' . "\n";
2102 if (! isset($dbname)) {
2104 // no database name was given, display select db
2106 $pred_db_array =PMA_DBI_fetch_result('SHOW DATABASES;');
2108 echo ' <label for="text_dbname">' . __('Add privileges on the following database') . ':</label>' . "\n";
2109 if (! empty($pred_db_array)) {
2110 echo ' <select name="pred_dbname" class="autosubmit">' . "\n"
2111 . ' <option value="" selected="selected">' . __('Use text field') . ':</option>' . "\n";
2112 foreach ($pred_db_array as $current_db) {
2113 $current_db = PMA_escape_mysql_wildcards($current_db);
2114 // cannot use array_diff() once, outside of the loop,
2115 // because the list of databases has special characters
2116 // already escaped in $found_rows,
2117 // contrary to the output of SHOW DATABASES
2118 if (empty($found_rows) ||
! in_array($current_db, $found_rows)) {
2119 echo ' <option value="' . htmlspecialchars($current_db) . '">'
2120 . htmlspecialchars($current_db) . '</option>' . "\n";
2123 echo ' </select>' . "\n";
2125 echo ' <input type="text" id="text_dbname" name="dbname" />' . "\n"
2126 . PMA_showHint(__('Wildcards % and _ should be escaped with a \ to use them literally'));
2128 echo ' <input type="hidden" name="dbname" value="' . htmlspecialchars($dbname) . '"/>' . "\n"
2129 . ' <label for="text_tablename">' . __('Add privileges on the following table') . ':</label>' . "\n";
2130 if ($res = @PMA_DBI_try_query
('SHOW TABLES FROM ' . PMA_backquote(PMA_unescape_mysql_wildcards($dbname)) . ';', null, PMA_DBI_QUERY_STORE
)) {
2131 $pred_tbl_array = array();
2132 while ($row = PMA_DBI_fetch_row($res)) {
2133 if (! isset($found_rows) ||
! in_array($row[0], $found_rows)) {
2134 $pred_tbl_array[] = $row[0];
2137 PMA_DBI_free_result($res);
2139 if (! empty($pred_tbl_array)) {
2140 echo ' <select name="pred_tablename" class="autosubmit">' . "\n"
2141 . ' <option value="" selected="selected">' . __('Use text field') . ':</option>' . "\n";
2142 foreach ($pred_tbl_array as $current_table) {
2143 echo ' <option value="' . htmlspecialchars($current_table) . '">' . htmlspecialchars($current_table) . '</option>' . "\n";
2145 echo ' </select>' . "\n";
2150 echo ' <input type="text" id="text_tablename" name="tablename" />' . "\n";
2152 echo '</fieldset>' . "\n";
2153 echo '<fieldset class="tblFooters">' . "\n"
2154 . ' <input type="submit" value="' . __('Go') . '" />'
2155 . '</fieldset>' . "\n"
2160 // Provide a line with links to the relevant database and table
2161 if (isset($dbname) && empty($dbname_is_wildcard)) {
2162 echo '[ ' . __('Database')
2163 . ' <a href="' . $GLOBALS['cfg']['DefaultTabDatabase'] . '?'
2164 . $GLOBALS['url_query'] . '&db=' . $url_dbname . '&reload=1">'
2165 . htmlspecialchars($dbname) . ': ' . PMA_getTitleForTarget($GLOBALS['cfg']['DefaultTabDatabase']) . "</a> ]\n";
2167 if (isset($tablename)) {
2168 echo ' [ ' . __('Table') . ' <a href="'
2169 . $GLOBALS['cfg']['DefaultTabTable'] . '?' . $GLOBALS['url_query']
2170 . '&db=' . $url_dbname . '&table=' . htmlspecialchars(urlencode($tablename))
2171 . '&reload=1">' . htmlspecialchars($tablename) . ': '
2172 . PMA_getTitleForTarget($GLOBALS['cfg']['DefaultTabTable'])
2178 if (! isset($dbname) && ! $user_does_not_exists) {
2179 include_once './libraries/display_change_password.lib.php';
2181 echo '<form action="server_privileges.php" method="post" class="copyUserForm">' . "\n"
2182 . PMA_generate_common_hidden_inputs('', '')
2183 . '<input type="hidden" name="old_username" value="' . htmlspecialchars($username) . '" />' . "\n"
2184 . '<input type="hidden" name="old_hostname" value="' . htmlspecialchars($hostname) . '" />' . "\n"
2185 . '<fieldset id="fieldset_change_copy_user">' . "\n"
2186 . ' <legend>' . __('Change Login Information / Copy User') . '</legend>' . "\n";
2187 PMA_displayLoginInformationFields('change');
2188 echo ' <fieldset>' . "\n"
2189 . ' <legend>' . __('Create a new user with the same privileges and ...') . '</legend>' . "\n";
2191 '4' => __('... keep the old one.'),
2192 '1' => __('... delete the old one from the user tables.'),
2193 '2' => __('... revoke all active privileges from the old one and delete it afterwards.'),
2194 '3' => __('... delete the old one from the user tables and reload the privileges afterwards.'));
2195 PMA_display_html_radio('mode', $choices, '4', true);
2198 echo ' </fieldset>' . "\n"
2199 . '</fieldset>' . "\n"
2200 . '<fieldset id="fieldset_change_copy_user_footer" class="tblFooters">' . "\n"
2201 . ' <input type="submit" name="change_copy" value="' . __('Go') . '" />' . "\n"
2202 . '</fieldset>' . "\n"
2206 } elseif (isset($_REQUEST['adduser'])) {
2209 $GLOBALS['url_query'] .= '&adduser=1';
2211 . PMA_getIcon('b_usradd.png') . __('Add user') . "\n"
2213 . '<form name="usersForm" id="addUsersForm_' . $random_n . '" action="server_privileges.php" method="post">' . "\n"
2214 . PMA_generate_common_hidden_inputs('', '');
2215 PMA_displayLoginInformationFields('new');
2216 echo '<fieldset id="fieldset_add_user_database">' . "\n"
2217 . '<legend>' . __('Database for user') . '</legend>' . "\n";
2219 $default_choice = 0;
2221 '0' => _pgettext('Create none database for user', 'None'),
2222 '1' => __('Create database with same name and grant all privileges'),
2223 '2' => __('Grant all privileges on wildcard name (username\\_%)'));
2225 if (! empty($dbname) ) {
2226 $choices['3'] = sprintf(
2227 __('Grant all privileges on database "%s"'),
2228 htmlspecialchars($dbname)
2230 $default_choice = 3;
2231 echo '<input type="hidden" name="dbname" value="' . htmlspecialchars($dbname) . '" />' . "\n";
2234 // 4th parameter set to true to add line breaks
2235 // 5th parameter set to false to avoid htmlspecialchars() escaping in the label
2236 // since we have some HTML in some labels
2237 PMA_display_html_radio('createdb', $choices, $default_choice, true, false);
2239 unset($default_choice);
2241 echo '</fieldset>' . "\n";
2242 PMA_displayPrivTable('*', '*', false);
2243 echo ' <fieldset id="fieldset_add_user_footer" class="tblFooters">' . "\n"
2244 . ' <input type="submit" name="adduser_submit" value="' . __('Go') . '" />' . "\n"
2245 . ' </fieldset>' . "\n"
2248 // check the privileges for a particular database.
2249 $user_form = '<form id="usersForm" action="server_privileges.php"><fieldset>' . "\n"
2251 . PMA_getIcon('b_usrcheck.png')
2252 . ' ' . sprintf(__('Users having access to "%s"'), '<a href="' . $GLOBALS['cfg']['DefaultTabDatabase'] . '?' . PMA_generate_common_url($checkprivs) . '">' . htmlspecialchars($checkprivs) . '</a>') . "\n"
2253 . '</legend>' . "\n"
2254 . '<table id="dbspecificuserrights" class="data">' . "\n"
2256 . ' <tr><th>' . __('User') . '</th>' . "\n"
2257 . ' <th>' . __('Host') . '</th>' . "\n"
2258 . ' <th>' . __('Type') . '</th>' . "\n"
2259 . ' <th>' . __('Privileges') . '</th>' . "\n"
2260 . ' <th>' . __('Grant') . '</th>' . "\n"
2261 . ' <th>' . __('Action') . '</th>' . "\n"
2266 unset($row, $row1, $row2);
2268 // now, we build the table...
2281 . '`References_priv`, '
2282 . '`Create_tmp_table_priv`, '
2283 . '`Lock_tables_priv`, '
2284 . '`Create_view_priv`, '
2285 . '`Show_view_priv`, '
2286 . '`Create_routine_priv`, '
2287 . '`Alter_routine_priv`, '
2290 $list_of_compared_privileges
2291 = '`Select_priv` = \'N\''
2292 . ' AND `Insert_priv` = \'N\''
2293 . ' AND `Update_priv` = \'N\''
2294 . ' AND `Delete_priv` = \'N\''
2295 . ' AND `Create_priv` = \'N\''
2296 . ' AND `Drop_priv` = \'N\''
2297 . ' AND `Grant_priv` = \'N\''
2298 . ' AND `References_priv` = \'N\''
2299 . ' AND `Create_tmp_table_priv` = \'N\''
2300 . ' AND `Lock_tables_priv` = \'N\''
2301 . ' AND `Create_view_priv` = \'N\''
2302 . ' AND `Show_view_priv` = \'N\''
2303 . ' AND `Create_routine_priv` = \'N\''
2304 . ' AND `Alter_routine_priv` = \'N\''
2305 . ' AND `Execute_priv` = \'N\'';
2307 if (PMA_MYSQL_INT_VERSION
>= 50106) {
2308 $list_of_privileges .=
2311 $list_of_compared_privileges .=
2312 ' AND `Event_priv` = \'N\''
2313 . ' AND `Trigger_priv` = \'N\'';
2316 $sql_query = '(SELECT ' . $list_of_privileges . ', `Db`'
2317 .' FROM `mysql`.`db`'
2318 .' WHERE \'' . PMA_sqlAddSlashes($checkprivs) . "'"
2320 .' AND NOT (' . $list_of_compared_privileges. ')) '
2322 .'(SELECT ' . $list_of_privileges . ', \'*\' AS `Db`'
2323 .' FROM `mysql`.`user` '
2324 .' WHERE NOT (' . $list_of_compared_privileges . ')) '
2325 .' ORDER BY `User` ASC,'
2328 $res = PMA_DBI_query($sql_query);
2329 $row = PMA_DBI_fetch_assoc($res);
2336 // prepare the current user
2337 $current_privileges = array();
2338 $current_user = $row['User'];
2339 $current_host = $row['Host'];
2340 while ($row && $current_user == $row['User'] && $current_host == $row['Host']) {
2341 $current_privileges[] = $row;
2342 $row = PMA_DBI_fetch_assoc($res);
2344 $user_form .= ' <tr class="noclick ' . ($odd_row ?
'odd' : 'even') . '">' . "\n"
2346 if (count($current_privileges) > 1) {
2347 $user_form .= ' rowspan="' . count($current_privileges) . '"';
2349 $user_form .= '>' . (empty($current_user) ?
'<span style="color: #FF0000">' . __('Any') . '</span>' : htmlspecialchars($current_user)) . "\n"
2352 if (count($current_privileges) > 1) {
2353 $user_form .= ' rowspan="' . count($current_privileges) . '"';
2355 $user_form .= '>' . htmlspecialchars($current_host) . '</td>' . "\n";
2356 for ($i = 0; $i < count($current_privileges); $i++
) {
2357 $current = $current_privileges[$i];
2358 $user_form .= ' <td>' . "\n"
2360 if (! isset($current['Db']) ||
$current['Db'] == '*') {
2361 $user_form .= __('global');
2362 } elseif ($current['Db'] == PMA_escape_mysql_wildcards($checkprivs)) {
2363 $user_form .= __('database-specific');
2365 $user_form .= __('wildcard'). ': <tt>' . htmlspecialchars($current['Db']) . '</tt>';
2371 . ' ' . join(',' . "\n" . ' ', PMA_extractPrivInfo($current, true)) . "\n"
2375 . ' ' . ($current['Grant_priv'] == 'Y' ?
__('Yes') : __('No')) . "\n"
2378 $user_form .= sprintf(
2380 urlencode($current_user),
2381 urlencode($current_host),
2382 urlencode(! isset($current['Db']) ||
$current['Db'] == '*' ?
'' : $current['Db']),
2385 $user_form .= '</td>' . "\n"
2387 if (($i +
1) < count($current_privileges)) {
2388 $user_form .= '<tr class="noclick ' . ($odd_row ?
'odd' : 'even') . '">' . "\n";
2391 if (empty($row) && empty($row1) && empty($row2)) {
2394 $odd_row = ! $odd_row;
2397 $user_form .= ' <tr class="odd">' . "\n"
2398 . ' <td colspan="6">' . "\n"
2399 . ' ' . __('No user found.') . "\n"
2403 $user_form .= '</tbody>' . "\n"
2404 . '</table></fieldset></form>' . "\n";
2406 if ($GLOBALS['is_ajax_request'] == true) {
2407 $extra_data['user_form'] = $user_form;
2408 $message = PMA_Message
::success(__('User has been added.'));
2409 PMA_ajaxResponse($message, $message->isSuccess(), $extra_data);
2411 // Offer to create a new user for the current database
2412 $user_form .= '<fieldset id="fieldset_add_user">' . "\n"
2413 . '<legend>' . __('New') . '</legend>' . "\n"
2414 . ' <a href="server_privileges.php?' . $GLOBALS['url_query'] . '&adduser=1&dbname=' . htmlspecialchars($checkprivs) .'" rel="'.'checkprivs='.htmlspecialchars($checkprivs). '&'.$GLOBALS['url_query'] . '" class="'.$conditional_class.'" name="db_specific">' . "\n"
2415 . PMA_getIcon('b_usradd.png')
2416 . ' ' . __('Add user') . '</a>' . "\n"
2417 . '</fieldset>' . "\n";
2421 } // end if (empty($_REQUEST['adduser']) && empty($checkprivs)) ... elseif ... else ...
2425 * Displays the footer
2428 require './libraries/footer.inc.php';