/** * Object constructor. * * @param string $type Engine type ('db' or 'memcache' or 'apc') * @param int $userid User identifier * @param string $prefix Key name prefix * @param string $ttl Expiration time of memcache/apc items * @param bool $packed Enables/disabled data serialization. * It's possible to disable data serialization if you're sure * stored data will be always a safe string */ function __construct($type, $userid, $prefix = '', $ttl = 0, $packed = true) { $rcube = rcube::get_instance(); $type = strtolower($type); if ($type == 'memcache') { $this->type = 'memcache'; $this->db = $rcube->get_memcache(); } else { if ($type == 'apc') { $this->type = 'apc'; $this->db = function_exists('apc_exists'); // APC 3.1.4 required } else { $this->type = 'db'; $this->db = $rcube->get_dbh(); $this->table = $this->db->table_name('cache'); } } // convert ttl string to seconds $ttl = get_offset_sec($ttl); if ($ttl > 2592000) { $ttl = 2592000; } $this->userid = (int) $userid; $this->ttl = $ttl; $this->packed = $packed; $this->prefix = $prefix; }
/** * Check for existing groups with the same name * * @param string Name to check * @return string A group name which is unique for the current use */ private function unique_groupname($name) { $checkname = $name; $num = 2; $hit = false; do { $sql_result = $this->db->query("SELECT 1 FROM " . $this->db->table_name($this->db_groups) . " WHERE del<>1" . " AND user_id=?" . " AND name=?", $this->user_id, $checkname); // append number to make name unique if ($hit = $this->db->fetch_array($sql_result)) { $checkname = $name . ' ' . $num++; } } while ($hit); return $checkname; }
/** * Create a new saved search record linked with this user * * @param array $data Hash array with col->value pairs to save * * @return int The inserted search ID or false on error */ function insert_search($data) { if (!$this->ID) { return false; } $insert_cols[] = 'user_id'; $insert_values[] = (int) $this->ID; $insert_cols[] = $this->db->quote_identifier('type'); $insert_values[] = (int) $data['type']; $insert_cols[] = $this->db->quote_identifier('name'); $insert_values[] = $data['name']; $insert_cols[] = $this->db->quote_identifier('data'); $insert_values[] = serialize($data['data']); $sql = "INSERT INTO " . $this->db->table_name('searches') . " (" . join(', ', $insert_cols) . ")" . " VALUES (" . join(', ', array_pad(array(), sizeof($insert_values), '?')) . ")"; call_user_func_array(array($this->db, 'query'), array_merge(array($sql), $insert_values)); return $this->db->insert_id('searches'); }
/** * Deletes the cache record(s). * * @param string $key Cache key name or pattern * @param boolean $prefix_mode Enable it to clear all keys starting * with prefix specified in $key */ private function remove_record($key = null, $prefix_mode = false) { if (!$this->db) { return; } if ($this->type != 'db') { $this->load_index(); // Remove all keys if ($key === null) { foreach ($this->index as $key) { $this->delete_record($key, false); } $this->index = array(); } else { if ($prefix_mode) { foreach ($this->index as $k) { if (strpos($k, $key) === 0) { $this->delete_record($k); } } } else { $this->delete_record($key); } } return; } // Remove all keys (in specified cache) if ($key === null) { $where = " AND cache_key LIKE " . $this->db->quote($this->prefix . '.%'); } else { if ($prefix_mode) { $where = " AND cache_key LIKE " . $this->db->quote($this->prefix . '.' . $key . '%'); } else { $where = " AND cache_key = " . $this->db->quote($this->prefix . '.' . $key); } } $this->db->query("DELETE FROM " . $this->db->table_name('cache') . " WHERE user_id = ?" . $where, $this->userid); }
/** * Object constructor. * * @param rcube_db $db DB handler * @param rcube_imap $imap IMAP handler * @param int $userid User identifier * @param bool $skip_deleted skip_deleted flag * @param string $ttl Expiration time of memcache/apc items * @param int $threshold Maximum cached message size */ function __construct($db, $imap, $userid, $skip_deleted, $ttl = 0, $threshold = 0) { // convert ttl string to seconds $ttl = get_offset_sec($ttl); if ($ttl > 2592000) { $ttl = 2592000; } $this->db = $db; $this->imap = $imap; $this->userid = $userid; $this->skip_deleted = $skip_deleted; $this->ttl = $ttl; $this->threshold = $threshold; // cache all possible information by default $this->mode = self::MODE_INDEX | self::MODE_MESSAGE; // database tables $this->index_table = $db->table_name('cache_index', true); $this->thread_table = $db->table_name('cache_thread', true); $this->messages_table = $db->table_name('cache_messages', true); }
/** * Synchronizes the mailbox. * * @param string $mailbox Folder name */ function synchronize($mailbox) { // RFC4549: Synchronization Operations for Disconnected IMAP4 Clients // RFC4551: IMAP Extension for Conditional STORE Operation // or Quick Flag Changes Resynchronization // RFC5162: IMAP Extensions for Quick Mailbox Resynchronization // @TODO: synchronize with other methods? $qresync = $this->imap->get_capability('QRESYNC'); $condstore = $qresync ? true : $this->imap->get_capability('CONDSTORE'); if (!$qresync && !$condstore) { return; } // Get stored index $index = $this->get_index_row($mailbox); // database is empty if (empty($index)) { // set the flag that DB was already queried for index // this way we'll be able to skip one SELECT in get_index() $this->icache[$mailbox]['index_queried'] = true; return; } $this->icache[$mailbox]['index'] = $index; // no last HIGHESTMODSEQ value if (empty($index['modseq'])) { return; } if (!$this->imap->check_connection()) { return; } // Enable QRESYNC $res = $this->imap->conn->enable($qresync ? 'QRESYNC' : 'CONDSTORE'); if ($res === false) { return; } // Close mailbox if already selected to get most recent data if ($this->imap->conn->selected == $mailbox) { $this->imap->conn->close(); } // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.) $mbox_data = $this->imap->folder_data($mailbox); if (empty($mbox_data)) { return; } // Check UIDVALIDITY if ($index['validity'] != $mbox_data['UIDVALIDITY']) { $this->clear($mailbox); return; } // QRESYNC not supported on specified mailbox if (!empty($mbox_data['NOMODSEQ']) || empty($mbox_data['HIGHESTMODSEQ'])) { return; } // Nothing new if ($mbox_data['HIGHESTMODSEQ'] == $index['modseq']) { return; } $uids = array(); $removed = array(); // Get known UIDs if ($this->mode & self::MODE_MESSAGE) { $sql_result = $this->db->query("SELECT uid" . " FROM " . $this->db->table_name('cache_messages') . " WHERE user_id = ?" . " AND mailbox = ?", $this->userid, $mailbox); while ($sql_arr = $this->db->fetch_assoc($sql_result)) { $uids[] = $sql_arr['uid']; } } // Synchronize messages data if (!empty($uids)) { // Get modified flags and vanished messages // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED) $result = $this->imap->conn->fetch($mailbox, $uids, true, array('FLAGS'), $index['modseq'], $qresync); if (!empty($result)) { foreach ($result as $msg) { $uid = $msg->uid; // Remove deleted message if ($this->skip_deleted && !empty($msg->flags['DELETED'])) { $removed[] = $uid; // Invalidate index $index['valid'] = false; continue; } $flags = 0; if (!empty($msg->flags)) { foreach ($this->flags as $idx => $flag) { if (!empty($msg->flags[$flag])) { $flags += $idx; } } } $this->db->query("UPDATE " . $this->db->table_name('cache_messages') . " SET flags = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . " WHERE user_id = ?" . " AND mailbox = ?" . " AND uid = ?" . " AND flags <> ?", $flags, $this->userid, $mailbox, $uid, $flags); } } // VANISHED found? if ($qresync) { $mbox_data = $this->imap->folder_data($mailbox); // Removed messages found $uids = rcube_imap_generic::uncompressMessageSet($mbox_data['VANISHED']); if (!empty($uids)) { $removed = array_merge($removed, $uids); // Invalidate index $index['valid'] = false; } } // remove messages from database if (!empty($removed)) { $this->remove_message($mailbox, $removed); } } $sort_field = $index['sort_field']; $sort_order = $index['object']->get_parameters('ORDER'); $exists = true; // Validate index if (!$this->validate($mailbox, $index, $exists)) { // Invalidate (remove) thread index // if $exists=false it was already removed in validate() if ($exists) { $this->remove_thread($mailbox); } // Update index $data = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data); } else { $data = $index['object']; } // update index and/or HIGHESTMODSEQ value $this->add_index_row($mailbox, $sort_field, $data, $mbox_data, $exists); // update internal cache for get_index() $this->icache[$mailbox]['index']['object'] = $data; }