diff --git a/program/include/rcube_mdb2.php b/program/include/rcube_mdb2.php deleted file mode 100644 index 721963b4c..000000000 --- a/program/include/rcube_mdb2.php +++ /dev/null @@ -1,935 +0,0 @@ - | - +-----------------------------------------------------------------------+ -*/ - - -/** - * Database independent query interface - * - * This is a wrapper for the PEAR::MDB2 class - * - * @package Database - * @author David Saez Padros - * @author Thomas Bruederli - * @author Lukas Kahwe Smith - * @version 1.18 - * @link http://pear.php.net/package/MDB2 - */ -class rcube_mdb2 -{ - public $db_dsnw; // DSN for write operations - public $db_dsnr; // DSN for read operations - public $db_connected = false; // Already connected ? - public $db_mode = ''; // Connection mode - public $db_handle = 0; // Connection handle - public $db_error = false; - public $db_error_msg = ''; - - private $debug_mode = false; - private $conn_failure = false; - private $a_query_results = array('dummy'); - private $last_res_id = 0; - private $tables; - private $variables; - - - /** - * Object constructor - * - * @param string $db_dsnw DSN for read/write operations - * @param string $db_dsnr Optional DSN for read only operations - */ - public function __construct($db_dsnw, $db_dsnr='', $pconn=false) - { - if (empty($db_dsnr)) { - $db_dsnr = $db_dsnw; - } - - $this->db_dsnw = $db_dsnw; - $this->db_dsnr = $db_dsnr; - $this->db_pconn = $pconn; - - $dsn_array = MDB2::parseDSN($db_dsnw); - $this->db_provider = $dsn_array['phptype']; - } - - - /** - * Connect to specific database - * - * @param string $dsn DSN for DB connections - * @return MDB2 PEAR database handle - * @access private - */ - private function dsn_connect($dsn) - { - // Use persistent connections if available - $db_options = array( - 'persistent' => $this->db_pconn, - 'emulate_prepared' => $this->debug_mode, - 'debug' => $this->debug_mode, - 'debug_handler' => array($this, 'debug_handler'), - 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL, - ); - - if ($this->db_provider == 'pgsql') { - $db_options['disable_smart_seqname'] = true; - $db_options['seqname_format'] = '%s'; - } - $this->db_error = false; - $this->db_error_msg = null; - - $dbh = MDB2::connect($dsn, $db_options); - - if (MDB2::isError($dbh)) { - $this->db_error = true; - $this->db_error_msg = $dbh->getMessage(); - - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $dbh->getUserInfo()), true, false); - } - else if ($this->db_provider == 'sqlite') { - $dsn_array = MDB2::parseDSN($dsn); - if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) { - $this->sqlite_create_database($dbh, $this->sqlite_initials); - } - } - else if ($this->db_provider != 'mssql' && $this->db_provider != 'sqlsrv') { - $dbh->setCharset('utf8'); - } - - return $dbh; - } - - - /** - * Connect to appropiate database depending on the operation - * - * @param string $mode Connection mode (r|w) - */ - public function db_connect($mode) - { - // previous connection failed, don't attempt to connect again - if ($this->conn_failure) { - return; - } - - // no replication - if ($this->db_dsnw == $this->db_dsnr) { - $mode = 'w'; - } - - // Already connected - if ($this->db_connected) { - // connected to db with the same or "higher" mode - if ($this->db_mode == 'w' || $this->db_mode == $mode) { - return; - } - } - - $dsn = ($mode == 'r') ? $this->db_dsnr : $this->db_dsnw; - - $this->db_handle = $this->dsn_connect($dsn); - $this->db_connected = !PEAR::isError($this->db_handle); - - // use write-master when read-only fails - if (!$this->db_connected && $mode == 'r') { - $mode = 'w'; - $this->db_handle = $this->dsn_connect($this->db_dsnw); - $this->db_connected = !PEAR::isError($this->db_handle); - } - - if ($this->db_connected) { - $this->db_mode = $mode; - } - else { - $this->conn_failure = true; - } - } - - - /** - * Activate/deactivate debug mode - * - * @param boolean $dbg True if SQL queries should be logged - */ - public function set_debug($dbg = true) - { - $this->debug_mode = $dbg; - if ($this->db_connected) { - $this->db_handle->setOption('debug', $dbg); - $this->db_handle->setOption('emulate_prepared', $dbg); - } - } - - - /** - * Getter for error state - * - * @param boolean True on error - */ - public function is_error() - { - return $this->db_error ? $this->db_error_msg : false; - } - - - /** - * Connection state checker - * - * @param boolean True if in connected state - */ - public function is_connected() - { - return PEAR::isError($this->db_handle) ? false : $this->db_connected; - } - - - /** - * Is database replication configured? - * This returns true if dsnw != dsnr - */ - public function is_replicated() - { - return !empty($this->db_dsnr) && $this->db_dsnw != $this->db_dsnr; - } - - - /** - * Get database runtime variables - * - * @param string Variable name - * @param mixed Default value if var is not set - * @return mixed Variable value or default - */ - public function get_variable($varname, $default = null) - { - if (!isset($this->variables)) { - $this->variables = array(); - - // only mysql and postgres are know to support this - if ($this->db_provider == 'pgsql' || $this->db_provider == 'mysql' || $this->db_provider == 'mysqli') { - $this->db_connect('r'); - $query = $this->db_provider == 'pgsql' ? 'SHOW ALL' : 'SHOW VARIABLES'; - foreach ((array)$this->db_handle->queryAll($query) as $row) - $this->variables[$row[0]] = $row[1]; - } - } - - return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; - } - - - /** - * Execute a SQL query - * - * @param string SQL query to execute - * @param mixed Values to be inserted in query - * - * @return number Query handle identifier - */ - public function query() - { - $params = func_get_args(); - $query = array_shift($params); - - // Support one argument of type array, instead of n arguments - if (count($params) == 1 && is_array($params[0])) { - $params = $params[0]; - } - - return $this->_query($query, 0, 0, $params); - } - - - /** - * Execute a SQL query with limits - * - * @param string SQL query to execute - * @param number Offset for LIMIT statement - * @param number Number of rows for LIMIT statement - * @param mixed Values to be inserted in query - * - * @return number Query handle identifier - */ - public function limitquery() - { - $params = func_get_args(); - $query = array_shift($params); - $offset = array_shift($params); - $numrows = array_shift($params); - - return $this->_query($query, $offset, $numrows, $params); - } - - - /** - * Execute a SQL query with limits - * - * @param string $query SQL query to execute - * @param number $offset Offset for LIMIT statement - * @param number $numrows Number of rows for LIMIT statement - * @param array $params Values to be inserted in query - * @return number Query handle identifier - * @access private - */ - private function _query($query, $offset, $numrows, $params) - { - // Read or write ? - $mode = (strtolower(substr(trim($query),0,6)) == 'select') ? 'r' : 'w'; - - $this->db_connect($mode); - - // check connection before proceeding - if (!$this->is_connected()) { - return null; - } - - if ($this->db_provider == 'sqlite') { - $this->sqlite_prepare(); - } - - if ($numrows || $offset) { - $result = $this->db_handle->setLimit($numrows,$offset); - } - - if (empty($params)) { - $result = $mode == 'r' ? $this->db_handle->query($query) : $this->db_handle->exec($query); - } - else { - $params = (array)$params; - $q = $this->db_handle->prepare($query, null, $mode=='w' ? MDB2_PREPARE_MANIP : null); - if ($this->db_handle->isError($q)) { - $this->db_error = true; - $this->db_error_msg = $q->userinfo; - - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg), true, false); - - $result = false; - } - else { - $result = $q->execute($params); - $q->free(); - } - } - - // add result, even if it's an error - return $this->_add_result($result); - } - - - /** - * Get number of rows for a SQL query - * If no query handle is specified, the last query will be taken as reference - * - * @param number $res_id Optional query handle identifier - * @return mixed Number of rows or false on failure - */ - public function num_rows($res_id=null) - { - if (!$this->db_connected) { - return false; - } - - if ($result = $this->_get_result($res_id)) { - return $result->numRows(); - } - - return false; - } - - - /** - * Get number of affected rows for the last query - * - * @param number $res_id Optional query handle identifier - * @return mixed Number of rows or false on failure - */ - public function affected_rows($res_id = null) - { - if (!$this->db_connected) { - return false; - } - - return $this->_get_result($res_id); - } - - - /** - * Get last inserted record ID - * For Postgres databases, a sequence name is required - * - * @param string $table Table name (to find the incremented sequence) - * - * @return mixed ID or false on failure - */ - public function insert_id($table = '') - { - if (!$this->db_connected || $this->db_mode == 'r') { - return false; - } - - if ($table) { - if ($this->db_provider == 'pgsql') { - // find sequence name - $table = $this->sequence_name($table); - } - else { - // resolve table name - $table = $this->table_name($table); - } - } - - $id = $this->db_handle->lastInsertID($table); - - return $this->db_handle->isError($id) ? null : $id; - } - - - /** - * Get an associative array for one row - * If no query handle is specified, the last query will be taken as reference - * - * @param number $res_id Optional query handle identifier - * - * @return mixed Array with col values or false on failure - */ - public function fetch_assoc($res_id = null) - { - $result = $this->_get_result($res_id); - return $this->_fetch_row($result, MDB2_FETCHMODE_ASSOC); - } - - - /** - * Get an index array for one row - * If no query handle is specified, the last query will be taken as reference - * - * @param number $res_id Optional query handle identifier - * - * @return mixed Array with col values or false on failure - */ - public function fetch_array($res_id = null) - { - $result = $this->_get_result($res_id); - return $this->_fetch_row($result, MDB2_FETCHMODE_ORDERED); - } - - - /** - * Get col values for a result row - * - * @param MDB2_Result_Common Query $result result handle - * @param number $mode Fetch mode identifier - * - * @return mixed Array with col values or false on failure - */ - private function _fetch_row($result, $mode) - { - if ($result === false || PEAR::isError($result) || !$this->is_connected()) { - return false; - } - - return $result->fetchRow($mode); - } - - - /** - * Wrapper for the SHOW TABLES command - * - * @return array List of all tables of the current database - * @since 0.4-beta - */ - public function list_tables() - { - // get tables if not cached - if (!$this->tables) { - $this->db_handle->loadModule('Manager'); - if (!PEAR::isError($result = $this->db_handle->listTables())) { - $this->tables = $result; - } - else { - $this->tables = array(); - } - } - - return $this->tables; - } - - - /** - * Wrapper for SHOW COLUMNS command - * - * @param string Table name - * - * @return array List of table cols - */ - public function list_cols($table) - { - $this->db_handle->loadModule('Manager'); - if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) { - return $result; - } - - return null; - } - - - /** - * Formats input so it can be safely used in a query - * - * @param mixed $input Value to quote - * @param string $type Type of data - * - * @return string Quoted/converted string for use in query - */ - public function quote($input, $type = null) - { - // handle int directly for better performance - if ($type == 'integer') { - return intval($input); - } - - // create DB handle if not available - if (!$this->db_handle) { - $this->db_connect('r'); - } - - return $this->db_connected ? $this->db_handle->quote($input, $type) : addslashes($input); - } - - - /** - * Quotes a string so it can be safely used as a table or column name - * - * @param string $str Value to quote - * - * @return string Quoted string for use in query - * @deprecated Replaced by rcube_MDB2::quote_identifier - * @see rcube_mdb2::quote_identifier - */ - public function quoteIdentifier($str) - { - return $this->quote_identifier($str); - } - - - /** - * Quotes a string so it can be safely used as a table or column name - * - * @param string $str Value to quote - * - * @return string Quoted string for use in query - */ - public function quote_identifier($str) - { - if (!$this->db_handle) { - $this->db_connect('r'); - } - - return $this->db_connected ? $this->db_handle->quoteIdentifier($str) : $str; - } - - - /** - * Escapes a string - * - * @param string $str The string to be escaped - * - * @return string The escaped string - * @since 0.1.1 - */ - public function escapeSimple($str) - { - if (!$this->db_handle) { - $this->db_connect('r'); - } - - return $this->db_handle->escape($str); - } - - - /** - * Return SQL function for current time and date - * - * @return string SQL function to use in query - */ - public function now() - { - switch ($this->db_provider) { - case 'mssql': - case 'sqlsrv': - return "getdate()"; - - default: - return "now()"; - } - } - - - /** - * Return list of elements for use with SQL's IN clause - * - * @param array $arr Input array - * @param string $type Type of data - * - * @return string Comma-separated list of quoted values for use in query - */ - public function array2list($arr, $type = null) - { - if (!is_array($arr)) { - return $this->quote($arr, $type); - } - - foreach ($arr as $idx => $item) { - $arr[$idx] = $this->quote($item, $type); - } - - return implode(',', $arr); - } - - - /** - * Return SQL statement to convert a field value into a unix timestamp - * - * This method is deprecated and should not be used anymore due to limitations - * of timestamp functions in Mysql (year 2038 problem) - * - * @param string $field Field name - * - * @return string SQL statement to use in query - * @deprecated - */ - public function unixtimestamp($field) - { - switch($this->db_provider) { - case 'pgsql': - return "EXTRACT (EPOCH FROM $field)"; - - case 'mssql': - case 'sqlsrv': - return "DATEDIFF(second, '19700101', $field) + DATEDIFF(second, GETDATE(), GETUTCDATE())"; - - default: - return "UNIX_TIMESTAMP($field)"; - } - } - - - /** - * Return SQL statement to convert from a unix timestamp - * - * @param string $timestamp Field name - * - * @return string SQL statement to use in query - */ - public function fromunixtime($timestamp) - { - return date("'Y-m-d H:i:s'", $timestamp); - } - - - /** - * Return SQL statement for case insensitive LIKE - * - * @param string $column Field name - * @param string $value Search value - * - * @return string SQL statement to use in query - */ - public function ilike($column, $value) - { - // TODO: use MDB2's matchPattern() function - switch ($this->db_provider) { - case 'pgsql': - return $this->quote_identifier($column).' ILIKE '.$this->quote($value); - default: - return $this->quote_identifier($column).' LIKE '.$this->quote($value); - } - } - - - /** - * Abstract SQL statement for value concatenation - * - * @return string SQL statement to be used in query - */ - public function concat(/* col1, col2, ... */) - { - $func = ''; - $args = func_get_args(); - if (is_array($args[0])) - $args = $args[0]; - - switch ($this->db_provider) { - case 'mysql': - case 'mysqli': - $func = 'CONCAT'; - $delim = ', '; - break; - case 'mssql': - case 'sqlsrv': - $delim = ' + '; - // Modify arguments, because + operator requires them to be of type varchar (#1488505) - // with SQL Server 2012 we can use just CONCAT(), but we need to support older versions - foreach ($args as $idx => $arg) { - $args[$idx] = "CAST($arg AS varchar)"; - } - break; - default: - $delim = ' || '; - } - - return $func . '(' . join($delim, $args) . ')'; - } - - - /** - * Encodes non-UTF-8 characters in string/array/object (recursive) - * - * @param mixed $input Data to fix - * - * @return mixed Properly UTF-8 encoded data - */ - public static function encode($input) - { - if (is_object($input)) { - foreach (get_object_vars($input) as $idx => $value) { - $input->$idx = self::encode($value); - } - return $input; - } - else if (is_array($input)) { - foreach ($input as $idx => $value) { - $input[$idx] = self::encode($value); - } - return $input; - } - - return utf8_encode($input); - } - - - /** - * Decodes encoded UTF-8 string/object/array (recursive) - * - * @param mixed $input Input data - * - * @return mixed Decoded data - */ - public static function decode($input) - { - if (is_object($input)) { - foreach (get_object_vars($input) as $idx => $value) { - $input->$idx = self::decode($value); - } - return $input; - } - else if (is_array($input)) { - foreach ($input as $idx => $value) { - $input[$idx] = self::decode($value); - } - return $input; - } - - return utf8_decode($input); - } - - - /** - * Adds a query result and returns a handle ID - * - * @param object $res Query handle - * - * @return mixed Handle ID - */ - private function _add_result($res) - { - // sql error occured - if (PEAR::isError($res)) { - $this->db_error = true; - $this->db_error_msg = $res->getMessage(); - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $res->getMessage() . " Query: " - . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 512)), - true, false); - } - - $res_id = sizeof($this->a_query_results); - $this->last_res_id = $res_id; - $this->a_query_results[$res_id] = $res; - return $res_id; - } - - - /** - * Resolves a given handle ID and returns the according query handle - * If no ID is specified, the last resource handle will be returned - * - * @param number $res_id Handle ID - * - * @return mixed Resource handle or false on failure - */ - private function _get_result($res_id = null) - { - if ($res_id == null) { - $res_id = $this->last_res_id; - } - - if (isset($this->a_query_results[$res_id])) { - if (!PEAR::isError($this->a_query_results[$res_id])) { - return $this->a_query_results[$res_id]; - } - } - - return false; - } - - - /** - * Create a sqlite database from a file - * - * @param MDB2 $dbh SQLite database handle - * @param string $file_name File path to use for DB creation - */ - private function sqlite_create_database($dbh, $file_name) - { - if (empty($file_name) || !is_string($file_name)) { - return; - } - - $data = file_get_contents($file_name); - - if (strlen($data)) { - if (!sqlite_exec($dbh->connection, $data, $error) || MDB2::isError($dbh)) { - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $error), true, false); - } - } - } - - - /** - * Add some proprietary database functions to the current SQLite handle - * in order to make it MySQL compatible - */ - private function sqlite_prepare() - { - // we emulate via callback some missing MySQL functions - sqlite_create_function($this->db_handle->connection, - 'unix_timestamp', array('rcube_mdb2', 'sqlite_unix_timestamp')); - sqlite_create_function($this->db_handle->connection, - 'now', array('rcube_mdb2', 'sqlite_now')); - } - - - /** - * Debug handler for the MDB2 - */ - function debug_handler(&$db, $scope, $message, $context = array()) - { - if ($scope != 'prepare') { - $debug_output = sprintf('%s(%d): %s;', - $scope, $db->db_index, rtrim($message, ';')); - rcube::write_log('sql', $debug_output); - } - } - - - /** - * Return correct name for a specific database table - * - * @param string $table Table name - * - * @return string Translated table name - */ - public function table_name($table) - { - $rcube = rcube::get_instance(); - - // return table name if configured - $config_key = 'db_table_'.$table; - - if ($name = $rcube->config->get($config_key)) { - return $name; - } - - return $table; - } - - - /** - * Return correct name for a specific database sequence - * (used for Postgres only) - * - * @param string $sequence Secuence name - * - * @return string Translated sequence name - */ - public function sequence_name($sequence) - { - $rcube = rcube::get_instance(); - - // return sequence name if configured - $config_key = 'db_sequence_'.$sequence; - - if ($name = $rcube->config->get($config_key)) { - return $name; - } - - return $sequence; - } - - - /** - * Callback for sqlite: unix_timestamp() - */ - public static function sqlite_unix_timestamp($timestamp = '') - { - $timestamp = trim($timestamp); - if (!$timestamp) { - $ret = time(); - } - else if (!preg_match('/^[0-9]+$/s', $timestamp)) { - $ret = strtotime($timestamp); - } - else { - $ret = $timestamp; - } - - return $ret; - } - - - /** - * Callback for sqlite: now() - */ - public static function sqlite_now() - { - return date("Y-m-d H:i:s"); - } - -}