diff --git a/plugins/libkolab/SQL/mysql.initial.sql b/plugins/libkolab/SQL/mysql.initial.sql
index 0213c45b..80da02fa 100644
--- a/plugins/libkolab/SQL/mysql.initial.sql
+++ b/plugins/libkolab/SQL/mysql.initial.sql
@@ -33,7 +33,6 @@ CREATE TABLE `kolab_cache_contact` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
@@ -57,7 +56,6 @@ CREATE TABLE `kolab_cache_event` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
@@ -77,7 +75,6 @@ CREATE TABLE `kolab_cache_task` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
@@ -97,7 +94,6 @@ CREATE TABLE `kolab_cache_journal` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
@@ -117,7 +113,6 @@ CREATE TABLE `kolab_cache_note` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`)
@@ -135,7 +130,6 @@ CREATE TABLE `kolab_cache_file` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`filename` varchar(255) DEFAULT NULL,
@@ -155,7 +149,6 @@ CREATE TABLE `kolab_cache_configuration` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
@@ -175,7 +168,6 @@ CREATE TABLE `kolab_cache_freebusy` (
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
- `xml` LONGBLOB NOT NULL,
`tags` TEXT NOT NULL,
`words` TEXT NOT NULL,
`dtstart` DATETIME,
@@ -188,4 +180,4 @@ CREATE TABLE `kolab_cache_freebusy` (
/*!40014 SET FOREIGN_KEY_CHECKS=1 */;
-REPLACE INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2018021300');
+REPLACE INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2018122700');
diff --git a/plugins/libkolab/SQL/mysql/2018122700.sql b/plugins/libkolab/SQL/mysql/2018122700.sql
new file mode 100644
index 00000000..08030dd0
--- /dev/null
+++ b/plugins/libkolab/SQL/mysql/2018122700.sql
@@ -0,0 +1,10 @@
+-- remove xml column, and change data format (clear cache needed)
+DELETE FROM `kolab_folders`;
+ALTER TABLE `kolab_cache_contact` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_event` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_task` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_journal` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_note` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_file` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_configuration` DROP COLUMN `xml`;
+ALTER TABLE `kolab_cache_freebusy` DROP COLUMN `xml`;
diff --git a/plugins/libkolab/SQL/oracle.initial.sql b/plugins/libkolab/SQL/oracle.initial.sql
index 78675783..ebba60dc 100644
--- a/plugins/libkolab/SQL/oracle.initial.sql
+++ b/plugins/libkolab/SQL/oracle.initial.sql
@@ -37,7 +37,6 @@ CREATE TABLE "kolab_cache_contact" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"type" varchar(32) NOT NULL,
@@ -60,7 +59,6 @@ CREATE TABLE "kolab_cache_event" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"dtstart" timestamp DEFAULT NULL,
@@ -79,7 +77,6 @@ CREATE TABLE "kolab_cache_task" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"dtstart" timestamp DEFAULT NULL,
@@ -98,7 +95,6 @@ CREATE TABLE "kolab_cache_journal" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"dtstart" timestamp DEFAULT NULL,
@@ -117,7 +113,6 @@ CREATE TABLE "kolab_cache_note" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
PRIMARY KEY ("folder_id", "msguid")
@@ -134,7 +129,6 @@ CREATE TABLE "kolab_cache_file" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"filename" varchar(255) DEFAULT NULL,
@@ -153,7 +147,6 @@ CREATE TABLE "kolab_cache_configuration" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"type" varchar(32) NOT NULL,
@@ -172,7 +165,6 @@ CREATE TABLE "kolab_cache_freebusy" (
"created" timestamp DEFAULT NULL,
"changed" timestamp DEFAULT NULL,
"data" clob NOT NULL,
- "xml" clob NOT NULL,
"tags" clob DEFAULT NULL,
"words" clob DEFAULT NULL,
"dtstart" timestamp DEFAULT NULL,
@@ -183,4 +175,4 @@ CREATE TABLE "kolab_cache_freebusy" (
CREATE INDEX "kolab_cache_fb_uid2msguid" ON "kolab_cache_freebusy" ("folder_id", "uid", "msguid");
-INSERT INTO "system" ("name", "value") VALUES ('libkolab-version', '2018021300');
+INSERT INTO "system" ("name", "value") VALUES ('libkolab-version', '2018122700');
diff --git a/plugins/libkolab/SQL/oracle/2018122700.sql b/plugins/libkolab/SQL/oracle/2018122700.sql
new file mode 100644
index 00000000..45fccb74
--- /dev/null
+++ b/plugins/libkolab/SQL/oracle/2018122700.sql
@@ -0,0 +1,10 @@
+-- remove xml column, and change data format (clear cache needed)
+DELETE FROM "kolab_folders";
+ALTER TABLE "kolab_cache_contact" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_event" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_task" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_journal" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_note" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_file" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_configuration" DROP COLUMN "xml";
+ALTER TABLE "kolab_cache_freebusy" DROP COLUMN "xml";
diff --git a/plugins/libkolab/SQL/sqlite.initial.sql b/plugins/libkolab/SQL/sqlite.initial.sql
index 909188ca..815316dc 100644
--- a/plugins/libkolab/SQL/sqlite.initial.sql
+++ b/plugins/libkolab/SQL/sqlite.initial.sql
@@ -25,7 +25,6 @@ CREATE TABLE kolab_cache_contact (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
type VARCHAR(32) NOT NULL,
@@ -46,7 +45,6 @@ CREATE TABLE kolab_cache_event (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
dtstart DATETIME,
@@ -63,7 +61,6 @@ CREATE TABLE kolab_cache_task (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
dtstart DATETIME,
@@ -80,7 +77,6 @@ CREATE TABLE kolab_cache_journal (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
dtstart DATETIME,
@@ -97,7 +93,6 @@ CREATE TABLE kolab_cache_note (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
PRIMARY KEY(folder_id,msguid)
@@ -112,7 +107,6 @@ CREATE TABLE kolab_cache_file (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
filename varchar(255) DEFAULT NULL,
@@ -129,7 +123,6 @@ CREATE TABLE kolab_cache_configuration (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
type VARCHAR(32) NOT NULL,
@@ -146,7 +139,6 @@ CREATE TABLE kolab_cache_freebusy (
created DATETIME DEFAULT NULL,
changed DATETIME DEFAULT NULL,
data TEXT NOT NULL,
- xml TEXT NOT NULL,
tags TEXT NOT NULL,
words TEXT NOT NULL,
dtstart DATETIME,
@@ -156,4 +148,4 @@ CREATE TABLE kolab_cache_freebusy (
CREATE INDEX ix_freebusy_uid2msguid ON kolab_cache_freebusy(folder_id,uid,msguid);
-INSERT INTO system (name, value) VALUES ('libkolab-version', '2018021300');
+INSERT INTO system (name, value) VALUES ('libkolab-version', '2018122700');
diff --git a/plugins/libkolab/SQL/sqlite/2018122700.sql b/plugins/libkolab/SQL/sqlite/2018122700.sql
new file mode 100644
index 00000000..ee24c1a6
--- /dev/null
+++ b/plugins/libkolab/SQL/sqlite/2018122700.sql
@@ -0,0 +1,137 @@
+DROP TABLE kolab_cache_contact;
+CREATE TABLE kolab_cache_contact (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ type VARCHAR(32) NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ firstname VARCHAR(255) NOT NULL,
+ surname VARCHAR(255) NOT NULL,
+ email VARCHAR(255) NOT NULL,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_contact_type ON kolab_cache_contact(folder_id,type);
+CREATE INDEX ix_contact_uid2msguid ON kolab_cache_contact(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_event;
+CREATE TABLE kolab_cache_event (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ dtstart DATETIME,
+ dtend DATETIME,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_event_uid2msguid ON kolab_cache_event(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_task;
+CREATE TABLE kolab_cache_task (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ dtstart DATETIME,
+ dtend DATETIME,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_task_uid2msguid ON kolab_cache_task(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_journal;
+CREATE TABLE kolab_cache_journal (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ dtstart DATETIME,
+ dtend DATETIME,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_journal_uid2msguid ON kolab_cache_journal(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_note;
+CREATE TABLE kolab_cache_note (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_note_uid2msguid ON kolab_cache_note(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_file;
+CREATE TABLE kolab_cache_file (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ filename varchar(255) DEFAULT NULL,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_folder_filename ON kolab_cache_file(folder_id,filename);
+CREATE INDEX ix_file_uid2msguid ON kolab_cache_file(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_configuration;
+CREATE TABLE kolab_cache_configuration (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ type VARCHAR(32) NOT NULL,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_configuration_type ON kolab_cache_configuration(folder_id,type);
+CREATE INDEX ix_configuration_uid2msguid ON kolab_cache_configuration(folder_id,uid,msguid);
+
+DROP TABLE kolab_cache_freebusy;
+CREATE TABLE kolab_cache_freebusy (
+ folder_id INTEGER NOT NULL,
+ msguid INTEGER NOT NULL,
+ uid VARCHAR(512) NOT NULL,
+ created DATETIME DEFAULT NULL,
+ changed DATETIME DEFAULT NULL,
+ data TEXT NOT NULL,
+ tags TEXT NOT NULL,
+ words TEXT NOT NULL,
+ dtstart DATETIME,
+ dtend DATETIME,
+ PRIMARY KEY(folder_id,msguid)
+);
+
+CREATE INDEX ix_freebusy_uid2msguid ON kolab_cache_freebusy(folder_id,uid,msguid);
diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index df851d6d..a0d360ed 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -46,7 +46,6 @@ class kolab_storage_cache
protected $folders_table;
protected $max_sql_packet;
protected $max_sync_lock_time = 600;
- protected $binary_items = array();
protected $extra_cols = array();
protected $data_props = array();
protected $order_by = null;
@@ -202,7 +201,7 @@ class kolab_storage_cache
$this->metadata['changed'] < date(self::DB_DATE_FORMAT, time() - $this->cache_refresh) ||
$this->metadata['ctag'] != $this->folder->get_ctag() ||
intval($this->metadata['objectcount']) !== $this->count()
- ) {
+ ) {
// lock synchronization for this folder or wait if locked
$this->_sync_lock();
@@ -448,7 +447,7 @@ class kolab_storage_cache
$sql_data['uid'] = $object['uid'];
$args = array();
- $cols = array('folder_id', 'msguid', 'uid', 'changed', 'data', 'xml', 'tags', 'words');
+ $cols = array('folder_id', 'msguid', 'uid', 'changed', 'data', 'tags', 'words');
$cols = array_merge($cols, $this->extra_cols);
foreach ($cols as $idx => $col) {
@@ -857,55 +856,36 @@ class kolab_storage_cache
*/
protected function _serialize($object)
{
- $sql_data = array('changed' => null, 'xml' => '', 'tags' => '', 'words' => '');
+ $data = array();
+ $sql_data = array('changed' => null, 'tags' => '', 'words' => '');
if ($object['changed']) {
$sql_data['changed'] = date(self::DB_DATE_FORMAT, is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']);
}
if ($object['_formatobj']) {
- $xml = (string) $object['_formatobj']->write(3.0);
- $size = strlen($xml);
+ $xml = (string) $object['_formatobj']->write(3.0);
- $sql_data['xml'] = preg_replace('!(?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', $xml);
+ $data['_size'] = strlen($xml);
$sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search
$sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' ';
}
- // TODO: get rid of xml column
- // TODO: store only small subset of properties in data column, i.e. properties that are
- // needed for fast-mode only (use $data_props)
- // TODO: store data in JSON format and no base64-encoding
-
- // extract object data
- $data = array();
- foreach ($object as $key => $val) {
- // skip empty properties
- if ($val === "" || $val === null) {
- continue;
- }
- // mark binary data to be extracted from xml on unserialize()
- if (isset($this->binary_items[$key])) {
- $data[$key] = true;
- }
- else if ($key[0] != '_') {
- $data[$key] = $val;
- }
- else if ($key == '_attachments') {
- foreach ($val as $k => $att) {
- unset($att['content'], $att['path']);
- if ($att['id'])
- $data[$key][$k] = $att;
+ // Store only minimal set of object properties
+ foreach ($this->data_props as $prop) {
+ if (isset($object[$prop])) {
+ $data[$prop] = $object[$prop];
+ if ($data[$prop] instanceof DateTime) {
+ $data[$prop] = array(
+ 'cl' => 'DateTime',
+ 'dt' => $data[$prop]->format('Y-m-d H:i:s'),
+ 'tz' => $data[$prop]->getTimezone()->getName(),
+ );
}
}
}
- if ($size) {
- $data['_size'] = $size;
- }
-
- // use base64 encoding (Bug #1912, #2662)
- $sql_data['data'] = base64_encode(serialize($data));
+ $sql_data['data'] = json_encode(rcube_charset::clean($data));
return $sql_data;
}
@@ -915,13 +895,14 @@ class kolab_storage_cache
*/
protected function _unserialize($sql_arr)
{
- if ($sql_arr['fast-mode'] && !empty($sql_arr['data'])
- && ($object = unserialize(base64_decode($sql_arr['data'])))
- ) {
+ if ($sql_arr['fast-mode'] && !empty($sql_arr['data']) && ($object = json_decode($sql_arr['data'], true))) {
$object['uid'] = $sql_arr['uid'];
foreach ($this->data_props as $prop) {
- if (!isset($object[$prop]) && isset($sql_arr[$prop])) {
+ if (isset($object[$prop]) && is_array($object[$prop]) && $object[$prop]['cl'] == 'DateTime') {
+ $object[$prop] = new DateTime($object[$prop]['dt'], new DateTimeZone($object[$prop]['tz']));
+ }
+ else if (!isset($object[$prop]) && isset($sql_arr[$prop])) {
$object[$prop] = $sql_arr[$prop];
}
}
@@ -939,9 +920,8 @@ class kolab_storage_cache
$object['_mailbox'] = $this->folder->name;
}
// Fetch object xml
- else if ($object = $this->folder->read_object($sql_arr['msguid'])) {
- // additional meta data
- $object['_size'] = strlen($xml);
+ else {
+ $object = $this->folder->read_object($sql_arr['msguid']);
}
return $object;
@@ -971,7 +951,7 @@ class kolab_storage_cache
}
$params = array($this->folder_id, $msguid, $object['uid'], $sql_data['changed'],
- $sql_data['data'], $sql_data['xml'], $sql_data['tags'], $sql_data['words']);
+ $sql_data['data'], $sql_data['tags'], $sql_data['words']);
foreach ($this->extra_cols as $col) {
$params[] = $sql_data[$col];
@@ -979,8 +959,8 @@ class kolab_storage_cache
$result = $this->db->query(
"INSERT INTO `{$this->cache_table}` "
- . " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `xml`, `tags`, `words` $extra_cols)"
- . " VALUES (?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ? $extra_args)",
+ . " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `tags`, `words`$extra_cols)"
+ . " VALUES (?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ? $extra_args)",
$params
);
@@ -1001,7 +981,6 @@ class kolab_storage_cache
$this->db->now(),
$this->db->quote($sql_data['changed']),
$this->db->quote($sql_data['data']),
- $this->db->quote($sql_data['xml']),
$this->db->quote($sql_data['tags']),
$this->db->quote($sql_data['words']),
);
@@ -1020,7 +999,7 @@ class kolab_storage_cache
$result = $this->db->query(
"INSERT INTO `{$this->cache_table}` ".
- " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `xml`, `tags`, `words` $extra_cols)".
+ " (`folder_id`, `msguid`, `uid`, `created`, `changed`, `data`, `tags`, `words`$extra_cols)".
" VALUES $buffer"
);
diff --git a/plugins/libkolab/lib/kolab_storage_cache_contact.php b/plugins/libkolab/lib/kolab_storage_cache_contact.php
index 5dfb16b7..22d0a330 100644
--- a/plugins/libkolab/lib/kolab_storage_cache_contact.php
+++ b/plugins/libkolab/lib/kolab_storage_cache_contact.php
@@ -26,11 +26,6 @@ class kolab_storage_cache_contact extends kolab_storage_cache
protected $extra_cols_max = 255;
protected $extra_cols = array('type', 'name', 'firstname', 'surname', 'email');
protected $data_props = array('type', 'name', 'firstname', 'middlename', 'prefix', 'suffix', 'surname', 'email', 'organization', 'member');
- protected $binary_items = array(
- 'photo' => '|[^;]+;base64,([^<]+)|i',
- 'pgppublickey' => '|data:application/pgp-keys;base64,([^<]+)|i',
- 'pkcs7publickey' => '|data:application/pkcs7-mime;base64,([^<]+)|i',
- );
/**
* Helper method to convert the given Kolab object into a dataset to be written to cache
diff --git a/plugins/libkolab/lib/kolab_storage_cache_mongodb.php b/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
deleted file mode 100644
index 8ae95e4b..00000000
--- a/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
+++ /dev/null
@@ -1,561 +0,0 @@
-
- *
- * Copyright (C) 2012, Kolab Systems AG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-class kolab_storage_cache_mongodb
-{
- private $db;
- private $imap;
- private $folder;
- private $uid2msg;
- private $objects;
- private $index = array();
- private $resource_uri;
- private $enabled = true;
- private $synched = false;
- private $synclock = false;
- private $ready = false;
- private $max_sql_packet = 1046576; // 1 MB - 2000 bytes
- private $binary_cols = array('photo','pgppublickey','pkcs7publickey');
-
-
- /**
- * Default constructor
- */
- public function __construct(kolab_storage_folder $storage_folder = null)
- {
- $rcmail = rcube::get_instance();
- $mongo = new Mongo();
- $this->db = $mongo->kolab_cache;
- $this->imap = $rcmail->get_storage();
- $this->enabled = $rcmail->config->get('kolab_cache', false);
-
- if ($this->enabled) {
- // remove sync-lock on script termination
- $rcmail->add_shutdown_function(array($this, '_sync_unlock'));
- }
-
- if ($storage_folder)
- $this->set_folder($storage_folder);
- }
-
-
- /**
- * Connect cache with a storage folder
- *
- * @param kolab_storage_folder The storage folder instance to connect with
- */
- public function set_folder(kolab_storage_folder $storage_folder)
- {
- $this->folder = $storage_folder;
-
- if (empty($this->folder->name)) {
- $this->ready = false;
- return;
- }
-
- // compose fully qualified ressource uri for this instance
- $this->resource_uri = $this->folder->get_resource_uri();
- $this->ready = $this->enabled;
- }
-
-
- /**
- * Synchronize local cache data with remote
- */
- public function synchronize()
- {
- // only sync once per request cycle
- if ($this->synched)
- return;
-
- // increase time limit
- @set_time_limit(500);
-
- // lock synchronization for this folder or wait if locked
- $this->_sync_lock();
-
- // synchronize IMAP mailbox cache
- $this->imap->folder_sync($this->folder->name);
-
- // compare IMAP index with object cache index
- $imap_index = $this->imap->index($this->folder->name);
- $this->index = $imap_index->get();
-
- // determine objects to fetch or to invalidate
- if ($this->ready) {
- // read cache index
- $old_index = array();
- $cursor = $this->db->cache->find(array('resource' => $this->resource_uri), array('msguid' => 1, 'uid' => 1));
- foreach ($cursor as $doc) {
- $old_index[] = $doc['msguid'];
- $this->uid2msg[$doc['uid']] = $doc['msguid'];
- }
-
- // fetch new objects from imap
- foreach (array_diff($this->index, $old_index) as $msguid) {
- if ($object = $this->folder->read_object($msguid, '*')) {
- try {
- $this->db->cache->insert($this->_serialize($object, $msguid));
- }
- catch (Exception $e) {
- rcmail::raise_error(array(
- 'code' => 900, 'type' => 'php',
- 'message' => "Failed to write to mongodb cache: " . $e->getMessage(),
- ), true);
- }
- }
- }
-
- // delete invalid entries from local DB
- $del_index = array_diff($old_index, $this->index);
- if (!empty($del_index)) {
- $this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => array('$in' => $del_index)));
- }
- }
-
- // remove lock
- $this->_sync_unlock();
-
- $this->synched = time();
- }
-
-
- /**
- * Read a single entry from cache or from IMAP directly
- *
- * @param string Related IMAP message UID
- * @param string Object type to read
- * @param string IMAP folder name the entry relates to
- * @param array Hash array with object properties or null if not found
- */
- public function get($msguid, $type = null, $foldername = null)
- {
- // delegate to another cache instance
- if ($foldername && $foldername != $this->folder->name) {
- return kolab_storage::get_folder($foldername)->cache->get($msguid, $object);
- }
-
- // load object if not in memory
- if (!isset($this->objects[$msguid])) {
- if ($this->ready && ($doc = $this->db->cache->findOne(array('resource' => $this->resource_uri, 'msguid' => $msguid))))
- $this->objects[$msguid] = $this->_unserialize($doc);
-
- // fetch from IMAP if not present in cache
- if (empty($this->objects[$msguid])) {
- $result = $this->_fetch(array($msguid), $type, $foldername);
- $this->objects[$msguid] = $result[0];
- }
- }
-
- return $this->objects[$msguid];
- }
-
-
- /**
- * Insert/Update a cache entry
- *
- * @param string Related IMAP message UID
- * @param mixed Hash array with object properties to save or false to delete the cache entry
- * @param string IMAP folder name the entry relates to
- */
- public function set($msguid, $object, $foldername = null)
- {
- // delegate to another cache instance
- if ($foldername && $foldername != $this->folder->name) {
- kolab_storage::get_folder($foldername)->cache->set($msguid, $object);
- return;
- }
-
- // write to cache
- if ($this->ready) {
- // remove old entry
- $this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => $msguid));
-
- // write new object data if not false (wich means deleted)
- if ($object) {
- try {
- $this->db->cache->insert($this->_serialize($object, $msguid));
- }
- catch (Exception $e) {
- rcmail::raise_error(array(
- 'code' => 900, 'type' => 'php',
- 'message' => "Failed to write to mongodb cache: " . $e->getMessage(),
- ), true);
- }
- }
- }
-
- // keep a copy in memory for fast access
- $this->objects[$msguid] = $object;
-
- if ($object)
- $this->uid2msg[$object['uid']] = $msguid;
- }
-
- /**
- * Move an existing cache entry to a new resource
- *
- * @param string Entry's IMAP message UID
- * @param string Entry's Object UID
- * @param string Target IMAP folder to move it to
- */
- public function move($msguid, $objuid, $target_folder)
- {
- $target = kolab_storage::get_folder($target_folder);
-
- // resolve new message UID in target folder
- if ($new_msguid = $target->cache->uid2msguid($objuid)) {
-/*
- $this->db->query(
- "UPDATE kolab_cache SET resource=?, msguid=? ".
- "WHERE resource=? AND msguid=? AND type<>?",
- $target->get_resource_uri(),
- $new_msguid,
- $this->resource_uri,
- $msguid,
- 'lock'
- );
-*/
- }
- else {
- // just clear cache entry
- $this->set($msguid, false);
- }
-
- unset($this->uid2msg[$uid]);
- }
-
-
- /**
- * Remove all objects from local cache
- */
- public function purge($type = null)
- {
- return $this->db->cache->remove(array(), array('safe' => true));
- }
-
-
- /**
- * Select Kolab objects filtered by the given query
- *
- * @param array Pseudo-SQL query as list of filter parameter triplets
- * triplet: array('', '', '')
- * @return array List of Kolab data objects (each represented as hash array)
- */
- public function select($query = array())
- {
- $result = array();
-
- // read from local cache DB (assume it to be synchronized)
- if ($this->ready) {
- $cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query));
- foreach ($cursor as $doc) {
- if ($object = $this->_unserialize($doc))
- $result[] = $object;
- }
- }
- else {
- // extract object type from query parameter
- $filter = $this->_query2assoc($query);
-
- // use 'list' for folder's default objects
- if ($filter['type'] == $this->type) {
- $index = $this->index;
- }
- else { // search by object type
- $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
- $index = $this->imap->search_once($this->folder->name, $search)->get();
- }
-
- // fetch all messages in $index from IMAP
- $result = $this->_fetch($index, $filter['type']);
-
- // TODO: post-filter result according to query
- }
-
- return $result;
- }
-
-
- /**
- * Get number of objects mathing the given query
- *
- * @param array $query Pseudo-SQL query as list of filter parameter triplets
- * @return integer The number of objects of the given type
- */
- public function count($query = array())
- {
- $count = 0;
-
- // cache is in sync, we can count records in local DB
- if ($this->synched) {
- $cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query));
- $count = $cursor->valid() ? $cursor->count() : 0;
- }
- else {
- // search IMAP by object type
- $filter = $this->_query2assoc($query);
- $ctype = kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
- $index = $this->imap->search_once($this->folder->name, 'UNDELETED HEADER X-Kolab-Type ' . $ctype);
- $count = $index->count();
- }
-
- return $count;
- }
-
- /**
- * Helper method to convert the pseudo-SQL query into a valid mongodb filter
- */
- private function _mongo_filter($query)
- {
- $filters = array();
- foreach ($query as $param) {
- $filter = array();
- if ($param[1] == '=' && is_array($param[2])) {
- $filter[$param[0]] = array('$in' => $param[2]);
- $filters[] = $filter;
- }
- else if ($param[1] == '=') {
- $filters[] = array($param[0] => $param[2]);
- }
- else if ($param[1] == 'LIKE' || $param[1] == '~') {
- $filter[$param[0]] = array('$regex' => preg_quote($param[2]), '$options' => 'i');
- $filters[] = $filter;
- }
- else if ($param[1] == '!~' || $param[1] == '!LIKE') {
- $filter[$param[0]] = array('$not' => '/' . preg_quote($param[2]) . '/i');
- $filters[] = $filter;
- }
- else {
- $op = '';
- switch ($param[1]) {
- case '>': $op = '$gt'; break;
- case '>=': $op = '$gte'; break;
- case '<': $op = '$lt'; break;
- case '<=': $op = '$lte'; break;
- case '!=':
- case '<>': $op = '$gte'; break;
- }
- if ($op) {
- $filter[$param[0]] = array($op => $param[2]);
- $filters[] = $filter;
- }
- }
- }
-
- return array('$and' => $filters);
- }
-
- /**
- * Helper method to convert the given pseudo-query triplets into
- * an associative filter array with 'equals' values only
- */
- private function _query2assoc($query)
- {
- // extract object type from query parameter
- $filter = array();
- foreach ($query as $param) {
- if ($param[1] == '=')
- $filter[$param[0]] = $param[2];
- }
- return $filter;
- }
-
- /**
- * Fetch messages from IMAP
- *
- * @param array List of message UIDs to fetch
- * @return array List of parsed Kolab objects
- */
- private function _fetch($index, $type = null, $folder = null)
- {
- $results = array();
- foreach ((array)$index as $msguid) {
- if ($object = $this->folder->read_object($msguid, $type, $folder)) {
- $results[] = $object;
- $this->set($msguid, $object);
- }
- }
-
- return $results;
- }
-
-
- /**
- * Helper method to convert the given Kolab object into a dataset to be written to cache
- */
- private function _serialize($object, $msguid)
- {
- $bincols = array_flip($this->binary_cols);
- $doc = array(
- 'resource' => $this->resource_uri,
- 'type' => $object['_type'] ? $object['_type'] : $this->folder->type,
- 'msguid' => $msguid,
- 'uid' => $object['uid'],
- 'xml' => '',
- 'tags' => array(),
- 'words' => array(),
- 'objcols' => array(),
- );
-
- // set type specific values
- if ($this->folder->type == 'event') {
- // database runs in server's timezone so using date() is what we want
- $doc['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
- $doc['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
-
- // extend date range for recurring events
- if ($object['recurrence']) {
- $doc['dtend'] = date('Y-m-d H:i:s', $object['recurrence']['UNTIL'] ?: strtotime('now + 2 years'));
- }
- }
-
- if ($object['_formatobj']) {
- $doc['xml'] = preg_replace('!(?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write());
- $doc['tags'] = $object['_formatobj']->get_tags();
- $doc['words'] = $object['_formatobj']->get_words();
- }
-
- // extract object data
- $data = array();
- foreach ($object as $key => $val) {
- if ($val === "" || $val === null) {
- // skip empty properties
- continue;
- }
- if (isset($bincols[$key])) {
- $data[$key] = base64_encode($val);
- }
- else if (is_object($val)) {
- if (is_a($val, 'DateTime')) {
- $data[$key] = array('_class' => 'DateTime', 'date' => $val->format('Y-m-d H:i:s'), 'timezone' => $val->getTimezone()->getName());
- $doc['objcols'][] = $key;
- }
- }
- else if ($key[0] != '_') {
- $data[$key] = $val;
- }
- else if ($key == '_attachments') {
- foreach ($val as $k => $att) {
- unset($att['content'], $att['path']);
- if ($att['id'])
- $data[$key][$k] = $att;
- }
- }
- }
-
- $doc['data'] = $data;
- return $doc;
- }
-
- /**
- * Helper method to turn stored cache data into a valid storage object
- */
- private function _unserialize($doc)
- {
- $object = $doc['data'];
-
- // decode binary properties
- foreach ($this->binary_cols as $key) {
- if (!empty($object[$key]))
- $object[$key] = base64_decode($object[$key]);
- }
-
- // restore serialized objects
- foreach ((array)$doc['objcols'] as $key) {
- switch ($object[$key]['_class']) {
- case 'DateTime':
- $val = new DateTime($object[$key]['date'], new DateTimeZone($object[$key]['timezone']));
- $object[$key] = $val;
- break;
- }
- }
-
- // add meta data
- $object['_type'] = $doc['type'];
- $object['_msguid'] = $doc['msguid'];
- $object['_mailbox'] = $this->folder->name;
- $object['_formatobj'] = kolab_format::factory($doc['type'], $doc['xml']);
-
- return $object;
- }
-
- /**
- * Check lock record for this folder and wait if locked or set lock
- */
- private function _sync_lock()
- {
- if (!$this->ready)
- return;
-
- $this->synclock = true;
- $lock = $this->db->locks->findOne(array('resource' => $this->resource_uri));
-
- // create lock record if not exists
- if (!$lock) {
- $this->db->locks->insert(array('resource' => $this->resource_uri, 'created' => time()));
- }
- // wait if locked (expire locks after 10 minutes)
- else if ((time() - $lock['created']) < 600) {
- usleep(500000);
- return $this->_sync_lock();
- }
- // set lock
- else {
- $lock['created'] = time();
- $this->db->locks->update(array('_id' => $lock['_id']), $lock, array('safe' => true));
- }
- }
-
- /**
- * Remove lock for this folder
- */
- public function _sync_unlock()
- {
- if (!$this->ready || !$this->synclock)
- return;
-
- $this->db->locks->remove(array('resource' => $this->resource_uri));
- }
-
- /**
- * Resolve an object UID into an IMAP message UID
- *
- * @param string Kolab object UID
- * @param boolean Include deleted objects
- * @return int The resolved IMAP message UID
- */
- public function uid2msguid($uid, $deleted = false)
- {
- if (!isset($this->uid2msg[$uid])) {
- // use IMAP SEARCH to get the right message
- $index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT ' . $uid);
- $results = $index->get();
- $this->uid2msg[$uid] = $results[0];
- }
-
- return $this->uid2msg[$uid];
- }
-
-}
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 1ffbb457..d0695cb2 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -562,6 +562,7 @@ class kolab_storage_folder extends kolab_storage_folder_api
$object['_msguid'] = $msguid;
$object['_mailbox'] = $this->name;
$object['_formatobj'] = $format;
+ $object['_size'] = strlen($xml);
return $object;
}