Viewing File: /usr/local/cpanel/base/3rdparty/phpMyAdmin/libraries/cpanel_interface.lib.php

<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
 * Custom cPanel Functions
 *  Provides the necessary functions to perform
 *  privileged MySQL actions as user.
 *
 * @version $Id$
 * @package phpMyAdmin
 */

// This is a library of functions provided by cPanel, Inc and is distributed under the same terms as phpMyAdmin itself (GPLv2).
if ( !defined( 'PHPMYADMIN' ) ) {
    exit;
}

use PhpMyAdmin\Util;

/**
 * Pass MySQL action to cpmysqlwrap
 *
 * Core function responsible for send desired MySQL
 * action to the privilege-aware cPanel binary
 * cpmysqlwrap.  cpmysqlwrap takes a limited set
 * of corelative API calls with arguments
 *
 * @access private
 * @param string $action The corelative API call to envoke
 * @param array $args Arguements for necessary for $action
 * @return array Array with 'raw_output' and 'exit_code' generated by cpmysqlwrap
 */
function _PMA_CPANEL_API_action( $action, $args = array() ) {
    //bail on blank/null action
    if ( empty( $action ) ) {
        $err_msg = "No cPanel action to perform against MySQL.";
        PMA_CPANEL_die( $err_msg );
    }

    // define our privilege binary
    $cpmysqladmin_bin = '/usr/local/cpanel/bin';
    if ( file_exists( $cpmysqladmin_bin . '/cpmysqlwrap' ) ) {
        $cpmysqladmin_bin .= '/cpmysqlwrap';
    }
    else {
        $cpmysqladmin_bin .= '/mysqlwrap';
    }

    $input = $action;
    if ( !empty( $args ) ) {
        foreach ( $args as $arg ) {
            if ( $arg !== NULL && $arg !== '' ) {
                $input .= " $arg";
            }
        }
    }
    //calling api action should have cleaned the input, as necessary...
    // but just in case we'll escape the input string before exec'ing it
    $input = escapeshellcmd( $input );
    $raw_output = array();
    $exit_code;
    exec( "$cpmysqladmin_bin $input", $raw_output, $exit_code );
    return array(
        'raw_output' => $raw_output,
        'exit_code'  => $exit_code,
    );
}// end _PMA_CPANEL_action

/**
 * Invoke cPanel API function 'adddb'
 *
 * Wrapper function for envoking the MySQL API call which
 * creates a database for the user.
 * @NOTE: Due to the way cPanel enforces prefixing,
 *  the requested db name may be prepend with "$user_"
 *  and if so, the function will alter the db name.
 *
 * @access public
 * @param string &$db_name Name of database to create.
 * @return array Array with 'sql_query' equivalent sql plus 'raw_output' and 'exit_code' generated by cpmysqlwrap
 */
function PMA_CPANEL_API_adddb( &$db_name ) {
    _PMA_CPANEL_validateDbName( $db_name );
    $results = array();
    if ( ! _PMA_CPANEL_isPriveleged() ) {
        $results = _PMA_CPANEL_API_action( 'ADDDB', array($db_name) );

        //check for errors
        if ( $results['exit_code'] !== 0 || !empty( $results['raw_output'] ) ) {
            /**
             * adddb was in error
             *  mysqlwrap adddb should provide output on failure
             *  status code of 1 is a catastrophic failure
             *  status code of 0 may be success or failure,
             *   this is a happenstance of the adminbin wrapper
             */
            $err_msg = ( !empty( $results['raw_output'] ) ) ? implode( "\n", $results['raw_output'] ) : "Could not perform MySQL action.";
            PMA_CPANEL_die( $err_msg );
        }

        //double check that the db was created
        $dbs = PMA_CPANEL_API_listdbs();
        $db_hunt = '';

        //first, get the true cpanel db name, if necessary
        if ( PMA_CPANEL_API_useprefix() ) {
            $cpprefix = PMA_CPANEL_getPrefix();
            if ( strpos( $db_name, $cpprefix ) !== 0 ) {
                $db_hunt = $cpprefix;
            }
        }
        $db_hunt .= $db_name;

        if ( empty( $dbs ) || !is_array( $dbs ) || !in_array( $db_hunt, $dbs ) ) {
            $err_msg = "Database $db_hunt not created.";
            PMA_CPANEL_die( $err_msg );
        }
        //set the new db name to what cpanel created
        $db_name = $db_hunt;

        //populate faux query statement for display purposes
        //  the create action is actually performed by mysqladmin
        $results['sql_query'] = "CREATE DATABASE `$db_hunt`;\n";
    }
    else{
        // running via WHM, use PMA code
        $local_query = 'CREATE DATABASE ' . Util::backquote( $db_name ) . ';';
        $results['sql_query'] = $local_query."\n";
        // save the original db name because Tracker.class.php which
        // may be called under PMA_DBI_query
        // for some statements, one of which being CREATE DATABASE
        $original_db = $db_name;
        if ( $GLOBALS['dbi']->query( $local_query ) ) {
            $results['raw_output'] = '';
            $results['exit_code'] = 0;
        }
        else {
            $results['raw_output'] = 'phpMyAdmin could not CREATE database ' . $original_db;
            $results['exit_code'] = 1; //we should be here, but for completion's sake
        }
        $db = $original_db;
        unset( $original_db );

    }
    return $results;
}//end PMA_CPANEL_API_adddb

/**
 * Envoke cPanel API function 'deldb'
 *
 * Wrapper function for envoking the MySQL API call which
 * drops a database for the user.
 *
 * @access public
 * @param string $db_name Name of database to dropped.
 * @return array Array with 'sql_query' equivalent sql plus 'raw_output' and 'exit_code' generated by cpmysqlwrap
 */
function PMA_CPANEL_API_deldb( $db_name ) {
    _PMA_CPANEL_validateDbName( $db_name );
    $results = array();
    if ( ! _PMA_CPANEL_isPriveleged() ) {
        $results = _PMA_CPANEL_API_action( 'DELDB', array( $db_name ) );

        //check for errors
        $expected_msg = 'Database "' . $db_name . '" dropped';

        if ( $results['exit_code'] !== 0
            || ( !empty( $results['raw_output'] ) && $results['raw_output'][0] !== $expected_msg ) ) {
            /**
             * adddb was in error
             *  mysqlwrap adddb should provide output on failure
             *  status code of 1 is a catastrophic failure
             *  status code of 0 may be success or failure,
             *   this is a happenstance of the adminbin wrapper
             *  with DELDB, specifically, we get an info message
             */
            $err_msg = ( !empty( $results['raw_output']) ) ? implode( "\n", $results['raw_output'] ) : "Could not perform MySQL action.";
            PMA_CPANEL_die( $err_msg );
        }

        //double check that the db was dropped
        $dbs = PMA_CPANEL_API_listdbs();

        if ( is_array( $dbs ) && in_array( $db_name, $dbs ) ) {
            $err_msg = "Database $db_name not dropped.";
            PMA_CPANEL_die( $err_msg );
        }

        //populate faux query statement for display purposes
        //  the drop action is actually performed by mysqladmin
        $results['sql_query'] = "DELETE FROM mysql.db WHERE Db='$db_name';\n"
                                        ."DELETE FROM mysql.host WHERE Db='$db_name';\n"
                                        ."DELETE FROM mysql.tables_priv WHERE Db='$db_name';\n"
                                        ."DELETE FROM mysql.columns_priv WHERE Db='$db_name';\n"
                                        ."DROP DATABASE `$db_name`;\n";
    }
    else {
        // running via WHM, use PMA code
        // if someday the RENAME DATABASE reappears, do not DROP
        $local_query = 'DROP DATABASE ' . Util::backquote( $db_name ) . ';';
        $results['sql_query'] = $local_query . "\n";
        if ( $GLOBALS['dbi']->query( $local_query ) ) {
            $results['raw_output'] = '';
            $results['exit_code'] = 0;
        }
        else {
            $results['raw_output'] = 'phpMyAdmin could not DROP database ' . $db_name;
            $results['exit_code'] = 1; //we should be here, but for completion's sake
        }
    }
    return $results;
}//end PMA_CPANEL_API_deldb

/**
 * Envoke cPanel API function 'listdbs'
 *
 * Wrapper function for envoking the MySQL API call which
 * lists all databases for the user.
 *
 * @access public
 * @return array Array of db names
 */
function PMA_CPANEL_API_listdbs() {
    $results = _PMA_CPANEL_API_action( 'LISTDBS' );
    return $results['raw_output'];
}//end PMA_CPANEL_API_listdbs

/**
 * Envoke cPanel API function 'useprefix'
 *
 * Wrapper function for envoking the MySQL API call which
 * returns the state of DB Prefixing on the server.
 *
 * @access public
 * @return boolean TRUE for prefixing "on" (the default), FALSE for off
 * or if effective user is 'cpanelphpmyadmin' because this will cause
 * the underlying USEPREFIX adminbin to fail with an error of not finding
 * info for user 'cpanelphpmyadmin'
 */
function PMA_CPANEL_API_useprefix() {
    $_userinfo = posix_getpwuid( posix_getuid() );
    if ( $_userinfo['name'] == 'cpanelphpmyadmin' ) {
      return false;
    }
    $results = _PMA_CPANEL_API_action( 'USEPREFIX' );
    $state = ( count( $results['raw_output'] ) ) ? $results['raw_output'][0] : 1;
    return (bool) $state;
}//end PMA_CPANEL_API_useprefix

/**
 * Wrapper function for using PMA to set collation of a database
 *
 * Wrapper function that will alter a database's collation. Internally
 * it uses PMA_DBI_query.  This is a utility function that can be called
 * arbitrarily and provides easy access to the sql statement for UI rendering.
 *
 * @access public
 * @param string $db Database to change.
 * @param string $db_collation The collation to set (value based on user select in PMA)
 * @return array Array with 'sql_query' equivalent sql plus 'pma_output' generated by PMA_DBI_query()
 */
function PMA_CPANEL_SQL_setDbCollation( $db, $db_collation ) {
    _PMA_CPANEL_validateDbName( $db );
    $results['sql_query'] = 'ALTER DATABASE ' . Util::backquote( $db ) . ' DEFAULT' . Util::getCharsetQueryPart( $db_collation );
    $results['pma_result'] = $GLOBALS['dbi']->query( $results['sql_query'] );
    $results['sql_query'] .= ";\n";
    return $results;
}//end PMA_CPANEL_SQL_setDbCollation

/**
 * Utility function to determine proper user prefix
 *
 * Returns the proper prefix for the effective user.
 * @NOTE: All cPanel users will have a "prefix" though
 *  its use is determined based on the state of DB Prefixing.
 *  if the PMA is being run via WHM, there is no prefix since
 *  the effective user is a system user, cpanelphpmyadmin
 *
 * @access public
 * @return string Effective user prefix
 */
function PMA_CPANEL_getPrefix() {
    $username = (posix_getpwuid ( posix_getuid() ))['name'];

    if ($username == 'cpanelphpmyadmin') {
        return '';
    }

    $results = _PMA_CPANEL_API_action('GETPREFIXLENGTH');
    $prefix_length = $results['raw_output'][0];

    if (strlen($username) > $prefix_length) {
        $username = substr($username, 0, $prefix_length);
    }

    return $username . '_';
}//end PMA_CPANEL_getPrefix

/**
 * Utility function to determine whether truncation should be applied.
 *
 * Returns a boolean that indicates whether truncation to 8 characters should be
 * performed.
 *
 * @access public
 * @return boolean TRUE if truncation should be performed, FALSE otherwise.
 */
function PMA_CPANEL_shouldTruncate() {
    $results = _PMA_CPANEL_API_action( 'VERSION' );
    return ($results['raw_output'][0] + 0) < 10.0;
}

/**
 * Displays a cPanel related error message in the right frame.
 *
 * Override the 'strMySQLSaid' global with value 'cPanel said:' and
 * internally can Util::mysqlDie which will render the page immediately
 *
 * @access  public
 * @param string Error message to print
 * @return void
 */
function PMA_CPANEL_die( $error_message = '' ) {
    $GLOBALS['strMySQLSaid'] = 'cPanel said: ';
    Util::mysqlDie( $error_message );
} //end PMA_CPANEL_die

/**
 * Check if PMA is being executed by a WHM/cPanel "super" user
 *
 * @access private
 * @return bool
 */
function _PMA_CPANEL_isPriveleged() {
    global $cfg;

    $pma_user = $cfg['Server']['user'];
    $eff_user = posix_getpwuid( posix_getuid() );

    if ( $pma_user == 'root' && $eff_user['name'] == 'cpanelphpmyadmin' ) {
        return TRUE;
    }

    return FALSE;
}//end of _PMA_CPANEL_isPriveleged

/**
 * Append prefix to DB name if ( PMA_CPANEL_API_useprefix() ),
 * making sure not to add it if it exists.
*/
function _PMA_CPANEL_addPrefixIfNeeded( $db_name ) {
    $db_hunt = '';
    //first, get the true cpanel db name, if necessary
    if ( PMA_CPANEL_API_useprefix() ) {
        $cpprefix = PMA_CPANEL_getPrefix();
        if ( strpos( $db_name, $cpprefix ) !== 0 ) {
            $db_hunt = $cpprefix;
        }
   }
   $db_hunt .= $db_name;
   return $db_hunt;
}

/**
 * Check if user supplied database name is valid
 *
 * In cPanel, we allow alphanumerics (a-z,A-Z,0-9), the underscore (_) and the
 * hyphen (-).  All cPanel action function in this interface should call this
 * function before attempting to perform a database operation.  The function
 * will bail the envoking action by using PMA_CPANEL_die so that the proper
 * error message is register for rendering in the PMA UI.
 *
 * @access private
 * @param string $db_name The database name (from user input) to check
 * @return mixed TRUE if database name is with [a-zA-Z0-9_-], safe exit if not
 */

function _PMA_CPANEL_validateDbName( $db_name ) {
    $input = str_replace( array('-','_'), '', $db_name );
    if ( ctype_alnum( $input ) ) {
        return true;
    }
    $err_msg =
    "\"$db_name\" is not a valid database name.\n"
    ."Only alphanumeric, '-' and '_' characters are allowed.\n";
    PMA_CPANEL_die( $err_msg );
}//end of _PMA_CPANEL_validateDbName

?>
Back to Directory File Manager