sf-static/sql/lib/util.php

606 lines
21 KiB
PHP
Raw Normal View History

2023-02-28 18:21:07 +00:00
<?php
/**
* This file is a part of MyWebSQL package
* Utility functions for server side database related functionality
*
* @file: lib/util.php
* @author Samnan ur Rehman
* @copyright (c) 2008-2014 Samnan ur Rehman
* @web http://mywebsql.net
* @license http://mywebsql.net/license
*/
include(BASE_PATH . '/lib/functions.php');
define('LIMIT_REGEXP', '/(.*)[\s]+(limit[\s]+[\d]+([\s]*(,|offset)[\s]*[\d]+)?)$/is');
define('SORT_REGEXP', '/(.*)[\s]+(ORDER[\s]+BY[\s]+([a-zA-z0-9\._]+|`.*`|\'.*\'|".*")\s*(ASC|DESC)?(\s*\,\s*([a-zA-z0-9\._]+|`.*`|\'.*\'|".*")\s*(ASC|DESC)?)*)$/is');
function showDBError() {
return __('Database connection failed to the server') . '. ' . __('Host') . ': ' . DB_HOST . ', ' . __('User') . ': ' . DB_USER;
}
function getDbName() {
return Session::get('db', 'name');
}
function getDbList(&$db) {
include (BASE_PATH . "/config/database.php");
$server = Session::get('auth', 'server_name', true);
// return restrictive db list based on config
if (isset($DB_LIST) && isset($DB_LIST[$server]))
return $DB_LIST[$server];
// return default db list user has access to
return $db->getDatabases();
}
function printDbList(&$db) {
$dblist = getDbList($db);
// if there is one database for the given userid, let's select it to cut out one extra step
if (! Session::get('db', 'name') ) {
$stDb = 0; $selDb = "";
$stDbList = $db->getStandardDbList();
foreach($dblist as $dbname) {
if ( in_array($dbname, $stDbList) )
$stDb++;
else
$selDb = $dbname;
}
if (count($dblist) == ($stDb+1)) {
Session::set('db', 'name', $selDb);
$db->selectDb(Session::get('db', 'name'));
}
}
// --- end of automatic selection logic
if (getDbName()) {
$folder = $db->name();
echo view(array($folder.'/dblist', 'dblist'), array(), $dblist);
}
else {
print '<span>'.__('Select a database to begin').'.</span>';
}
return $dblist;
}
function getDBClass() {
// use the common driver to connect to database
if ( ! Session::get('auth', 'valid') )
return array(BASE_PATH . '/lib/db/manager.php', 'DbManager');
$driver = Session::get('db', 'driver');
if ( !$driver || empty($driver) )
return array(BASE_PATH . '/lib/db/manager.php', 'DbManager');
$lib = BASE_PATH . '/lib/db/'.$driver.'.php';
$class = 'DB_' . ucfirst( str_replace('/', '_', $driver) );
return array($lib, $class);
}
function execute_request(&$db) {
// do not append or prepend anything to output if we have to download
$output = v($_REQUEST["type"]) == 'download' ? false: true;
if ( $output ) {
startForm($db);
}
if ( isset($_REQUEST["type"]) ) {
$module_requested = $_REQUEST["type"];
$_REQUEST["query"] = trim(v($_REQUEST["query"], ""), " \t\r\n;");
$module = BASE_PATH . "/modules/".$module_requested.".php";
if (ctype_alpha( $module_requested ) && file_exists($module)) {
require( BASE_PATH . '/config/modules.php' );
// check for module access type and allow/disallow as needed
if (MODULE_ACCESS_MODE == 'deny' && in_array($module_requested, $DENY_MODULES)) {
createErrorPage();
}
else if (MODULE_ACCESS_MODE == 'allow' && !in_array($module_requested, $ALLOW_MODULES)) {
createErrorPage();
} else {
include($module);
function_exists('processRequest') ? processRequest($db) : createErrorGrid($db, "");
}
}
else
createErrorPage(); // unidentified type requested
}
if ( $output ) {
print "</form>\n";
print "</body></html>";
}
}
function createResultGrid(&$db)
{
Session::del('select', 'pkey');
Session::del('select', 'ukey');
Session::del('select', 'mkey');
Session::del('select', 'unique_table'); // this is different from the one used for viewing table data
$record_limit = Options::get('res-max-count', MAX_RECORD_TO_DISPLAY);
// <form element is moved to js, see comments there
print "<div id='results'>";
print "<table cellspacing=\"0\" width='100%' border=\"0\" class='results' id=\"dataTable\"><thead>\n";
$f = $db->getFieldInfo();
// see if all fields come from one table so we can allow editing
if (Session::get('select', 'can_limit') && count($f) > 0) {
Session::set('select', 'unique_table', $f[0]->table);
for($i=1; $i<count($f);$i++) {
if ($f[$i]->table != Session::get('select', 'unique_table')) { // bail out, more than one table data
Session::del('select', 'unique_table');
break;
}
}
}
// desc command returns table name as COLUMNS, so we make it empty
//if (Session::get('select', 'unique_table') == "COLUMNS")
// Session::del('select', 'unique_table');
$ed = Session::get('select', 'can_limit') && (Session::get('select', 'unique_table') == "" ? false : true);// && ($db->numRows() > 0);
// ------------ print header -----------
print "<tr id=\"fhead\">";
print "<th class=\"th tch\"><div>#</div></th>";
if ($ed)
print "<th class=\"th_nosort tch\"><div><input class=\"check-all\" type=\"checkbox\"
onclick=\"resultSelectAll()\" title=\"".__('Select/unselect All records')."\" /></div></th>";
$v = "";
// more than one field can be part of a primary key (composite key)
Session::set('select', 'pkey', array());
Session::set('select', 'ukey', array());
Session::set('select', 'mkey', array());
$fieldNames = '';
$fieldInfo = json_encode($f);
$i = 1;
foreach($f as $fn) {
$cls = $fn->type == 'numeric' ? "th_numeric" : "th";
print "<th nowrap=\"nowrap\" class='$cls'><div>";
if ($fn->pkey == 1) {
Session::add('select', 'pkey', $fn->name);
print "<span class='pk' title='".__('Primary key column')."'>&nbsp;</span>";
}
else if ($fn->ukey == 1) {
Session::add('select', 'ukey', $fn->name);
print "<span class='uk' title='".__('Unique key column')."'>&nbsp;</span>";
}
else if ($fn->mkey == 1 && !$fn->blob) // blob/text fields are FULL TEXT KEYS only
Session::add('select', 'mkey', $fn->name);
print $fn->name;
if (Session::get('select', 'sortcol') == $i) {
print ( Session::get('select', 'sort') == 'DESC' ? '&nbsp;&#x25BE;' : '&nbsp;&#x25B4' );
}
$i++;
print "</div></th>";
$fieldNames .= "'" . str_replace("'", "\\'", $fn->name) . "',";
}
print "</tr></thead><tbody>\n";
// ------------ print data -----------
$j = 0;
while($r = $db->fetchRow(0, 'num')) {
$i = 0;
print "<tr class=\"row\">";
print "<td class=\"tj\">".($j+1)."</td>";
if ($ed)
print "<td class=\"tch\"><input type=\"checkbox\" /></td>";
foreach($r as $rs) {
$class = ($rs === NULL) ? "tnl" : ($f[$i]->numeric == 1 ? "tr" : "tl");
if ($ed) $class .= ' edit';
//if ($f[$i]->blob)
// $class .= $f[$i]->type == 'binary' ? ' blob' : ' text';
if (!$f[$i]->blob)
$data = ($rs === NULL) ? "NULL" : (($rs === "") ? "" : htmlspecialchars($rs));
else
$data = getBlobDisplay($rs, $f[$i], $j, $ed);
print "<td nowrap=\"nowrap\" class=\"$class\">$data</td>";
$i++;
}
print "</tr>\n";
$j++;
if (Session::get('select', 'can_limit') && $record_limit > 0 && $j >= $record_limit)
break;
}
$numRows = $j;
print "</tbody></table>";
print "</div>";
$editTableName = Session::get('select', 'unique_table');
$gridTitle = $editTableName == '' ? __('Query Results') :
str_replace('{{TABLE}}', htmlspecialchars($editTableName), __('Data for {{TABLE}}'));
print '<div id="title">'. $gridTitle . '</div>';
$message = '';
if (Session::get('select', 'can_limit')) { // can limit be applied to this type of query (e.g. show,explain cannot be limited/sorted)
if (Session::get('select', 'limit')) { // yes, and limit is applied to records by the application
$total_records = Session::get('select', 'count');
$total_pages = ceil($total_records / $record_limit);
$current_page = Session::get('select', 'page');
$from = (($current_page - 1) * $record_limit) + 1;
$to = $from + $db->numRows() - 1;
$message = "<div class='numrec'>".str_replace(array('{{START}}', '{{END}}'), array($from, $to), __('Showing records {{START}} - {{END}}'))."</div>";
}
else {
$total_records = $db->numRows();
$total_pages = 1;
$current_page = 1;
if ($record_limit > 0 && $total_records > $record_limit)
$message = "<div class='numrec'>".str_replace('{{MAX}}', $record_limit, __('Showing first {{MAX}} records only'))."!</div>";
}
}
else {
$total_records = $db->numRows();
$total_pages = 1;
$current_page = 1;
$message = "";
}
$js = "<script type=\"text/javascript\" language=\"javascript\">\n";
if (count(Session::get('select', 'pkey')) > 0)
$js .= "parent.editKey = ".json_encode(Session::get('select', 'pkey')).";\n";
else if (count(Session::get('select', 'ukey')) > 0)
$js .= "parent.editKey = ".json_encode(Session::get('select', 'ukey')).";\n";
else
$js .= "parent.editKey = [];\n";
$js .= "parent.editTableName = \"" . htmlspecialchars($editTableName) ."\";\n";
$js .= "parent.fieldInfo = ".$fieldInfo.";\n";
$js .= "parent.queryID = '".md5(Session::get('select', 'query'))."';\n";
$tm = $db->getQueryTime();
$js .= "parent.totalRecords = $total_records;\n";
$js .= "parent.totalPages = $total_pages;\n";
$js .= "parent.currentPage = $current_page;\n";
$js .= "parent.queryType = \"".(Session::get('select', 'table') ? 'table' : 'query')."\";\n";
$js .= "parent.transferResultGrid(".$numRows.", '$tm', \"$message\");\n";
$js .= "parent.addCmdHistory(\"".preg_replace("/[\n\r]+/", "<br/>", htmlspecialchars(Session::get('select', 'query')))."\", 1);\n";
$js .= "parent.resetFrame();\n";
$js .= "</script>\n";
print $js;
}
function createSimpleGrid(&$db, $message) {
print "<div id='results'>";
print "<div class='message ui-state-default'>$message<span style='float:right'>".__('Quick Search')."&nbsp;<input type=\"text\" id=\"quick-info-search\" maxlength=\"50\" /></div>";
print "<table cellspacing=\"0\" width='100%' border=\"0\" class='results' id=\"infoTable\"><thead>\n";
$f = $db->getFieldInfo();
$ed = false;
// ------------ print header -----------
print "<tr id=\"fhead\">";
print "<th class=\"th index\"><div>#</div></th>";
$v = "";
foreach($f as $fn) {
$cls = $fn->type == 'numeric' ? "th_numeric" : "th";
$dsrt = $fn->type == 'numeric' ? "numeric" : "text";
print "<th nowrap=\"nowrap\" class='$cls' data-sort=\"$dsrt\"><div>";
print $fn->name."</div></th>";
}
print "</tr></thead><tbody>\n";
// ------------ print data -----------
$j = 0;
while($r = $db->fetchRow(0, 'num')) {
$i = 0;
print "<tr id=\"rc$j\" class=\"row\">";
print "<td class=\"tj\">".($j+1)."</td>";
foreach($r as $rs) {
$class = ($rs === NULL) ? "tnl" : ($f[$i]->numeric == 1 ? "tr" : "tl");
if ($f[$i]->blob)
$class .= $f[$i]->type == 'binary' ? ' blob' : ' text';
$data = ($rs === NULL) ? "NULL" : (($rs === "") ? "&nbsp;" : htmlspecialchars($rs));
print "<td nowrap=\"nowrap\" id=\"r$j"."f$i\" class=\"$class\">$data</td>";
$i++;
}
print "</tr>\n";
$j++;
}
$numRows = $j;
print "</tbody></table>";
print "</div>";
$js = "<script type=\"text/javascript\" language=\"javascript\">\n";
$tm = $db->getQueryTime();
$js .= "parent.transferInfoMessage();\n";
$js .= "parent.resetFrame();\n";
$js .= "</script>\n";
print $js;
}
function createErrorPage() {
echo view('error_page');
}
// numQueries = number of 'successful' executed queries
// affectedRows = some rows maybe affected in batch processing, even if error occured
function createErrorGrid(&$db, $query='', $numQueries=0, $affectedRows=-1) {
if ($query == '')
$query = Session::get('select', 'query');
Session::del('select', 'result');
Session::del('select', 'pkey');
Session::del('select', 'ukey');
Session::del('select', 'mkey');
Session::del('select', 'unique_table');
Session::set('select', 'result', array()); // result blob data
$e = $db->getError();
print "<div id='results'>\n";
if ($numQueries > 0) {
print "<div class=\"message ui-state-default\">";
$msg = ($numQueries == 1) ? __('1 query successfully executed') : str_replace('{{NUM}}', $numQueries, __('{{NUM}} queries successfully executed'));
$msg .= ".<br/><br/>".str_replace('{{NUM}}', $affectedRows, __('{{NUM}} record(s) were affected')).".<br/><br/>";
print $msg . '</div>';
}
//else
// print "No query was successful";
$formatted_query = preg_replace("/[\\n|\\r]?[\\n]+/", "<br>", htmlspecialchars($query));
print "<div class=\"message ui-state-error\">".__('Error occurred while executing the query').":</div><div class=\"message ui-state-highlight\">".htmlspecialchars($e)."</div><div class=\"sql-text ui-state-error\">".$formatted_query."</div>";
print "</div>";
print "<script type=\"text/javascript\" language='javascript'> parent.transferResultMessage(-1, '&nbsp;', '".__('Error occurred while executing the query')."');\n";
print "parent.addCmdHistory(\"".preg_replace("/[\n\r]/", "<br/>", htmlspecialchars(Session::get('select', 'query')))."\");\n";
print "parent.resetFrame();\n";
print "</script>\n";
}
// batch process will send default params as required
function createInfoGrid(&$db, $query="", $numQueries=1, $affectedRows=-1, $addHistory=true, $executionTime=false) {
Session::del('select', 'pkey');
Session::del('select', 'ukey');
Session::del('select', 'mkey');
Session::del('select', 'unique_table');
if ($affectedRows == -1)
$affectedRows = $db->getAffectedRows();
if ($query == "")
$query = $_REQUEST["query"];
print "<div id='results'>\n";
print "<div class=\"message ui-state-default\">";
$msg = ($numQueries == 1) ? __('1 query successfully executed') : str_replace('{{NUM}}', $numQueries, __('{{NUM}} queries successfully executed'));
print $msg . ".</div>";
print "<div class=\"message ui-state-highlight\">".str_replace('{{NUM}}', $affectedRows, __('{{NUM}} record(s) were affected'))."</div>";
if ($numQueries == 1) {
$formatted_query = preg_replace("/[\\n|\\r]?[\\n]+/", "<br>", htmlspecialchars($query));
print "<div class='sql-text ui-state-default'>".$formatted_query."</div>";
$warnings = $db->getWarnings();
if (count($warnings) > 0) {
print '<div class="message ui-state-error">';
foreach($warnings as $warning)
print htmlspecialchars($warning) . '<br />';
print '</div>';
}
}
print "</div>";
$tm = $executionTime ? $executionTime : $db->getQueryTime();
print "<script type=\"text/javascript\" language='javascript'> parent.transferResultMessage(-1, '$tm', '".str_replace('{{NUM}}', $affectedRows, __('{{NUM}} record(s) updated'))."');\n";
if ($addHistory)
print "parent.addCmdHistory(\"".preg_replace("/[\n\r]/", "<br/>", htmlspecialchars($query))."\");\n";
if (Session::get('db', 'altered')) {
Session::del('db', 'altered');
print "parent.objectsRefresh();\n";
}
print "parent.resetFrame();\n";
print "</script>\n";
}
function getQueryType($query) {
$type = array('result'=>FALSE,'can_limit'=>FALSE,'has_limit'=>FALSE,'update'=>FALSE);
$q = trim($query, " \n\t");
$q = strtolower(substr($query, 0, 7)); // work on only first few required characters of query
if( $q == "explain" || substr($q, 0, 6) == "select" || substr($q, 0, 4) == "desc"
|| substr($q, 0, 4) == "show" || substr($q, 0, 4) == "help" ) {
$type['result'] = TRUE;
if (substr($q, 0, 6) == "select") {
$type['can_limit'] = TRUE; // we don't want to limit results for other queries like 'show...'
preg_match(LIMIT_REGEXP, $query, $matches);
if (count($matches) == 3) {
$type['has_limit'] = true;
}
}
}
else
$type['update'] = TRUE;
return $type;
}
function sortQuery($query, $field) {
$query = trim($query);
$sort = '';
$sort_type = Session::get('select', 'sort');
if (!$sort_type)
$sort_type = 'ASC';
$limit = '';
// find and extract limit clause out of query (must be the last clause, otherwise sorting will not work)
preg_match(LIMIT_REGEXP, $query, $matches);
if (count($matches) == 3) {
$query = trim($matches[1]);
$limit = trim($matches[2]);
}
preg_match(SORT_REGEXP, $query, $matches);
if (count($matches) > 3) {
$query = trim($matches[1]);
// if sorting with same field, change sorting order
if ( trim($matches[3]) == $field )
$sort_type = $sort_type == 'ASC' ? 'DESC' : 'ASC';
else // reset to ascending order
$sort_type = 'ASC';
}
Session::set('select', 'sortcol', $field);
Session::set('select', 'sort', $sort_type);
$sort = 'ORDER BY ' . $field . ' ' . $sort_type;
$query .= ' ' . $sort . ' ' . $limit;
return $query;
}
function getCommandInfo($sql) {
$info = array('db'=>'', 'dbChanged'=>FALSE, 'dbAltered'=>FALSE, 'setvar'=>FALSE);
if (preg_match('@^[\s]*USE[[:space:]]*([\S]+)@i', $sql, $match)) {
$info['db'] = trim($match[1], ' ;');
$info['dbChanged'] = TRUE;
} else if (preg_match('/^(CREATE|ALTER|DROP)\s+/i', $sql)) {
$info['dbAltered'] = true;
} else if (preg_match('/^SET[\s]+@([a-zA-z0-9_]+|`.*`|\'.*\'|".*")[\s]?=[\s]?(.*)/i', $sql, $matches)) {
//SET[\s]+@([a-zA-z0-9_]+)[\s]+=[\s]+(.*)
$info['setvar'] = true;
$info['variable'] = trim($matches[1]);
$info['value'] = trim($matches[2]);
}
/*preg_match('@^[\s]*(DROP|CREATE)[\s]+(IF EXISTS[[:space:]]+)?(TABLE|DATABASE)[[:space:]]+(.+)@im', $sql)*/
return $info;
}
function startForm($db, $style="margin:0px;overflow:hidden;width:100%;height:100%") {
print "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
print "<html xmlns=\"http://www.w3.org/1999/xhtml\" style=\"overflow:hidden;width:100%;height:100%\">\n";
print "<head><title>MyWebSQL</title>\n";
print "</head><body class=\"dialogbody\" style=\"$style\">\n";
print '<div id="popup_overlay" class="ui-widget-overlay">';
print '<div><span><img src="themes/'.THEME_PATH.'/images/loading.gif" alt="" /></span></div>';
print '</div>';
print "<script language='javascript' type='text/javascript' src='cache.php?script=".$db->name().",common'></script>\n";
print "<!--[if lt IE 8]>
<script type=\"text/javascript\" language=\"javascript\" src=\"cache.php?script=json2\"></script>
<![endif]-->";
print "<script language='javascript' type='text/javascript'>
var EXTERNAL_PATH = '".EXTERNAL_PATH."';
var THEME_PATH = '".THEME_PATH."';
var DB = db_" . $db->name() .";
</script>\n";
print "<form name='frmquery' id='frmquery' method='post' action='#' enctype='multipart/form-data' onsubmit='return false'>";
print "<input type='hidden' name='type' value='query' />";
print "<input type='hidden' name='id' value='' />";
print "<input type='hidden' name='name' value='' />";
print "<input type='hidden' name='query' value='' />";
}
function sanitizeCreateCommand($type, $cmd) {
$str = preg_replace("/[\\n|\\r]?[\\n]+/", "<br>", htmlspecialchars($cmd));
return $str;
/*if ($type == "table")
return $str;
else if ($type == "view")
{
$str = str_replace(" DEFINER=", "<br>DEFINER=", $str);
$str = str_replace(" SQL SECURITY ", "<br>SQL SECURITY ", $str);
$str = str_replace(" AS (", "<br> AS<br>(", $str);
}
else if ($type == "procedure")
{
$str = str_replace(" DEFINER=", "<br>DEFINER=", $str);
$str = str_replace(" PROCEDURE ", "<br>PROCEDURE ", $str);
$str = str_replace("BEGIN", "<br>BEGIN<br>", $str);
$str = str_replace(" END", "<br>END", $str);
}
else if ($type == "function")
{
$str = str_replace(" DEFINER=", "<br>DEFINER=", $str);
$str = str_replace(" FUNCTION ", "<br>FUNCTION ", $str);
$str = str_replace("BEGIN", "<br>BEGIN<br> ", $str);
$str = str_replace(" END", "<br>END", $str);
}
else if ($type == "trigger")
{
$str = str_replace("\n", "<br>", $str);
}
return $str;*/
}
function getBlobDisplay($rs, $info, $numRecord, $editable) {
//$pattern = '/[\x00-\x08\x0E-\x1F\x7F]/'; for binary matching
$binary = ($info->type == 'binary') ? true : false;
$length = strlen($rs);
$size = format_bytes($length);
$span = '<span class="i">';
if ($rs === NULL)
$span .= "NULL";
else if ($rs === "")
$span .= "&nbsp;";
else {
if (MAX_TEXT_LENGTH_DISPLAY >= $length)
$span .= htmlspecialchars($rs);
else if ($binary)
$span .= str_replace( '{{SIZE}}', $size, __('Blob Data [{{SIZE}}]') );
else
$span .= str_replace( '{{SIZE}}', $size, __('Text Data [{{SIZE}}]') );
}
$extra = "";
$btype = "text";
if ($binary) {
include(BASE_PATH . "/config/blobs.php");
foreach($blobTypes as $k => $v) {
if ( $v[1] && matchFileHeader($rs, $v[1]) ) {
$btype = $k;
break;
}
}
$extra = 'onclick="vwBlb(this, '.$numRecord.', \''.$btype.'\')"';
}
$span .= "</span>";
// for binary fields, editing is separated from interface
if ($binary) {
$span .= "<span title=\"" . str_replace('{{NUM}}', $length, __('Click to view/edit column data [{{NUM}} Bytes]')). "\" class=\"blob $btype\" $extra>&nbsp;</span>";
return $span;
}
$span .= '<span class="d" style="display:none">' . htmlspecialchars($rs) . '</span>';
// for non editable text fields, we need to show the data associated
if (!$editable && $rs !== NULL && MAX_TEXT_LENGTH_DISPLAY < $length) {
$extra = 'onclick="vwTxt(this, &quot;'.$size.'&quot;, \''.$btype.'\')"';
$span .= "<span title=\"" . str_replace('{{NUM}}', $length, __('Click to view/edit column data [{{NUM}} Bytes]')). "\" class=\"blob $btype\" $extra>&nbsp;</span>";
}
return $span;
}
function setDbVar( $variable, $value ) {
Session::set('vars', $variable, $value);
}
function loadDbVars(&$db) {
$vars = Session::get_all('vars');
foreach($vars as $variable => $value) {
$query = 'SET @'.$variable.' = '.$value;
$db->query($query);
}
}
?>