* @category Horde * @copyright 2013-2017 Horde LLC * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 * @package Imap_Client */ class Horde_Imap_Client_Cache_Backend_Db extends Horde_Imap_Client_Cache_Backend { /** SQL table names. */ const BASE_TABLE = 'horde_imap_client_data'; const MD_TABLE = 'horde_imap_client_metadata'; const MSG_TABLE = 'horde_imap_client_message'; /** * Handle for the database connection. * * @var Horde_Db_Adapter */ protected $_db; /** * Constructor. * * @param array $params Configuration parameters: *
     *   - REQUIRED Parameters:
     *     - db: (Horde_Db_Adapter) DB object.
     * 
*/ public function __construct(array $params = array()) { if (!isset($params['db'])) { throw new InvalidArgumentException('Missing db parameter.'); } parent::__construct($params); } /** */ protected function _initOb() { $this->_db = $this->_params['db']; } /** */ public function get($mailbox, $uids, $fields, $uidvalid) { $this->getMetaData($mailbox, $uidvalid, array('uidvalid')); $query = $this->_baseSql($mailbox, self::MSG_TABLE); $query[0] = 'SELECT t.data, t.msguid ' . $query[0]; $uid_query = array(); foreach ($uids as $val) { $uid_query[] = 't.msguid = ?'; $query[1][] = strval($val); } $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')'; $compress = new Horde_Compress_Fast(); $out = array(); try { $columns = $this->_db->columns(self::MSG_TABLE); $res = $this->_db->select($query[0], $query[1]); foreach ($res as $row) { try { $out[$row['msguid']] = @unserialize($compress->decompress( $columns['data']->binaryToString($row['data']) )); } catch (Exception $e) {} } } catch (Horde_Db_Exception $e) {} return $out; } /** */ public function getCachedUids($mailbox, $uidvalid) { $this->getMetaData($mailbox, $uidvalid, array('uidvalid')); $query = $this->_baseSql($mailbox, self::MSG_TABLE); $query[0] = 'SELECT DISTINCT t.msguid ' . $query[0]; try { return $this->_db->selectValues($query[0], $query[1]); } catch (Horde_Db_Exception $e) { return array(); } } /** */ public function set($mailbox, $data, $uidvalid) { if ($uid = $this->_getUid($mailbox)) { $res = $this->get($mailbox, array_keys($data), array(), $uidvalid); } else { $res = array(); $uid = $this->_createUid($mailbox); } $compress = new Horde_Compress_Fast(); foreach ($data as $key => $val) { if (isset($res[$key])) { try { /* Update */ $this->_db->updateBlob( self::MSG_TABLE, array('data' => new Horde_Db_Value_Binary($compress->compress(serialize(array_merge($res[$key], $val))))), array( 'messageid = ? AND msguid = ?', array($uid, strval($key)) ) ); } catch (Horde_Db_Exception $e) {} } else { /* Insert */ try { $this->_db->insertBlob( self::MSG_TABLE, array( 'data' => new Horde_Db_Value_Binary($compress->compress(serialize($val))), 'msguid' => strval($key), 'messageid' => $uid ) ); } catch (Horde_Db_Exception $e) {} } } /* Update modified time. */ try { $this->_db->update( sprintf( 'UPDATE %s SET modified = ? WHERE messageid = ?', self::BASE_TABLE ), array(time(), $uid) ); } catch (Horde_Db_Exception $e) {} /* Update uidvalidity. */ $this->setMetaData($mailbox, array('uidvalid' => $uidvalid)); } /** */ public function getMetaData($mailbox, $uidvalid, $entries) { $query = $this->_baseSql($mailbox, self::MD_TABLE); $query[0] = 'SELECT t.field, t.data ' . $query[0]; if (!empty($entries)) { $entries[] = 'uidvalid'; $entry_query = array(); foreach (array_unique($entries) as $val) { $entry_query[] = 't.field = ?'; $query[1][] = $val; } $query[0] .= ' AND (' . implode(' OR ', $entry_query) . ')'; } try { if ($res = $this->_db->selectAssoc($query[0], $query[1])) { $columns = $this->_db->columns(self::MD_TABLE); foreach ($res as $key => $val) { switch ($key) { case 'uidvalid': $res[$key] = $columns['data']->binaryToString($val); break; default: try { $res[$key] = @unserialize( $columns['data']->binaryToString($val) ); } catch (Exception $e) {} break; } } if (is_null($uidvalid) || !isset($res['uidvalid']) || ($res['uidvalid'] == $uidvalid)) { return $res; } $this->deleteMailbox($mailbox); } } catch (Horde_Db_Exception $e) {} return array(); } /** */ public function setMetaData($mailbox, $data) { if (!($uid = $this->_getUid($mailbox))) { $uid = $this->_createUid($mailbox); } $query = sprintf('SELECT field FROM %s where messageid = ?', self::MD_TABLE); $values = array($uid); try { $fields = $this->_db->selectValues($query, $values); } catch (Horde_Db_Exception $e) { return; } foreach ($data as $key => $val) { $val = new Horde_Db_Value_Binary(($key == 'uidvalid') ? $val : serialize($val)); if (in_array($key, $fields)) { /* Update */ try { $this->_db->updateBlob( self::MD_TABLE, array('data' => $val), array('field = ? AND messageid = ?', array($key, $uid)) ); } catch (Horde_Db_Exception $e) {} } else { /* Insert */ try { $this->_db->insertBlob( self::MD_TABLE, array('data' => $val, 'field' => $key, 'messageid' => $uid) ); } catch (Horde_Db_Exception $e) {} } } } /** */ public function deleteMsgs($mailbox, $uids) { if (empty($uids)) { return; } $query = $this->_baseSql($mailbox); $query[0] = sprintf( 'DELETE FROM %s WHERE messageid IN (SELECT messageid ' . $query[0] . ')', self::MSG_TABLE ); $uid_query = array(); foreach ($uids as $val) { $uid_query[] = 'msguid = ?'; $query[1][] = strval($val); } $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')'; try { $this->_db->delete($query[0], $query[1]); } catch (Horde_Db_Exception $e) {} } /** */ public function deleteMailbox($mailbox) { if (is_null($uid = $this->_getUid($mailbox))) { return; } foreach (array(self::BASE_TABLE, self::MD_TABLE, self::MSG_TABLE) as $val) { try { $this->_db->delete( sprintf('DELETE FROM %s WHERE messageid = ?', $val), array($uid) ); } catch (Horde_Db_Exception $e) {} } } /** */ public function clear($lifetime) { if (is_null($lifetime)) { try { $this->_db->delete(sprintf('DELETE FROM %s', self::BASE_TABLE)); $this->_db->delete(sprintf('DELETE FROM %s', self::MD_TABLE)); $this->_db->delete(sprintf('DELETE FROM %s', self::MSG_TABLE)); } catch (Horde_Db_Exception $e) {} return; } $purge = time() - $lifetime; $sql = 'DELETE FROM %s WHERE messageid IN (SELECT messageid FROM %s WHERE modified < ?)'; foreach (array(self::MD_TABLE, self::MSG_TABLE) as $val) { try { $this->_db->delete( sprintf($sql, $val, self::BASE_TABLE), array($purge) ); } catch (Horde_Db_Exception $e) { } } try { $this->_db->delete( sprintf('DELETE FROM %s WHERE modified < ?', self::BASE_TABLE), array($purge) ); } catch (Horde_Db_Exception $e) { } } /** * Prepare the base SQL query. * * @param string $mailbox The mailbox. * @param string $join The table to join with the base table. * * @return array SQL query and bound parameters. */ protected function _baseSql($mailbox, $join = null) { $sql = sprintf('FROM %s d', self::BASE_TABLE); if (!is_null($join)) { $sql .= sprintf(' INNER JOIN %s t ON d.messageid = t.messageid', $join); } return array( $sql . ' WHERE d.hostspec = ? AND d.port = ? AND d.username = ? AND d.mailbox = ?', array( $this->_params['hostspec'], $this->_params['port'], $this->_params['username'], $mailbox ) ); } /** * @param string $mailbox * * @return string UID from base table. */ protected function _getUid($mailbox) { $query = $this->_baseSql($mailbox); $query[0] = 'SELECT d.messageid ' . $query[0]; try { return $this->_db->selectValue($query[0], $query[1]); } catch (Horde_Db_Exception $e) { return null; } } /** * @param string $mailbox * * @return string UID from base table. */ protected function _createUid($mailbox) { return $this->_db->insert( sprintf( 'INSERT INTO %s (hostspec, mailbox, port, username) ' . 'VALUES (?, ?, ?, ?)', self::BASE_TABLE ), array( $this->_params['hostspec'], $mailbox, $this->_params['port'], $this->_params['username'] ) ); } }