From ed1d212ae2daea5e4bd043417610177093e99f19 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Sat, 16 Jan 2016 03:03:51 -0500
Subject: [PATCH] Improved SVG cleanup code
---
program/lib/Roundcube/rcube_db.php | 223 +++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 189 insertions(+), 34 deletions(-)
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index 29f125d..ba3acf6 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -50,6 +50,7 @@
);
const DEBUG_LINE_LENGTH = 4096;
+ const DEFAULT_QUOTE = '`';
/**
* Factory, returns driver-specific instance of the class
@@ -68,6 +69,8 @@
'sybase' => 'mssql',
'dblib' => 'mssql',
'mysqli' => 'mysql',
+ 'oci' => 'oracle',
+ 'oci8' => 'oracle',
);
$driver = isset($driver_map[$driver]) ? $driver_map[$driver] : $driver;
@@ -128,13 +131,23 @@
return $this->dbh;
}
+ // connect to database
+ if ($dbh = $this->conn_create($dsn)) {
+ $this->dbh = $dbh;
+ $this->dbhs[$mode] = $dbh;
+ $this->db_mode = $mode;
+ $this->db_connected = true;
+ }
+ }
+
+ /**
+ * Create PDO connection
+ */
+ protected function conn_create($dsn)
+ {
// Get database specific connection options
$dsn_string = $this->dsn_string($dsn);
$dsn_options = $this->dsn_options($dsn);
-
- if ($this->db_pconn) {
- $dsn_options[PDO::ATTR_PERSISTENT] = true;
- }
// Connect
try {
@@ -149,6 +162,8 @@
// don't throw exceptions or warnings
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+
+ $this->conn_configure($dsn, $dbh);
}
catch (Exception $e) {
$this->db_error = true;
@@ -161,11 +176,7 @@
return null;
}
- $this->dbh = $dbh;
- $this->dbhs[$mode] = $dbh;
- $this->db_mode = $mode;
- $this->db_connected = true;
- $this->conn_configure($dsn, $dbh);
+ return $dbh;
}
/**
@@ -237,8 +248,12 @@
// Read or write ?
$mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w';
+ $start = '[' . $this->options['identifier_start'] . self::DEFAULT_QUOTE . ']';
+ $end = '[' . $this->options['identifier_end'] . self::DEFAULT_QUOTE . ']';
+ $regex = '/(?:^|\s)(from|update|into|join)\s+'.$start.'?([a-z0-9._]+)'.$end.'?\s+/i';
+
// find tables involved in this query
- if (preg_match_all('/(?:^|\s)(from|update|into|join)\s+'.$this->options['identifier_start'].'?([a-z0-9._]+)'.$this->options['identifier_end'].'?\s+/i', $query, $matches, PREG_SET_ORDER)) {
+ if (preg_match_all($regex, $query, $matches, PREG_SET_ORDER)) {
foreach ($matches as $m) {
$table = $m[2];
@@ -338,7 +353,7 @@
public function get_variable($varname, $default = null)
{
// to be implemented by driver class
- return $default;
+ return rcube::get_instance()->config->get('db_' . $varname, $default);
}
/**
@@ -407,6 +422,9 @@
$query = $this->set_limit($query, $numrows, $offset);
}
+ // replace self::DEFAULT_QUOTE with driver-specific quoting
+ $query = $this->query_parse($query);
+
// Because in Roundcube we mostly use queries that are
// executed only once, we will not use prepared queries
$pos = 0;
@@ -426,14 +444,28 @@
}
}
- // replace escaped '?' back to normal, see self::quote()
- $query = str_replace('??', '?', $query);
$query = rtrim($query, " \t\n\r\0\x0B;");
+ // replace escaped '?' and quotes back to normal, see self::quote()
+ $query = str_replace(
+ array('??', self::DEFAULT_QUOTE.self::DEFAULT_QUOTE),
+ array('?', self::DEFAULT_QUOTE),
+ $query
+ );
+
+ // log query
$this->debug($query);
+ return $this->query_execute($query);
+ }
+
+ /**
+ * Query execution
+ */
+ protected function query_execute($query)
+ {
// destroy reference to previous result, required for SQLite driver (#1488874)
- $this->last_result = null;
+ $this->last_result = null;
$this->db_error_msg = null;
// send query
@@ -443,9 +475,49 @@
$result = $this->handle_error($query);
}
- $this->last_result = $result;
+ return $this->last_result = $result;
+ }
- return $result;
+ /**
+ * Parse SQL query and replace identifier quoting
+ *
+ * @param string $query SQL query
+ *
+ * @return string SQL query
+ */
+ protected function query_parse($query)
+ {
+ $start = $this->options['identifier_start'];
+ $end = $this->options['identifier_end'];
+ $quote = self::DEFAULT_QUOTE;
+
+ if ($start == $quote) {
+ return $query;
+ }
+
+ $pos = 0;
+ $in = false;
+
+ while ($pos = strpos($query, $quote, $pos)) {
+ if ($query[$pos+1] == $quote) { // skip escaped quote
+ $pos += 2;
+ }
+ else {
+ if ($in) {
+ $q = $end;
+ $in = false;
+ }
+ else {
+ $q = $start;
+ $in = true;
+ }
+
+ $query = substr_replace($query, $q, $pos, 1);
+ $pos++;
+ }
+ }
+
+ return $query;
}
/**
@@ -482,7 +554,9 @@
public function affected_rows($result = null)
{
if ($result || ($result === null && ($result = $this->last_result))) {
- return $result->rowCount();
+ if ($result !== true) {
+ return $result->rowCount();
+ }
}
return 0;
@@ -498,7 +572,7 @@
*/
public function num_rows($result = null)
{
- if ($result || ($result === null && ($result = $this->last_result))) {
+ if (($result || ($result === null && ($result = $this->last_result))) && $result !== true) {
// repeat query with SELECT COUNT(*) ...
if (preg_match('/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/ims', $result->queryString, $m)) {
$query = $this->dbh->query('SELECT COUNT(*) FROM ' . $m[1], PDO::FETCH_NUM);
@@ -574,7 +648,9 @@
protected function _fetch_row($result, $mode)
{
if ($result || ($result === null && ($result = $this->last_result))) {
- return $result->fetch($mode);
+ if ($result !== true) {
+ return $result->fetch($mode);
+ }
}
return false;
@@ -611,15 +687,11 @@
{
// get tables if not cached
if ($this->tables === null) {
- $q = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME',
- array($this->db_dsnw_array['database']));
+ $q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"
+ . " WHERE TABLE_TYPE = 'BASE TABLE'"
+ . " ORDER BY TABLE_NAME");
- if ($q) {
- $this->tables = $q->fetchAll(PDO::FETCH_COLUMN, 0);
- }
- else {
- $this->tables = array();
- }
+ $this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : array();
}
return $this->tables;
@@ -634,14 +706,71 @@
*/
public function list_cols($table)
{
- $q = $this->query('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?',
- array($table, $this->db_dsnw_array['database']));
+ $q = $this->query('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?',
+ array($table));
if ($q) {
return $q->fetchAll(PDO::FETCH_COLUMN, 0);
}
return array();
+ }
+
+ /**
+ * Start transaction
+ *
+ * @return bool True on success, False on failure
+ */
+ public function startTransaction()
+ {
+ $this->db_connect('w', true);
+
+ // check connection before proceeding
+ if (!$this->is_connected()) {
+ return $this->last_result = false;
+ }
+
+ $this->debug('BEGIN TRANSACTION');
+
+ return $this->last_result = $this->dbh->beginTransaction();
+ }
+
+ /**
+ * Commit transaction
+ *
+ * @return bool True on success, False on failure
+ */
+ public function endTransaction()
+ {
+ $this->db_connect('w', true);
+
+ // check connection before proceeding
+ if (!$this->is_connected()) {
+ return $this->last_result = false;
+ }
+
+ $this->debug('COMMIT TRANSACTION');
+
+ return $this->last_result = $this->dbh->commit();
+ }
+
+ /**
+ * Rollback transaction
+ *
+ * @return bool True on success, False on failure
+ */
+ public function rollbackTransaction()
+ {
+ $this->db_connect('w', true);
+
+ // check connection before proceeding
+ if (!$this->is_connected()) {
+ return $this->last_result = false;
+ }
+
+ $this->debug('ROLLBACK TRANSACTION');
+
+ return $this->last_result = $this->dbh->rollBack();
}
/**
@@ -677,8 +806,13 @@
'bool' => PDO::PARAM_BOOL,
'integer' => PDO::PARAM_INT,
);
+
$type = isset($map[$type]) ? $map[$type] : PDO::PARAM_STR;
- return strtr($this->dbh->quote($input, $type), array('?' => '??')); // escape ?
+
+ return strtr($this->dbh->quote($input, $type),
+ // escape ? and `
+ array('?' => '??', self::DEFAULT_QUOTE => self::DEFAULT_QUOTE.self::DEFAULT_QUOTE)
+ );
}
return 'NULL';
@@ -917,15 +1051,24 @@
/**
* Return correct name for a specific database table
*
- * @param string $table Table name
+ * @param string $table Table name
+ * @param bool $quoted Quote table identifier
*
* @return string Translated table name
*/
- public function table_name($table)
+ public function table_name($table, $quoted = false)
{
+ // let plugins alter the table name (#1489837)
+ $plugin = rcube::get_instance()->plugins->exec_hook('db_table_name', array('table' => $table));
+ $table = $plugin['table'];
+
// add prefix to the table name if configured
if (($prefix = $this->options['table_prefix']) && strpos($table, $prefix) !== 0) {
- return $prefix . $table;
+ $table = $prefix . $table;
+ }
+
+ if ($quoted) {
+ $table = $this->quote_identifier($table);
}
return $table;
@@ -1040,7 +1183,7 @@
}
// process the different protocol options
- $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
+ $parsed['protocol'] = $proto ?: 'tcp';
$proto_opts = rawurldecode($proto_opts);
if (strpos($proto_opts, ':') !== false) {
list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
@@ -1124,6 +1267,18 @@
{
$result = array();
+ if ($this->db_pconn) {
+ $result[PDO::ATTR_PERSISTENT] = true;
+ }
+
+ if (!empty($dsn['prefetch'])) {
+ $result[PDO::ATTR_PREFETCH] = (int) $dsn['prefetch'];
+ }
+
+ if (!empty($dsn['timeout'])) {
+ $result[PDO::ATTR_TIMEOUT] = (int) $dsn['timeout'];
+ }
+
return $result;
}
--
Gitblit v1.9.1