From 3412e50b54e3daac8745234e21ab6e72be0ed165 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Wed, 04 Jun 2014 11:20:33 -0400
Subject: [PATCH] Fix attachment menu structure and aria-attributes
---
program/lib/Roundcube/rcube_db.php | 111 ++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 85 insertions(+), 26 deletions(-)
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index 2861a91..a2271fd 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -31,8 +31,10 @@
protected $db_dsnr; // DSN for read operations
protected $db_connected = false; // Already connected ?
protected $db_mode; // Connection mode
+ protected $db_table_dsn_map = array();
protected $dbh; // Connection handle
protected $dbhs = array();
+ protected $table_connections = array();
protected $db_error = false;
protected $db_error_msg = '';
@@ -102,6 +104,8 @@
$this->db_dsnw_array = self::parse_dsn($db_dsnw);
$this->db_dsnr_array = self::parse_dsn($db_dsnr);
+
+ $this->db_table_dsn_map = array_map(array($this, 'table_name'), rcube::get_instance()->config->get('db_table_dsn', array()));
}
/**
@@ -185,8 +189,9 @@
* Connect to appropriate database depending on the operation
*
* @param string $mode Connection mode (r|w)
+ * @param boolean $force Enforce using the given mode
*/
- public function db_connect($mode)
+ public function db_connect($mode, $force = false)
{
// previous connection failed, don't attempt to connect again
if ($this->conn_failure) {
@@ -201,7 +206,7 @@
// Already connected
if ($this->db_connected) {
// connected to db with the same or "higher" mode (if allowed)
- if ($this->db_mode == $mode || $this->db_mode == 'w' && !$this->db_dsnw_noread) {
+ if ($this->db_mode == $mode || $this->db_mode == 'w' && !$force && !$this->db_dsnw_noread) {
return;
}
}
@@ -215,6 +220,46 @@
}
$this->conn_failure = !$this->db_connected;
+ }
+
+ /**
+ * Analyze the given SQL statement and select the appropriate connection to use
+ */
+ protected function dsn_select($query)
+ {
+ // no replication
+ if ($this->db_dsnw == $this->db_dsnr) {
+ return 'w';
+ }
+
+ // Read or write ?
+ $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w';
+
+ // 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)) {
+ foreach ($matches as $m) {
+ $table = $m[2];
+
+ // always use direct mapping
+ if ($this->db_table_dsn_map[$table]) {
+ $mode = $this->db_table_dsn_map[$table];
+ break; // primary table rules
+ }
+ else if ($mode == 'r') {
+ // connected to db with the same or "higher" mode for this table
+ $db_mode = $this->table_connections[$table];
+ if ($db_mode == 'w' && !$this->db_dsnw_noread) {
+ $mode = $db_mode;
+ }
+ }
+ }
+
+ // remember mode chosen (for primary table)
+ $table = $matches[0][2];
+ $this->table_connections[$table] = $mode;
+ }
+
+ return $mode;
}
/**
@@ -347,12 +392,9 @@
*/
protected function _query($query, $offset, $numrows, $params)
{
- $query = trim($query);
+ $query = ltrim($query);
- // Read or write ?
- $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w';
-
- $this->db_connect($mode);
+ $this->db_connect($this->dsn_select($query), true);
// check connection before proceeding
if (!$this->is_connected()) {
@@ -363,27 +405,28 @@
$query = $this->set_limit($query, $numrows, $offset);
}
- $params = (array) $params;
-
// Because in Roundcube we mostly use queries that are
// executed only once, we will not use prepared queries
$pos = 0;
$idx = 0;
- while ($pos = strpos($query, '?', $pos)) {
- if ($query[$pos+1] == '?') { // skip escaped ?
- $pos += 2;
- }
- else {
- $val = $this->quote($params[$idx++]);
- unset($params[$idx-1]);
- $query = substr_replace($query, $val, $pos, 1);
- $pos += strlen($val);
+ if (count($params)) {
+ while ($pos = strpos($query, '?', $pos)) {
+ if ($query[$pos+1] == '?') { // skip escaped '?'
+ $pos += 2;
+ }
+ else {
+ $val = $this->quote($params[$idx++]);
+ unset($params[$idx-1]);
+ $query = substr_replace($query, $val, $pos, 1);
+ $pos += strlen($val);
+ }
}
}
- // replace escaped ? back to normal
- $query = rtrim(strtr($query, array('??' => '?')), ';');
+ // replace escaped '?' back to normal, see self::quote()
+ $query = str_replace('??', '?', $query);
+ $query = rtrim($query, " \t\n\r\0\x0B;");
$this->debug($query);
@@ -414,7 +457,7 @@
{
$error = $this->dbh->errorInfo();
- if (empty($this->options['ignore_key_errors']) || $error[0] != '23000') {
+ if (empty($this->options['ignore_key_errors']) || !in_array($error[0], array('23000', '23505'))) {
$this->db_error = true;
$this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]);
@@ -566,7 +609,8 @@
{
// get tables if not cached
if ($this->tables === null) {
- $q = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME');
+ $q = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME',
+ array($this->db_dsnw_array['database']));
if ($q) {
$this->tables = $q->fetchAll(PDO::FETCH_COLUMN, 0);
@@ -588,8 +632,8 @@
*/
public function list_cols($table)
{
- $q = $this->query('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?',
- array($table));
+ $q = $this->query('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?',
+ array($table, $this->db_dsnw_array['database']));
if ($q) {
return $q->fetchAll(PDO::FETCH_COLUMN, 0);
@@ -877,10 +921,14 @@
*/
public function table_name($table)
{
- $rcube = rcube::get_instance();
+ static $rcube;
+
+ if (!$rcube) {
+ $rcube = rcube::get_instance();
+ }
// add prefix to the table name if configured
- if ($prefix = $rcube->config->get('db_prefix')) {
+ if (($prefix = $rcube->config->get('db_prefix')) && strpos($table, $prefix) !== 0) {
return $prefix . $table;
}
@@ -899,6 +947,17 @@
}
/**
+ * Set DSN connection to be used for the given table
+ *
+ * @param string Table name
+ * @param string DSN connection ('r' or 'w') to be used
+ */
+ public function set_table_dsn($table, $mode)
+ {
+ $this->db_table_dsn_map[$this->table_name($table)] = $mode;
+ }
+
+ /**
* MDB2 DSN string parser
*
* @param string $sequence Secuence name
--
Gitblit v1.9.1