You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
847 lines
26 KiB
847 lines
26 KiB
<?php |
|
|
|
/** |
|
* Sql functions/classes for OpenEMR. |
|
* |
|
* Includes classes and functions that OpenEMR uses |
|
* to interact with SQL. |
|
* |
|
* @package OpenEMR |
|
* @link http://www.open-emr.org |
|
* @author Brady Miller <brady.g.miller@gmail.com> |
|
* @author Kevin Yeh <kevin.y@integralemr.com> |
|
* @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com> |
|
* @copyright Copyright (c) 2013 Kevin Yeh <kevin.y@integralemr.com> |
|
* @copyright Copyright (c) 2013 OEMR <www.oemr.org> |
|
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 |
|
*/ |
|
|
|
require_once(dirname(__FILE__) . "/sqlconf.php"); |
|
require_once(dirname(__FILE__) . "/../vendor/adodb/adodb-php/adodb.inc.php"); |
|
require_once(dirname(__FILE__) . "/../vendor/adodb/adodb-php/drivers/adodb-mysqli.inc.php"); |
|
require_once(dirname(__FILE__) . "/ADODB_mysqli_log.php"); |
|
|
|
if (!defined('ADODB_FETCH_ASSOC')) { |
|
define('ADODB_FETCH_ASSOC', 2); |
|
} |
|
|
|
$database = NewADOConnection("mysqli_log"); // Use the subclassed driver which logs execute events |
|
// Below optionFlags flag is telling the mysql connection to ensure local_infile setting, |
|
// which is needed to import data in the Administration->Other->External Data Loads feature. |
|
// (Note the MYSQLI_READ_DEFAULT_GROUP is just to keep the current setting hard-coded in adodb) |
|
$database->setConnectionParameter(MYSQLI_READ_DEFAULT_GROUP, 0); |
|
$database->setConnectionParameter(MYSQLI_OPT_LOCAL_INFILE, 1); |
|
// Set mysql to use ssl, if applicable. |
|
// Can support basic encryption by including just the mysql-ca pem (this is mandatory for ssl) |
|
// Can also support client based certificate if also include mysql-cert and mysql-key (this is optional for ssl) |
|
if (file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-ca")) { |
|
if (defined('MYSQLI_CLIENT_SSL')) { |
|
if ( |
|
file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-key") && |
|
file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-cert") |
|
) { |
|
// with client side certificate/key |
|
$database->ssl_key = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-key"; |
|
$database->ssl_cert = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-cert"; |
|
$database->ssl_ca = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-ca"; |
|
} else { |
|
// without client side certificate/key |
|
$database->ssl_ca = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-ca"; |
|
} |
|
$database->clientFlags = MYSQLI_CLIENT_SSL; |
|
} |
|
} |
|
$database->port = $port; |
|
if ((!empty($GLOBALS["enable_database_connection_pooling"]) || !empty($_SESSION["enable_database_connection_pooling"])) && empty($GLOBALS['connection_pooling_off'])) { |
|
$database->PConnect($host, $login, $pass, $dbase); |
|
} else { |
|
$database->connect($host, $login, $pass, $dbase); |
|
} |
|
$GLOBALS['adodb']['db'] = $database; |
|
$GLOBALS['dbh'] = $database->_connectionID; |
|
|
|
// Modified 5/2009 by BM for UTF-8 project --------- |
|
if (!$disable_utf8_flag) { |
|
if (!empty($sqlconf["db_encoding"]) && ($sqlconf["db_encoding"] == "utf8mb4")) { |
|
$success_flag = $database->ExecuteNoLog("SET NAMES 'utf8mb4'"); |
|
if (!$success_flag) { |
|
error_log("PHP custom error: from openemr library/sql.inc - Unable to set up UTF8MB4 encoding with mysql database: " . errorLogEscape(getSqlLastError()), 0); |
|
} |
|
} else { |
|
$success_flag = $database->ExecuteNoLog("SET NAMES 'utf8'"); |
|
if (!$success_flag) { |
|
error_log("PHP custom error: from openemr library/sql.inc - Unable to set up UTF8 encoding with mysql database: " . errorLogEscape(getSqlLastError()), 0); |
|
} |
|
} |
|
} |
|
|
|
// Turn off STRICT SQL |
|
$sql_strict_set_success = $database->ExecuteNoLog("SET sql_mode = ''"); |
|
if (!$sql_strict_set_success) { |
|
error_log("Unable to set strict sql setting: " . errorLogEscape(getSqlLastError()), 0); |
|
} |
|
|
|
// set up associations in adodb calls (not sure why above define |
|
// command does not work) |
|
$GLOBALS['adodb']['db']->SetFetchMode(ADODB_FETCH_ASSOC); |
|
|
|
if (!empty($GLOBALS['debug_ssl_mysql_connection'])) { |
|
error_log("CHECK SSL CIPHER IN MAIN ADODB: " . errorLogEscape(print_r($GLOBALS['adodb']['db']->ExecuteNoLog("SHOW STATUS LIKE 'Ssl_cipher';")->fields, true))); |
|
} |
|
|
|
//fmg: This makes the login screen informative when no connection can be made |
|
if (!$GLOBALS['dbh']) { |
|
//try to be more helpful |
|
if ($host == "localhost") { |
|
echo "Check that mysqld is running.<p>"; |
|
} else { |
|
echo "Check that you can ping the server " . text($host) . ".<p>"; |
|
}//if local |
|
HelpfulDie("Could not connect to server!", getSqlLastError()); |
|
exit; |
|
}//if no connection |
|
|
|
/** |
|
* Standard sql query in OpenEMR. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. Will continue to |
|
* be compatible with previous function calls that do |
|
* not use binding. |
|
* It will return a recordset object. |
|
* The sqlFetchArray() function should be used to |
|
* utilize the return object. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return recordset |
|
*/ |
|
function sqlStatement($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
// Use adodb Execute with binding and return a recordset. |
|
// Note that the auditSQLEvent function is embedded |
|
// in the Execute command. |
|
$recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds); |
|
if ($recordset === false) { |
|
HelpfulDie("query failed: $statement", getSqlLastError()); |
|
} |
|
|
|
return $recordset; |
|
} |
|
|
|
/** |
|
* Standard sql query in OpenEMR. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. Will continue to |
|
* be compatible with previous function calls that do |
|
* not use binding. |
|
* It will return a recordset object. |
|
* The sqlFetchArray() function should be used to |
|
* utilize the return object. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return recordset |
|
*/ |
|
function sqlStatementThrowException($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
//Run a adodb execute |
|
// Note the auditSQLEvent function is embedded in the |
|
// Execute function. |
|
$recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds, true); |
|
if ($recordset === false) { |
|
throw new \OpenEMR\Common\Database\SqlQueryException($statement, "Failed to execute statement. Error: " . getSqlLastError() . " Statement: " . $statement); |
|
} |
|
return $recordset; |
|
} |
|
|
|
/** |
|
* Return the last inserted sql id for a query. |
|
* @return int |
|
*/ |
|
function sqlGetLastInsertId() |
|
{ |
|
// Return the correct last id generated using function |
|
// that is safe with the audit engine. |
|
return $GLOBALS['lastidado'] > 0 ? $GLOBALS['lastidado'] : $GLOBALS['adodb']['db']->Insert_ID(); |
|
} |
|
|
|
/** |
|
* Specialized sql query in OpenEMR that skips auditing. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. Will continue to |
|
* be compatible with previous function calls that do |
|
* not use binding. It is equivalent to the |
|
* sqlStatement() function, EXCEPT it skips the |
|
* audit engine. This function should only be used |
|
* in very special situations. |
|
* It will return a recordset object. |
|
* The sqlFetchArray() function should be used to |
|
* utilize the return object. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return recordset |
|
*/ |
|
function sqlStatementNoLog($statement, $binds = false, $throw_exception_on_error = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
// Use adodb ExecuteNoLog with binding and return a recordset. |
|
$recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds); |
|
if ($recordset === false) { |
|
if ($throw_exception_on_error) { |
|
throw new \OpenEMR\Common\Database\SqlQueryException($statement, "Failed to execute statement. Error: " . getSqlLastError() . " Statement: " . $statement); |
|
} else { |
|
HelpfulDie("query failed: $statement", getSqlLastError()); |
|
} |
|
} |
|
|
|
return $recordset; |
|
} |
|
|
|
/** |
|
* sqlStatement() function wrapper for CDR engine in OpenEMR. |
|
* Allows option to turn on/off auditing specifically for the |
|
* CDR engine. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return recordset/resource |
|
*/ |
|
function sqlStatementCdrEngine($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
if ($GLOBALS['audit_events_cdr']) { |
|
return sqlStatement($statement, $binds); |
|
} else { |
|
return sqlStatementNoLog($statement, $binds); |
|
} |
|
} |
|
|
|
/** |
|
* Returns a row (as an array) from a sql recordset. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. |
|
* It will act upon the object returned from the |
|
* sqlStatement() function (and sqlQ() function). |
|
* |
|
* @param recordset $r |
|
* @return array |
|
*/ |
|
function sqlFetchArray($r) |
|
{ |
|
//treat as an adodb recordset |
|
if ($r === false) { |
|
return false; |
|
} |
|
|
|
if ($r->EOF ?? '') { |
|
return false; |
|
} |
|
|
|
//ensure it's an object (ie. is set) |
|
if (!is_object($r)) { |
|
return false; |
|
} |
|
|
|
return $r->FetchRow(); |
|
} |
|
|
|
|
|
/** |
|
* Wrapper for ADODB getAssoc |
|
* |
|
* @see http://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getassoc |
|
* |
|
* @param string $sql |
|
* @param string[] $bindvars |
|
* @param boolean $forceArray |
|
* @param boolean $first2Cols |
|
* @return array |
|
*/ |
|
function sqlGetAssoc($sql, $bindvars = false, $forceArray = false, $first2Cols = false) |
|
{ |
|
|
|
return $GLOBALS['adodb']['db']->getAssoc($sql, $bindvars, $forceArray, $first2Cols); |
|
} |
|
|
|
/** |
|
* Standard sql insert query in OpenEMR. |
|
* Only use this function if you need to have the |
|
* id returned. If doing an insert query and do |
|
* not need the id returned, then use the |
|
* sqlStatement function instead. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. This function |
|
* is specialized for insert function and will return |
|
* the last id generated from the insert. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return integer Last id generated from the sql insert command |
|
*/ |
|
function sqlInsert($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
//Run a adodb execute |
|
// Note the auditSQLEvent function is embedded in the |
|
// Execute function. |
|
$recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds, true); |
|
if ($recordset === false) { |
|
HelpfulDie("insert failed: $statement", getSqlLastError()); |
|
} |
|
|
|
// Return the correct last id generated using function |
|
// that is safe with the audit engine. |
|
return $GLOBALS['lastidado'] > 0 ? $GLOBALS['lastidado'] : $GLOBALS['adodb']['db']->Insert_ID(); |
|
} |
|
|
|
/** |
|
* Specialized sql query in OpenEMR that only returns |
|
* the first row of query results as an associative array. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return array |
|
*/ |
|
function sqlQuery($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
$recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds); |
|
|
|
if ($recordset === false) { |
|
HelpfulDie("query failed: $statement", getSqlLastError()); |
|
} |
|
|
|
if ($recordset->EOF) { |
|
return false; |
|
} |
|
|
|
$rez = $recordset->FetchRow(); |
|
if ($rez == false) { |
|
return false; |
|
} |
|
|
|
return $rez; |
|
} |
|
|
|
/** |
|
* Specialized sql query in OpenEMR that bypasses the auditing engine |
|
* and only returns the first row of query results as an associative array. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. It is equivalent to the |
|
* sqlQuery() function, EXCEPT it skips the |
|
* audit engine. This function should only be used |
|
* in very special situations. |
|
* |
|
* Note: If you do an INSERT or UPDATE statement you will get an empty string ("") as a response |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return array|false|"" |
|
*/ |
|
function sqlQueryNoLog($statement, $binds = false, $throw_exception_on_error = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
$recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds); |
|
|
|
if ($recordset === false) { |
|
if ($throw_exception_on_error) { |
|
throw new \OpenEMR\Common\Database\SqlQueryException($statement, "Failed to execute statement. Error: " . getSqlLastError() . " Statement: " . $statement); |
|
} else { |
|
HelpfulDie("query failed: $statement", getSqlLastError()); |
|
} |
|
} |
|
|
|
if ($recordset->EOF) { |
|
return false; |
|
} |
|
|
|
$rez = $recordset->FetchRow(); |
|
if ($rez == false) { |
|
return false; |
|
} |
|
|
|
return $rez; |
|
} |
|
|
|
/** |
|
* Specialized sql query in OpenEMR that ignores sql errors, bypasses the |
|
* auditing engine and only returns the first row of query results as an |
|
* associative array. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. It is equivalent to the |
|
* sqlQuery() function, EXCEPT it skips the |
|
* audit engine and ignores erros. This function should only be used |
|
* in very special situations. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return array |
|
*/ |
|
function sqlQueryNoLogIgnoreError($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
$recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds); |
|
|
|
if ($recordset === false) { |
|
// ignore the error and return FALSE |
|
return false; |
|
} |
|
|
|
if ($recordset->EOF) { |
|
return false; |
|
} |
|
|
|
$rez = $recordset->FetchRow(); |
|
if ($rez == false) { |
|
return false; |
|
} |
|
|
|
return $rez; |
|
} |
|
|
|
/** |
|
* sqlQuery() function wrapper for CDR engine in OpenEMR. |
|
* Allows option to turn on/off auditing specifically for the |
|
* CDR engine. |
|
* |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return array |
|
*/ |
|
function sqlQueryCdrEngine($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
if ($GLOBALS['audit_events_cdr']) { |
|
return sqlQuery($statement, $binds); |
|
} else { |
|
return sqlQueryNoLog($statement, $binds); |
|
} |
|
} |
|
|
|
/** |
|
* Specialized sql query in OpenEMR that skips auditing. |
|
* |
|
* This function should only be used in very special situations. |
|
* |
|
* @param string $statement query |
|
*/ |
|
function sqlInsertClean_audit($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
$ret = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds); |
|
if ($ret === false) { |
|
HelpfulDie("insert failed: $statement", getSqlLastError()); |
|
} |
|
} |
|
|
|
/** |
|
* Function that will safely return the last error, |
|
* and accounts for the audit engine. |
|
* |
|
* @param string $mode either adodb(default) or native_mysql |
|
* @return string last mysql error |
|
*/ |
|
function getSqlLastError() |
|
{ |
|
return !empty($GLOBALS['last_mysql_error']) ? $GLOBALS['last_mysql_error'] : $GLOBALS['adodb']['db']->ErrorMsg(); |
|
} |
|
|
|
/** |
|
* Function that will safely return the last error no, |
|
* and accounts for the audit engine. |
|
* |
|
* @param string $mode either adodb(default) or native_mysql |
|
* @return string last mysql error no |
|
*/ |
|
function getSqlLastErrorNo() |
|
{ |
|
return !empty($GLOBALS['last_mysql_error_no']) ? $GLOBALS['last_mysql_error_no'] : $GLOBALS['adodb']['db']->ErrorNo(); |
|
} |
|
|
|
/** |
|
* Function that will return an array listing |
|
* of columns that exist in a table. |
|
* |
|
* @param string $table sql table |
|
* @return array |
|
*/ |
|
function sqlListFields($table) |
|
{ |
|
$sql = "SHOW COLUMNS FROM " . add_escape_custom($table); |
|
$resource = sqlStatementNoLog($sql); |
|
$field_list = array(); |
|
while ($row = sqlFetchArray($resource)) { |
|
$field_list[] = $row['Field']; |
|
} |
|
|
|
return $field_list; |
|
} |
|
|
|
/** |
|
* Returns the number of sql rows |
|
* |
|
* @param recordset $r |
|
* @return integer Number of rows |
|
*/ |
|
function sqlNumRows($r) |
|
{ |
|
return $r->RecordCount(); |
|
} |
|
|
|
/** |
|
* Error function for OpenEMR sql functions |
|
* |
|
* @param string $statement |
|
* @param string $sqlerr |
|
*/ |
|
function HelpfulDie($statement, $sqlerr = '') |
|
{ |
|
|
|
echo "<h2><font color='red'>" . xlt('Query Error') . "</font></h2>"; |
|
|
|
if (!$GLOBALS['sql_string_no_show_screen']) { |
|
echo "<p><font color='red'>ERROR:</font> " . text($statement) . "</p>"; |
|
} |
|
|
|
$logMsg = "SQL Error with statement:" . $statement; |
|
|
|
if ($sqlerr) { |
|
if (!$GLOBALS['sql_string_no_show_screen']) { |
|
echo "<p>Error: <font color='red'>" . text($sqlerr) . "</font></p>"; |
|
} |
|
|
|
$logMsg .= "--" . $sqlerr; |
|
}//if error |
|
|
|
$backtrace = debug_backtrace(); |
|
|
|
if (!$GLOBALS['sql_string_no_show_screen']) { |
|
for ($level = 1; $level < count($backtrace); $level++) { |
|
$info = $backtrace[$level]; |
|
echo "<br />" . text($info["file"] . " at " . $info["line"] . ":" . $info["function"]); |
|
if ($level > 1) { |
|
echo "(" . text(implode(",", $info["args"])) . ")"; |
|
} |
|
} |
|
} |
|
|
|
$logMsg .= "==>" . $backtrace[1]["file"] . " at " . $backtrace[1]["line"] . ":" . $backtrace[1]["function"]; |
|
|
|
error_log(errorLogEscape($logMsg)); |
|
|
|
exit; |
|
} |
|
|
|
/** |
|
* Function provides generation of sequence numbers with built-in ADOdb function. |
|
* Increments the number in the sequences table. |
|
* One example of use is the counter for form_id in the forms table. |
|
* |
|
* @return integer |
|
*/ |
|
function generate_id() |
|
{ |
|
$database = $GLOBALS['adodb']['db']; |
|
return $database->GenID("sequences"); |
|
} |
|
|
|
/** |
|
* Deprecated function. Standard sql query in OpenEMR. |
|
* |
|
* Function that will allow use of the adodb binding |
|
* feature to prevent sql-injection. Will continue to |
|
* be compatible with previous function calls that do |
|
* not use binding. |
|
* It will return a recordset object. |
|
* The sqlFetchArray() function should be used to |
|
* utilize the return object. |
|
* |
|
* @deprecated |
|
* @param string $statement query |
|
* @param array $binds binded variables array (optional) |
|
* @return recordset |
|
*/ |
|
function sqlQ($statement, $binds = false) |
|
{ |
|
// Below line is to avoid a nasty bug in windows. |
|
if (empty($binds)) { |
|
$binds = false; |
|
} |
|
|
|
$recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds) or |
|
HelpfulDie("query failed: $statement", getSqlLastError()); |
|
return $recordset; |
|
} |
|
|
|
|
|
/** |
|
* Sql close connection function (deprecated) |
|
* |
|
* No longer needed since PHP does this automatically. |
|
* |
|
* @deprecated |
|
* @return boolean |
|
*/ |
|
function sqlClose() |
|
{ |
|
//----------Close our mysql connection |
|
$closed = $GLOBALS['adodb']['db']->close or |
|
HelpfulDie("could not disconnect from mysql server link", getSqlLastError()); |
|
return $closed; |
|
} |
|
|
|
/** |
|
* Very simple wrapper function and not necessary (deprecated) |
|
* |
|
* Do not use. |
|
* |
|
* @deprecated |
|
* @return connection |
|
*/ |
|
function get_db() |
|
{ |
|
return $GLOBALS['adodb']['db']; |
|
} |
|
|
|
/** |
|
* Generic mysql select db function |
|
* Used when converted to mysqli to centralize special circumstances. |
|
* @param string $database |
|
*/ |
|
function generic_sql_select_db($database, $link = null) |
|
{ |
|
if (is_null($link)) { |
|
$link = $GLOBALS['dbh']; |
|
} |
|
|
|
mysqli_select_db($link, $database); |
|
} |
|
|
|
/** |
|
* Generic mysql affected rows function |
|
* Used when converted to mysqli to centralize special circumstances. |
|
* |
|
*/ |
|
function generic_sql_affected_rows() |
|
{ |
|
return mysqli_affected_rows($GLOBALS['dbh']); |
|
} |
|
|
|
/** |
|
* Generic mysql insert id function |
|
* Used when converted to mysqli to centralize special circumstances. |
|
* |
|
*/ |
|
function generic_sql_insert_id() |
|
{ |
|
return mysqli_insert_id($GLOBALS['dbh']); |
|
} |
|
|
|
|
|
/** |
|
* Begin a Transaction. |
|
*/ |
|
function sqlBeginTrans() |
|
{ |
|
$GLOBALS['adodb']['db']->BeginTrans(); |
|
} |
|
|
|
|
|
/** |
|
* Commit a transaction |
|
*/ |
|
function sqlCommitTrans($ok = true) |
|
{ |
|
$GLOBALS['adodb']['db']->CommitTrans(); |
|
} |
|
|
|
|
|
/** |
|
* Rollback a transaction |
|
*/ |
|
function sqlRollbackTrans() |
|
{ |
|
$GLOBALS['adodb']['db']->RollbackTrans(); |
|
} |
|
|
|
/** |
|
* For the 3 functions below: |
|
* |
|
* To support an optional higher level of security, queries that access password |
|
* related information use these functions instead of the standard functions |
|
* provided by sql.inc. |
|
* |
|
* By default, the privQuery and privStatement calls pass-through to |
|
* the existing ADODB instance initialized by sql.inc. |
|
* |
|
* If an additional configuration file is created (secure_sqlconf.php) and saved |
|
* in the sites/<sitename> directory (e.g. sites/default). The MySQL login |
|
* information defined in that file as $secure_* will be used to create an ADODB |
|
* instance specifically for querying privileged information. |
|
* |
|
* By configuring a server in this way, the default MySQL user can be denied access |
|
* to sensitive tables (currently only "users_secure" would qualify). Thus |
|
* the likelyhood of unintended modification can be reduced (e.g. through SQL Injection). |
|
* |
|
* Details on how to set this up are included in Documentation/privileged_db/priv_db_HOWTO |
|
* |
|
* The trade off for this additional security is extra complexity in configuration and |
|
* maintenance of the database, hence it is not enabled at install time and must be |
|
* done manually. |
|
* |
|
*/ |
|
function getPrivDB() |
|
{ |
|
if (!isset($GLOBALS['PRIV_DB'])) { |
|
$secure_config = $GLOBALS['OE_SITE_DIR'] . "/secure_sqlconf.php"; |
|
if (file_exists($secure_config)) { |
|
require_once($secure_config); |
|
$GLOBALS['PRIV_DB'] = NewADOConnection("mysqli_log"); // Use the subclassed driver which logs execute events |
|
// Below optionFlags flag is telling the mysql connection to ensure local_infile setting, |
|
// which is needed to import data in the Administration->Other->External Data Loads feature. |
|
// (Note the MYSQLI_READ_DEFAULT_GROUP is just to keep the current setting hard-coded in adodb) |
|
$GLOBALS['PRIV_DB']->setConnectionParameter(MYSQLI_READ_DEFAULT_GROUP, 0); |
|
$GLOBALS['PRIV_DB']->setConnectionParameter(MYSQLI_OPT_LOCAL_INFILE, 1); |
|
// Set mysql to use ssl, if applicable. |
|
// Can support basic encryption by including just the mysql-ca pem (this is mandatory for ssl) |
|
// Can also support client based certificate if also include mysql-cert and mysql-key (this is optional for ssl) |
|
if (file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-ca")) { |
|
if (defined('MYSQLI_CLIENT_SSL')) { |
|
if ( |
|
file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-key") && |
|
file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-cert") |
|
) { |
|
// with client side certificate/key |
|
$GLOBALS['PRIV_DB']->ssl_key = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-key"; |
|
$GLOBALS['PRIV_DB']->ssl_cert = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-cert"; |
|
$GLOBALS['PRIV_DB']->ssl_ca = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-ca"; |
|
} else { |
|
// without client side certificate/key |
|
$GLOBALS['PRIV_DB']->ssl_ca = "${GLOBALS['OE_SITE_DIR']}/documents/certificates/mysql-ca"; |
|
} |
|
$GLOBALS['PRIV_DB']->clientFlags = MYSQLI_CLIENT_SSL; |
|
} |
|
} |
|
$GLOBALS['PRIV_DB']->port = $port; |
|
if ((!empty($GLOBALS["enable_database_connection_pooling"]) || !empty($_SESSION["enable_database_connection_pooling"])) && empty($GLOBALS['connection_pooling_off'])) { |
|
$GLOBALS['PRIV_DB']->PConnect($secure_host, $secure_login, $secure_pass, $secure_dbase); |
|
} else { |
|
$GLOBALS['PRIV_DB']->connect($secure_host, $secure_login, $secure_pass, $secure_dbase); |
|
} |
|
// set up associations in adodb calls |
|
$GLOBALS['PRIV_DB']->SetFetchMode(ADODB_FETCH_ASSOC); |
|
// debug hook for ssl stuff |
|
if (!empty($GLOBALS['debug_ssl_mysql_connection'])) { |
|
error_log("CHECK SSL CIPHER IN PRIV_DB ADODB: " . errorLogEscape(print_r($GLOBALS[PRIV_DB]->ExecuteNoLog("SHOW STATUS LIKE 'Ssl_cipher';")->fields), true)); |
|
} |
|
} else { |
|
$GLOBALS['PRIV_DB'] = $GLOBALS['adodb']['db']; |
|
} |
|
} |
|
|
|
return $GLOBALS['PRIV_DB']; |
|
} |
|
/** |
|
* mechanism to use "super user" for SQL queries related to password operations |
|
* |
|
* @param type $sql |
|
* @param type $params |
|
* @return type |
|
*/ |
|
function privStatement($sql, $params = null) |
|
{ |
|
if (is_array($params)) { |
|
$recordset = getPrivDB()->ExecuteNoLog($sql, $params); |
|
} else { |
|
$recordset = getPrivDB()->ExecuteNoLog($sql); |
|
} |
|
|
|
if ($recordset === false) { |
|
// These error messages are explictly NOT run through xl() because we still |
|
// need them if there is a database problem. |
|
echo "Failure during database access! Check server error log."; |
|
$backtrace = debug_backtrace(); |
|
|
|
error_log("Executing as user:" . errorLogEscape(getPrivDB()->user) . " Statement failed:" . errorLogEscape($sql) . ":" . errorLogEscape($GLOBALS['last_mysql_error']) |
|
. "==>" . errorLogEscape($backtrace[1]["file"]) . " at " . errorLogEscape($backtrace[1]["line"]) . ":" . errorLogEscape($backtrace[1]["function"])); |
|
exit; |
|
} |
|
|
|
return $recordset; |
|
} |
|
/** |
|
* |
|
* Wrapper for privStatement that just returns the first row of a query or FALSE |
|
* if there were no results. |
|
* |
|
* @param type $sql |
|
* @param type $params |
|
* @return boolean |
|
*/ |
|
function privQuery($sql, $params = null) |
|
{ |
|
$recordset = privStatement($sql, $params); |
|
if ($recordset->EOF) { |
|
return false; |
|
} |
|
|
|
$rez = $recordset->FetchRow(); |
|
if ($rez == false) { |
|
return false; |
|
} |
|
|
|
return $rez; |
|
}
|
|
|