summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO12
-rw-r--r--ECDBDef.h429
-rw-r--r--ECDatabaseMySQL.cpp2031
-rw-r--r--ECDatabaseUpdate.cpp2719
-rw-r--r--ECDatabaseUpdate.h109
-rw-r--r--PKGBUILD16
6 files changed, 5312 insertions, 4 deletions
diff --git a/.SRCINFO b/.SRCINFO
index f34f215e1e08..d00218e2403c 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,9 +1,9 @@
# Generated by mksrcinfo v8
-# Tue Dec 20 15:11:06 UTC 2016
+# Tue Dec 20 21:57:46 UTC 2016
pkgbase = zarafa-server
pkgdesc = Open Source Groupware Solution
pkgver = 7.2.4.29
- pkgrel = 112
+ pkgrel = 113
url = http://www.zarafa.com/
install = install
arch = armv7h
@@ -94,11 +94,19 @@ pkgbase = zarafa-server
source = python-zarafa::git+https://github.com/zarafagroupware/python-zarafa.git
source = zarafa-inspector::git+https://github.com/zarafagroupware/zarafa-inspector.git
source = zarafa-pietma::git+https://git.pietma.com/pietma/com-pietma-zarafa.git#tag=v0.13
+ source = ECDBDef.h
+ source = ECDatabaseMySQL.cpp
+ source = ECDatabaseUpdate.h
+ source = ECDatabaseUpdate.cpp
md5sums = 0790d8314fa4aef9788e5020be832535
md5sums = SKIP
md5sums = SKIP
md5sums = SKIP
md5sums = SKIP
+ md5sums = SKIP
+ md5sums = SKIP
+ md5sums = SKIP
+ md5sums = SKIP
pkgname = zarafa-server
diff --git a/ECDBDef.h b/ECDBDef.h
new file mode 100644
index 000000000000..f7c499edf7eb
--- /dev/null
+++ b/ECDBDef.h
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2005 - 2016 Zarafa and its licensors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ECDBDEF_H
+#define ECDBDEF_H
+
+/**
+ * @page kopano_db Database
+ *
+ * @section kopano_db_layout Database layout
+ *
+ * Server tables:
+ * @code
+ * abchanges | All addressbook changes
+ * acl | User permission objects
+ * changes | Object changes
+ * clientupdatestatus| Update status of the kopano client, only used with auto updater
+ * hierarchy | The hiearchy between the mapi objects
+ * indexedproperties | Mapi object entryid and sourcekey
+ * lob | Attachment data. Only when the setting attachment in database is enabled
+ * mvproperties | The multi value properties of a mapi object. Store, folder and message
+ * names | Custom property defines
+ * outgoingqueue | Pending messages to send
+ * properties | The single value properties of a mapi object. Store, folder and message
+ * receivefolder | Specifies the mapi receivefolder, for example the inbox
+ * searchresults | Search folder results
+ * settings | Server dependent settings
+ * singleinstances | The relation between an attachment and one or more message objects
+ * stores | A list with data stores related to one user and includes the deleted stores.
+ * syncedmessages | Messages which are synced with a specific restriction
+ * syncs | Sync state of a folder
+ * users | User relation between the userplugin and kopano
+ * versions | Database update information
+ * @endcode
+ *
+ * Database and unix user plugin tables:
+ * @code
+ * object | Unique user object id and user type
+ * objectmvproperty | Multi value properties of a user
+ * objectproperty | Single value properties of a user
+ * objectrelation | User, group, company and sendas relations
+ * @endcode
+ *
+ * @todo Add an image of the database layout
+ *
+ * @section kopano_db_update Database update system
+ *
+ * @todo describe the update system
+ *
+ *
+ */
+
+#define Z_TABLEDEF_ACL "CREATE TABLE `acl` ( \
+ `id` int(11) NOT NULL default '0', \
+ `hierarchy_id` int(11) unsigned NOT NULL default '0', \
+ `type` tinyint(4) unsigned NOT NULL default '0', \
+ `rights` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`hierarchy_id`,`id`,`type`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_HIERARCHY "CREATE TABLE `hierarchy` ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `parent` int(11) unsigned default '0', \
+ `type` tinyint(4) unsigned NOT NULL default '0', \
+ `flags` smallint(6) unsigned NOT NULL default '0', \
+ `owner` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`id`), \
+ KEY `parenttypeflags` (`parent`, `type`, `flags`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_NAMES "CREATE TABLE `names` ( \
+ `id` int(11) NOT NULL auto_increment, \
+ `nameid` int(11) default NULL, \
+ `namestring` varchar(255) binary default NULL, \
+ `guid` blob NOT NULL, \
+ PRIMARY KEY (`id`), \
+ KEY `nameid` (`nameid`), \
+ KEY `namestring` (`namestring`), \
+ KEY `guidnameid` (`guid`(16),`nameid`), \
+ KEY `guidnamestring` (`guid`(16),`namestring`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_MVPROPERTIES "CREATE TABLE `mvproperties` ( \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `orderid` smallint(6) unsigned NOT NULL default '0', \
+ `tag` smallint(6) unsigned NOT NULL default '0', \
+ `type` smallint(6) unsigned NOT NULL default '0', \
+ `val_ulong` int(11) unsigned default NULL, \
+ `val_string` longtext, \
+ `val_binary` longblob, \
+ `val_double` double default NULL, \
+ `val_longint` bigint(20) default NULL, \
+ `val_hi` int(11) default NULL, \
+ `val_lo` int(11) unsigned default NULL, \
+ PRIMARY KEY (`hierarchyid`, `tag`, `type`, `orderid`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_TPROPERTIES "CREATE TABLE `tproperties` ( \
+ `folderid` int(11) unsigned NOT NULL default '0', \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `tag` smallint(6) unsigned NOT NULL default '0', \
+ `type` smallint(6) unsigned NOT NULL, \
+ `val_ulong` int(11) unsigned default NULL, \
+ `val_string` longtext, \
+ `val_binary` longblob, \
+ `val_double` double default NULL, \
+ `val_longint` bigint(20) default NULL, \
+ `val_hi` int(11) default NULL, \
+ `val_lo` int(11) unsigned default NULL, \
+ PRIMARY KEY `ht` (`folderid`,`tag`,`hierarchyid`,`type`), \
+ KEY `hi` (`hierarchyid`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_DELAYEDUPDATE "CREATE TABLE `deferredupdate` (\
+ `hierarchyid` int(11) unsigned NOT NULL, \
+ `folderid` int(11) unsigned NOT NULL, \
+ `srcfolderid` int(11) unsigned, \
+ PRIMARY KEY(`hierarchyid`), \
+ KEY `folderid` (`folderid`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_PROPERTIES "CREATE TABLE `properties` ( \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `tag` smallint(6) unsigned NOT NULL default '0', \
+ `type` smallint(6) unsigned NOT NULL, \
+ `val_ulong` int(11) unsigned default NULL, \
+ `val_string` longtext, \
+ `val_binary` longblob, \
+ `val_double` double default NULL, \
+ `val_longint` bigint(20) default NULL, \
+ `val_hi` int(11) default NULL, \
+ `val_lo` int(11) unsigned default NULL, \
+ `comp` bool default false, \
+ PRIMARY KEY `ht` (`hierarchyid`,`tag`,`type`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_RECEIVEFOLDER "CREATE TABLE `receivefolder` ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `storeid` int(11) unsigned NOT NULL default '0', \
+ `objid` int(11) unsigned NOT NULL default '0', \
+ `messageclass` varchar(255) NOT NULL default '', \
+ PRIMARY KEY (`id`), \
+ UNIQUE KEY `storeid` (`storeid`,`messageclass`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_STORES "CREATE TABLE `stores` ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `hierarchy_id` int(11) unsigned NOT NULL default '0', \
+ `user_id` int(11) unsigned NOT NULL default '0', \
+ `type` smallint(6) unsigned NOT NULL default '0', \
+ `user_name` varchar(255) CHARACTER SET utf8 NOT NULL default '', \
+ `company` int(11) unsigned NOT NULL default '0', \
+ `guid` blob NOT NULL, \
+ PRIMARY KEY (`user_id`, `hierarchy_id`, `type`), \
+ UNIQUE KEY `id` (`id`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_USERS "CREATE TABLE `users` ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `externid` blob, \
+ `objectclass` int(11) NOT NULL default '0', \
+ `signature` varbinary(255) NOT NULL default '0', \
+ `company` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`id`), \
+ UNIQUE KEY externid (`externid`(255), `objectclass`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_OUTGOINGQUEUE "CREATE TABLE `outgoingqueue` ( \
+ `store_id` int(11) unsigned NOT NULL default '0', \
+ `hierarchy_id` int(11) unsigned NOT NULL default '0', \
+ `flags` tinyint(4) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`hierarchy_id`,`flags`,`store_id`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_LOB "CREATE TABLE `lob` ( \
+ `instanceid` int(11) unsigned NOT NULL, \
+ `chunkid` smallint(6) unsigned NOT NULL, \
+ `tag` smallint(6) unsigned NOT NULL, \
+ `val_binary` longblob, \
+ PRIMARY KEY (`instanceid`,`tag`,`chunkid`) \
+ ) ENGINE=InnoDB MAX_ROWS=1000000000 AVG_ROW_LENGTH=1750 CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_REFERENCES "CREATE TABLE `singleinstances` ( \
+ `instanceid` int(11) unsigned NOT NULL auto_increment, \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `tag` smallint(6) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`instanceid`, `hierarchyid`, `tag`), \
+ UNIQUE KEY `hkey` (`hierarchyid`, `tag`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_OBJECT "CREATE TABLE object ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `externid` blob, \
+ `objectclass` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`id`, `objectclass`), \
+ UNIQUE KEY id (`id`), \
+ UNIQUE KEY externid (`externid`(255), `objectclass`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_OBJECT_PROPERTY "CREATE TABLE objectproperty ( \
+ `objectid` int(11) unsigned NOT NULL default '0', \
+ `propname` varchar(255) binary NOT NULL, \
+ `value` text, \
+ PRIMARY KEY (`objectid`, `propname`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_OBJECT_MVPROPERTY "CREATE TABLE objectmvproperty ( \
+ `objectid` int(11) unsigned NOT NULL default '0', \
+ `propname` varchar(255) binary NOT NULL, \
+ `orderid` tinyint(11) unsigned NOT NULL default '0', \
+ `value` text, \
+ PRIMARY KEY (`objectid`, `orderid`, `propname`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_OBJECT_RELATION "CREATE TABLE objectrelation ( \
+ `objectid` int(11) unsigned NOT NULL default '0', \
+ `parentobjectid` int(11) unsigned NOT NULL default '0', \
+ `relationtype` tinyint(11) unsigned NOT NULL, \
+ PRIMARY KEY (`objectid`, `parentobjectid`, `relationtype`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_VERSIONS "CREATE TABLE versions ( \
+ `major` int(11) unsigned NOT NULL default '0', \
+ `minor` int(11) unsigned NOT NULL default '0', \
+ `micro` int(11) unsigned not null default 0, \
+ `revision` int(11) unsigned NOT NULL default '0', \
+ `databaserevision` int(11) unsigned NOT NULL default '0', \
+ `updatetime` datetime NOT NULL, \
+ PRIMARY KEY (`major`, `minor`, `micro`, `revision`, `databaserevision`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_SEARCHRESULTS "CREATE TABLE searchresults ( \
+ `folderid` int(11) unsigned NOT NULL default '0', \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `flags` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`folderid`, `hierarchyid`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_CHANGES "CREATE TABLE `changes` ( \
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, \
+ `sourcekey` VARBINARY(64) NOT NULL, \
+ `parentsourcekey` VARBINARY(64) NOT NULL, \
+ `change_type` INT(11) UNSIGNED NOT NULL DEFAULT '0', \
+ `flags` INT(11) UNSIGNED DEFAULT NULL, \
+ `sourcesync` INT(11) UNSIGNED DEFAULT NULL, \
+ PRIMARY KEY (`parentsourcekey`,`sourcekey`,`change_type`), \
+ UNIQUE KEY `changeid` (`id`), \
+ UNIQUE KEY `state` (`parentsourcekey`,`id`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_ABCHANGES "CREATE TABLE `abchanges` ( \
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, \
+ `sourcekey` VARBINARY(255) NOT NULL, \
+ `parentsourcekey` VARBINARY(255) NOT NULL, \
+ `change_type` INT(11) UNSIGNED NOT NULL DEFAULT '0', \
+ PRIMARY KEY (`parentsourcekey`,`change_type`,`sourcekey`), \
+ UNIQUE KEY `changeid` (`id`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_SYNCS "CREATE TABLE `syncs` ( \
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, \
+ `sourcekey` VARBINARY(64) NOT NULL, \
+ `change_id` INT(11) UNSIGNED NOT NULL, \
+ `sync_type` INT(11) UNSIGNED NULL, \
+ `sync_time` DATETIME NOT NULL, \
+ PRIMARY KEY(`id`), \
+ KEY `foldersync` (`sourcekey`,`sync_type`), \
+ KEY `changes` (`change_id`), \
+ KEY `sync_time` (`sync_time`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEFS_SYNCEDMESSAGES "CREATE TABLE `syncedmessages` ( \
+ `sync_id` int(11) unsigned NOT NULL, \
+ `change_id` int(11) unsigned NOT NULL, \
+ `sourcekey` varbinary(64) NOT NULL, \
+ `parentsourcekey` varbinary(64) NOT NULL, \
+ PRIMARY KEY (`sync_id`,`change_id`,`sourcekey`), \
+ KEY `sync_state` (`sync_id`,`change_id`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_INDEXED_PROPERTIES "CREATE TABLE indexedproperties ( \
+ `hierarchyid` int(11) unsigned NOT NULL default '0', \
+ `tag` smallint(6) unsigned NOT NULL default '0', \
+ `val_binary` varbinary(255), \
+ PRIMARY KEY (`hierarchyid`, `tag`), \
+ UNIQUE KEY `bin` (`tag`, `val_binary`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_SETTINGS "CREATE TABLE settings ( \
+ `name` varchar(255) binary NOT NULL, \
+ `value` blob NOT NULL, \
+ PRIMARY KEY (`name`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+#define Z_TABLEDEF_CLIENTUPDATESTATUS "CREATE TABLE clientupdatestatus ( \
+ `userid` int(11) unsigned NOT NULL, \
+ `trackid` int(11) unsigned NOT NULL, \
+ `updatetime` DATETIME NOT NULL, \
+ `currentversion` varchar(50) binary NOT NULL, \
+ `latestversion` varchar(50) binary NOT NULL, \
+ `computername` varchar(255) binary NOT NULL, \
+ `status` int(11) unsigned NOT NULL, \
+ PRIMARY KEY (`userid`), \
+ UNIQUE KEY (`trackid`) \
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
+
+// Default mysql table data
+#define Z_TABLEDATA_ACL "INSERT INTO `acl` VALUES (2, 2, 2, 1531), \
+ (2, 1, 2, 1531), \
+ (1, 1, 2, 1531), \
+ (1, 2, 2, 1531);"
+
+#define Z_TABLEDATA_HIERARCHY "INSERT INTO `hierarchy` VALUES \
+ (1, NULL, 1, 0, 2),\
+ (2, 1, 3, 0, 2);"
+
+#define Z_TABLEDATA_PROPERTIES "INSERT INTO `properties` VALUES \
+ (1, 12289, 30, NULL, 'Admin store', NULL, NULL, NULL, NULL, NULL, false), \
+ (2, 12289, 30, NULL, 'root Admin store', NULL, NULL, NULL, NULL, NULL, false);"
+
+#define Z_TABLEDATA_STORES "INSERT INTO `stores` VALUES (1, 1, 2, 0, 'SYSTEM', 0, 0x8962ffeffb7b4d639bc5967c4bb58234);"
+
+//1=KOPANO_UID_EVERYONE, 0x30002=DISTLIST_SECURITY
+//2=KOPANO_UID_SYSTEM, 0x10001=ACTIVE_USER
+#define Z_TABLEDATA_USERS "INSERT INTO `users` (`id`, `externid`, `objectclass`, `signature`, `company`) VALUES \
+ (1, NULL, 0x30002, '', 0), \
+ (2, NULL, 0x10001, '', 0);"
+
+#define Z_TABLEDATA_INDEXED_PROPERTIES "INSERT INTO `indexedproperties` VALUES (1, 0x0FFF, 0x000000008962ffeffb7b4d639bc5967c4bb5823400000000010000000100000000000000 ), \
+ (2,0x0FFF, 0x000000008962ffeffb7b4d639bc5967c4bb5823400000000030000000200000000000000);"
+
+#define Z_TABLEDATA_SETTINGS "INSERT INTO `settings` VALUES ('source_key_auto_increment' , CHAR(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)), \
+ ('imapseq', '3'), \
+ ('charset', 'utf8');"
+
+// Database update definitions
+#define Z_UPDATE_CREATE_VERSIONS_TABLE 1
+#define Z_UPDATE_CREATE_SEARCHFOLDERS_TABLE 2
+#define Z_UPDATE_FIX_USERTABLE_NONACTIVE 3
+#define Z_UPDATE_ADD_FLAGS_TO_SEARCHRESULTS 4
+#define Z_UPDATE_POPULATE_SEARCHFOLDERS 5
+#define Z_UPDATE_CREATE_CHANGES_TABLE 6
+#define Z_UPDATE_CREATE_SYNCS_TABLE 7
+#define Z_UPDATE_CREATE_INDEXEDPROPS_TABLE 8
+#define Z_UPDATE_CREATE_SETTINGS_TABLE 9
+#define Z_UPDATE_CREATE_SERVER_GUID 10
+#define Z_UPDATE_CREATE_SOURCE_KEYS 11
+#define Z_UPDATE_CONVERT_ENTRYIDS 12
+#define Z_UPDATE_CONVERT_SC_ENTRYIDLIST 13
+#define Z_UPDATE_CONVERT_USER_OBJECT_TYPE 14
+#define Z_UPDATE_ADD_USER_SIGNATURE 15
+#define Z_UPDATE_ADD_SOURCE_KEY_SETTING 16
+#define Z_UPDATE_FIX_USERS_RESTRICTIONS 17
+#define Z_UPDATE_ADD_USER_COMPANY 18
+#define Z_UPDATE_ADD_OBJECT_RELATION_TYPE 19
+#define Z_UPDATE_DEL_DEFAULT_COMPANY 20
+#define Z_UPDATE_ADD_COMPANY_TO_STORES 21
+#define Z_UPDATE_ADD_IMAP_SEQ 22
+#define Z_UPDATE_KEYS_CHANGES 23
+#define Z_UPDATE_MOVE_PUBLICFOLDERS 24
+#define Z_UPDATE_ADD_EXTERNID_TO_OBJECT 25
+#define Z_UPDATE_CREATE_REFERENCES 26
+#define Z_UPDATE_LOCK_DISTRIBUTED 27
+#define Z_UPDATE_CREATE_ABCHANGES_TABLE 28
+#define Z_UPDATE_SINGLEINSTANCE_TAG 29
+#define Z_UPDATE_CREATE_SYNCEDMESSAGES_TABLE 30
+#define Z_UPDATE_FORCE_AB_RESYNC 31
+#define Z_UPDATE_RENAME_OBJECT_TYPE_TO_CLASS 32
+#define Z_UPDATE_CONVERT_OBJECT_TYPE_TO_CLASS 33
+#define Z_UPDATE_ADD_OBJECT_MVPROPERTY_TABLE 34
+#define Z_UPDATE_COMPANYNAME_TO_COMPANYID 35
+#define Z_UPDATE_OUTGOINGQUEUE_PRIMARY_KEY 36
+#define Z_UPDATE_ACL_PRIMARY_KEY 37
+#define Z_UPDATE_BLOB_EXTERNID 38
+#define Z_UPDATE_KEYS_CHANGES_2 39
+#define Z_UPDATE_MVPROPERTIES_PRIMARY_KEY 40
+#define Z_UPDATE_FIX_SECURITYGROUP_DBPLUGIN 41
+#define Z_UPDATE_CONVERT_SENDAS_DBPLUGIN 42
+#define Z_UPDATE_MOVE_IMAP_SUBSCRIBES 43
+#define Z_UPDATE_SYNC_TIME_KEY 44
+#define Z_UPDATE_ADD_STATE_KEY 45
+#define Z_UPDATE_CONVERT_TO_UNICODE 46
+#define Z_UPDATE_CONVERT_STORE_USERNAME 47
+#define Z_UPDATE_CONVERT_RULES 48
+#define Z_UPDATE_CONVERT_SEARCH_FOLDERS 49
+#define Z_UPDATE_CONVERT_PROPERTIES 50
+#define Z_UPDATE_CREATE_COUNTERS 51
+#define Z_UPDATE_CREATE_COMMON_PROPS 52
+#define Z_UPDATE_CHECK_ATTACHMENTS 53
+#define Z_UPDATE_CREATE_TPROPERTIES 54
+#define Z_UPDATE_CONVERT_HIERARCHY 55
+#define Z_UPDATE_CREATE_DEFERRED 56
+#define Z_UPDATE_CONVERT_CHANGES 57
+#define Z_UPDATE_CONVERT_NAMES 58
+#define Z_UPDATE_CONVERT_RF_TOUNICODE 59
+#define Z_UPDATE_CREATE_CLIENTUPDATE_TABLE 60
+#define Z_UPDATE_CONVERT_STORES 61
+#define Z_UPDATE_UPDATE_STORES 62
+#define Z_UPDATE_UPDATE_WLINK_RECKEY 63
+#define Z_UPDATE_VERSIONTBL_MICRO 64
+#define Z_UPDATE_CHANGES_PKEY 65
+#define Z_UPDATE_ABCHANGES_PKEY 66
+
+/*
+ * The first population of the SQL tables can use both create-type and
+ * update-type operations; %Z_UPDATE_RELEASE_ID specifies the schema
+ * version that can be reached with creates only.
+ * (This is never less than %Z_UPDATE_LAST.)
+ */
+#define Z_UPDATE_RELEASE_ID 66
+
+// This is the last update ID always update this to the last ID
+#define Z_UPDATE_LAST 66
+
+#endif
diff --git a/ECDatabaseMySQL.cpp b/ECDatabaseMySQL.cpp
new file mode 100644
index 000000000000..d9698c297c7d
--- /dev/null
+++ b/ECDatabaseMySQL.cpp
@@ -0,0 +1,2031 @@
+/*
+ * Copyright 2005 - 2015 Zarafa B.V. and its licensors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <zarafa/platform.h>
+
+#include <iostream>
+#include <errmsg.h>
+#include "ECDatabaseMySQL.h"
+#include "mysqld_error.h"
+
+#include <zarafa/stringutil.h>
+
+#include <zarafa/ECDefs.h>
+#include "ECDBDef.h"
+#include "ECUserManagement.h"
+#include <zarafa/ECConfig.h>
+#include <zarafa/ECLogger.h>
+#include <zarafa/MAPIErrors.h>
+#include <zarafa/ZarafaCode.h>
+
+#include <zarafa/ecversion.h>
+
+#include <mapidefs.h>
+#include "ECConversion.h"
+#include "SOAPUtils.h"
+#include "ECSearchFolders.h"
+
+#include "ECDatabaseUpdate.h"
+#include "ECStatsCollector.h"
+
+#ifdef HAVE_OFFLINE_SUPPORT
+#include "ECDBUpdateProgress.h"
+#endif
+
+using namespace std;
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static const char THIS_FILE[] = __FILE__;
+#endif
+#ifdef DEBUG
+#define DEBUG_SQL 0
+#define DEBUG_TRANSACTION 0
+#endif
+
+#define LOG_SQL_DEBUG(_msg, ...) \
+ ec_log(EC_LOGLEVEL_DEBUG | EC_LOGLEVEL_SQL, _msg, ##__VA_ARGS__)
+
+// The maximum packet size. This is automatically also the maximum
+// size of a single entry in the database. This means that PR_BODY, PR_COMPRESSED_RTF
+// etc. cannot grow larger than 16M. This shouldn't be such a problem in practice.
+
+// In debian lenny, setting your max_allowed_packet to 16M actually gives this value.... Unknown
+// why.
+#define MAX_ALLOWED_PACKET 16776192
+
+typedef struct _sUpdateList {
+ unsigned int ulVersion;
+ unsigned int ulVersionMin; // Version to start the update
+ const char *lpszLogComment;
+ ECRESULT (*lpFunction)(ECDatabase* lpDatabase);
+} sUpdateList_t;
+
+typedef struct _sNewDatabase {
+ const char *lpComment;
+ const char *lpSQL;
+} sSQLDatabase_t;
+
+class zcp_versiontuple _zcp_final {
+ public:
+ zcp_versiontuple(unsigned int maj = 0, unsigned int min = 0,
+ unsigned int mic = 0, unsigned int rev = 0, unsigned int dbs = 0) :
+ v_major(maj), v_minor(min), v_micro(mic),
+ v_rev(rev), v_schema(dbs)
+ {
+ }
+ std::string stringify(char sep = '.') const;
+ int compare(const zcp_versiontuple &) const;
+ /* stupid major(3) function uses a #define in glibc */
+ unsigned int v_major, v_minor, v_micro, v_rev, v_schema;
+};
+
+static const sUpdateList_t sUpdateList[] = {
+ // Updates from version 5.02 to 5.10
+ { Z_UPDATE_CREATE_VERSIONS_TABLE, 0, "Create table: versions", UpdateDatabaseCreateVersionsTable },
+ { Z_UPDATE_CREATE_SEARCHFOLDERS_TABLE, 0, "Create table: searchresults", UpdateDatabaseCreateSearchFolders },
+ { Z_UPDATE_FIX_USERTABLE_NONACTIVE, 0, "Update table: users, field: nonactive", UpdateDatabaseFixUserNonActive },
+ // Only update if previous revision was after Z_UPDATE_CREATE_SEARCHFOLDERS_TABLE, because the definition has changed
+ { Z_UPDATE_ADD_FLAGS_TO_SEARCHRESULTS, Z_UPDATE_CREATE_SEARCHFOLDERS_TABLE, "Update table: searchresults", UpdateDatabaseCreateSearchFoldersFlags },
+ { Z_UPDATE_POPULATE_SEARCHFOLDERS, 0, "Populate search folders", UpdateDatabasePopulateSearchFolders },
+
+ // Updates from version 5.10 to 5.20
+ { Z_UPDATE_CREATE_CHANGES_TABLE, 0, "Create table: changes", UpdateDatabaseCreateChangesTable },
+ { Z_UPDATE_CREATE_SYNCS_TABLE, 0, "Create table: syncs", UpdateDatabaseCreateSyncsTable },
+ { Z_UPDATE_CREATE_INDEXEDPROPS_TABLE, 0, "Create table: indexedproperties", UpdateDatabaseCreateIndexedPropertiesTable },
+ { Z_UPDATE_CREATE_SETTINGS_TABLE, 0, "Create table: settings", UpdateDatabaseCreateSettingsTable},
+ { Z_UPDATE_CREATE_SERVER_GUID, 0, "Insert server GUID into settings", UpdateDatabaseCreateServerGUID },
+ { Z_UPDATE_CREATE_SOURCE_KEYS, 0, "Insert source keys into indexedproperties", UpdateDatabaseCreateSourceKeys },
+
+ // Updates from version 5.20 to 6.00
+ { Z_UPDATE_CONVERT_ENTRYIDS, 0, "Convert entryids: indexedproperties", UpdateDatabaseConvertEntryIDs },
+ { Z_UPDATE_CONVERT_SC_ENTRYIDLIST, 0, "Update entrylist searchcriteria", UpdateDatabaseSearchCriteria },
+ { Z_UPDATE_CONVERT_USER_OBJECT_TYPE, 0, "Add Object type to 'users' table", UpdateDatabaseAddUserObjectType },
+ { Z_UPDATE_ADD_USER_SIGNATURE, 0, "Add signature to 'users' table", UpdateDatabaseAddUserSignature },
+ { Z_UPDATE_ADD_SOURCE_KEY_SETTING, 0, "Add setting 'source_key_auto_increment'", UpdateDatabaseAddSourceKeySetting },
+ { Z_UPDATE_FIX_USERS_RESTRICTIONS, 0, "Add restriction to 'users' table", UpdateDatabaseRestrictExternId },
+
+ // Update from version 6.00 to 6.10
+ { Z_UPDATE_ADD_USER_COMPANY, 0, "Add company column to 'users' table", UpdateDatabaseAddUserCompany },
+ { Z_UPDATE_ADD_OBJECT_RELATION_TYPE, 0, "Add Object relation type to 'objectrelation' table", UpdateDatabaseAddObjectRelationType },
+ { Z_UPDATE_DEL_DEFAULT_COMPANY, 0, "Delete default company from 'users' table", UpdateDatabaseDelUserCompany},
+ { Z_UPDATE_ADD_COMPANY_TO_STORES, 0, "Adding company to 'stores' table", UpdateDatabaseAddCompanyToStore},
+
+ // Update from version x to x
+ { Z_UPDATE_ADD_IMAP_SEQ, 0, "Add IMAP sequence number in 'settings' table", UpdateDatabaseAddIMAPSequenceNumber},
+ { Z_UPDATE_KEYS_CHANGES, Z_UPDATE_CREATE_CHANGES_TABLE, "Update keys in 'changes' table", UpdateDatabaseKeysChanges},
+
+ // Update from version 6.1x to 6.20
+ { Z_UPDATE_MOVE_PUBLICFOLDERS, 0, "Moving publicfolders and favorites", UpdateDatabaseMoveFoldersInPublicFolder},
+
+ // Update from version 6.2x to 6.30
+ { Z_UPDATE_ADD_EXTERNID_TO_OBJECT, 0, "Adding externid to 'object' table", UpdateDatabaseAddExternIdToObject},
+ { Z_UPDATE_CREATE_REFERENCES, 0, "Creating Single Instance Attachment table", UpdateDatabaseCreateReferences},
+ { Z_UPDATE_LOCK_DISTRIBUTED, 0, "Locking multiserver capability", UpdateDatabaseLockDistributed},
+ { Z_UPDATE_CREATE_ABCHANGES_TABLE, 0, "Creating Addressbook Changes table", UpdateDatabaseCreateABChangesTable},
+ { Z_UPDATE_SINGLEINSTANCE_TAG, 0, "Updating 'singleinstances' table to correct tag value", UpdateDatabaseSetSingleinstanceTag},
+
+ // Update from version 6.3x to 6.40
+ { Z_UPDATE_CREATE_SYNCEDMESSAGES_TABLE, 0, "Create table: synced messages", UpdateDatabaseCreateSyncedMessagesTable},
+
+ // Update from < 6.30 to >= 6.30
+ { Z_UPDATE_FORCE_AB_RESYNC, 0, "Force Addressbook Resync", UpdateDatabaseForceAbResync},
+
+ // Update from version 6.3x to 6.40
+ { Z_UPDATE_RENAME_OBJECT_TYPE_TO_CLASS, 0, "Rename objecttype columns to objectclass", UpdateDatabaseRenameObjectTypeToObjectClass},
+ { Z_UPDATE_CONVERT_OBJECT_TYPE_TO_CLASS, 0, "Convert objecttype columns to objectclass values", UpdateDatabaseConvertObjectTypeToObjectClass},
+ { Z_UPDATE_ADD_OBJECT_MVPROPERTY_TABLE, 0, "Add object MV property table", UpdateDatabaseAddMVPropertyTable},
+ { Z_UPDATE_COMPANYNAME_TO_COMPANYID, 0, "Link objects in DB plugin through companyid", UpdateDatabaseCompanyNameToCompanyId},
+ { Z_UPDATE_OUTGOINGQUEUE_PRIMARY_KEY, 0, "Update outgoingqueue key", UpdateDatabaseOutgoingQueuePrimarykey},
+ { Z_UPDATE_ACL_PRIMARY_KEY, 0, "Update acl key", UpdateDatabaseACLPrimarykey},
+ { Z_UPDATE_BLOB_EXTERNID, 0, "Update externid in object table", UpdateDatabaseBlobExternId}, // Avoid MySQL 4.x traling spaces quirk
+ { Z_UPDATE_KEYS_CHANGES_2, 0, "Update keys in 'changes' table", UpdateDatabaseKeysChanges2},
+ { Z_UPDATE_MVPROPERTIES_PRIMARY_KEY, 0, "Update mvproperties key", UpdateDatabaseMVPropertiesPrimarykey},
+ { Z_UPDATE_FIX_SECURITYGROUP_DBPLUGIN, 0, "Update DB plugin group to security groups", UpdateDatabaseFixDBPluginGroups},
+ { Z_UPDATE_CONVERT_SENDAS_DBPLUGIN, 0, "Update DB/Unix plugin sendas settings", UpdateDatabaseFixDBPluginSendAs},
+
+ { Z_UPDATE_MOVE_IMAP_SUBSCRIBES, 0, "Move IMAP subscribed list from store to inbox", UpdateDatabaseMoveSubscribedList},
+ { Z_UPDATE_SYNC_TIME_KEY, 0, "Update sync table time index", UpdateDatabaseSyncTimeIndex },
+
+ // Update within the 6.40
+ { Z_UPDATE_ADD_STATE_KEY, 0, "Update changes table state key", UpdateDatabaseAddStateKey },
+
+ // Blocking upgrade from 6.40 to 7.00, tables are not unicode compatible.
+ { Z_UPDATE_CONVERT_TO_UNICODE, 0, "Converting database to Unicode", UpdateDatabaseConvertToUnicode },
+
+ // Update from version 6.4x to 7.00
+ { Z_UPDATE_CONVERT_STORE_USERNAME, 0, "Update stores table usernames", UpdateDatabaseConvertStoreUsername },
+ { Z_UPDATE_CONVERT_RULES, 0, "Converting rules to Unicode", UpdateDatabaseConvertRules },
+ { Z_UPDATE_CONVERT_SEARCH_FOLDERS, 0, "Converting search folders to Unicode", UpdateDatabaseConvertSearchFolders },
+ { Z_UPDATE_CONVERT_PROPERTIES, 0, "Converting properties for IO performance", UpdateDatabaseConvertProperties },
+ { Z_UPDATE_CREATE_COUNTERS, 0, "Creating counters for IO performance", UpdateDatabaseCreateCounters },
+ { Z_UPDATE_CREATE_COMMON_PROPS, 0, "Creating common properties for IO performance", UpdateDatabaseCreateCommonProps },
+ { Z_UPDATE_CHECK_ATTACHMENTS, 0, "Checking message attachment properties for IO performance", UpdateDatabaseCheckAttachments },
+ { Z_UPDATE_CREATE_TPROPERTIES, 0, "Creating tproperties for IO performance", UpdateDatabaseCreateTProperties },
+ { Z_UPDATE_CONVERT_HIERARCHY, 0, "Converting hierarchy for IO performance", UpdateDatabaseConvertHierarchy },
+ { Z_UPDATE_CREATE_DEFERRED, 0, "Creating deferred table for IO performance", UpdateDatabaseCreateDeferred },
+ { Z_UPDATE_CONVERT_CHANGES, 0, "Converting changes for IO performance", UpdateDatabaseConvertChanges },
+ { Z_UPDATE_CONVERT_NAMES, 0, "Converting names table to Unicode", UpdateDatabaseConvertNames },
+
+ // Update from version 7.00 to 7.0.1
+ { Z_UPDATE_CONVERT_RF_TOUNICODE, 0, "Converting receivefolder table to Unicode", UpdateDatabaseReceiveFolderToUnicode },
+
+ // Update from 6.40.13 / 7.0.3
+ { Z_UPDATE_CREATE_CLIENTUPDATE_TABLE, 0, "Creating client update status table", UpdateDatabaseClientUpdateStatus },
+
+ { Z_UPDATE_CONVERT_STORES, 0, "Converting stores table", UpdateDatabaseConvertStores },
+ { Z_UPDATE_UPDATE_STORES, 0, "Updating stores table", UpdateDatabaseUpdateStores },
+
+ // Update from 7.0 to 7.1
+ { Z_UPDATE_UPDATE_WLINK_RECKEY, 0, "Updating wunderbar record keys", UpdateWLinkRecordKeys },
+
+ // New in 7.2.2
+ { Z_UPDATE_VERSIONTBL_MICRO, 0, "Add \"micro\" column to \"versions\" table", UpdateVersionsTbl },
+
+ // New in 8.1.0 / 7.2.4, MySQL 5.7 compatibility
+ { Z_UPDATE_ABCHANGES_PKEY, 0, "Updating abchanges table", UpdateABChangesTbl },
+ { Z_UPDATE_CHANGES_PKEY, 0, "Updating changes table", UpdateChangesTbl },
+};
+
+static const char *const server_groups[] = {
+ "zarafa",
+ NULL,
+};
+
+typedef struct {
+ const char *szName;
+ const char *szSQL;
+} STOREDPROCS;
+
+/**
+ * Mode 0 = All bodies
+ * Mode 1 = Best body only (RTF better than HTML) + plaintext
+ * Mode 2 = Plaintext only
+ */
+
+static const char szGetProps[] =
+"CREATE PROCEDURE GetProps(IN hid integer, IN mode integer)\n"
+"BEGIN\n"
+" DECLARE bestbody INT;\n"
+
+" IF mode = 1 THEN\n"
+" call GetBestBody(hid, bestbody);\n"
+" END IF;\n"
+
+" SELECT 0, tag, properties.type, val_ulong, val_string, val_binary, val_double, val_longint, val_hi, val_lo, 0, names.nameid, names.namestring, names.guid\n"
+" FROM properties LEFT JOIN names ON (properties.tag-0x8501)=names.id WHERE hierarchyid=hid AND (tag <= 0x8500 OR names.id IS NOT NULL) AND (tag NOT IN (0x1009, 0x1013) OR mode = 0 OR (mode = 1 AND tag = bestbody) )\n"
+" UNION\n"
+" SELECT count(*), tag, mvproperties.type, \n"
+" group_concat(length(mvproperties.val_ulong),':', mvproperties.val_ulong ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_string),':', mvproperties.val_string ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_binary),':', mvproperties.val_binary ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_double),':', mvproperties.val_double ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_longint),':', mvproperties.val_longint ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_hi),':', mvproperties.val_hi ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_lo),':', mvproperties.val_lo ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" 0, names.nameid, names.namestring, names.guid \n"
+" FROM mvproperties LEFT JOIN names ON (mvproperties.tag-0x8501)=names.id WHERE hierarchyid=hid AND (tag <= 0x8500 OR names.id IS NOT NULL) GROUP BY tag, mvproperties.type; \n"
+"END;\n";
+
+static const char szPrepareGetProps[] =
+"CREATE PROCEDURE PrepareGetProps(IN hid integer)\n"
+"BEGIN\n"
+" SELECT 0, tag, properties.type, val_ulong, val_string, val_binary, val_double, val_longint, val_hi, val_lo, hierarchy.id, names.nameid, names.namestring, names.guid\n"
+" FROM properties JOIN hierarchy ON properties.hierarchyid=hierarchy.id LEFT JOIN names ON (properties.tag-0x8501)=names.id WHERE hierarchy.parent=hid AND (tag <= 0x8500 OR names.id IS NOT NULL);\n"
+" SELECT count(*), tag, mvproperties.type, \n"
+" group_concat(length(mvproperties.val_ulong),':', mvproperties.val_ulong ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_string),':', mvproperties.val_string ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_binary),':', mvproperties.val_binary ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_double),':', mvproperties.val_double ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_longint),':', mvproperties.val_longint ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_hi),':', mvproperties.val_hi ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" group_concat(length(mvproperties.val_lo),':', mvproperties.val_lo ORDER BY mvproperties.orderid SEPARATOR ''), \n"
+" hierarchy.id, names.nameid, names.namestring, names.guid \n"
+" FROM mvproperties JOIN hierarchy ON mvproperties.hierarchyid=hierarchy.id LEFT JOIN names ON (mvproperties.tag-0x8501)=names.id WHERE hierarchy.parent=hid AND (tag <= 0x8500 OR names.id IS NOT NULL) GROUP BY tag, mvproperties.type; \n"
+"END;\n";
+
+
+static const char szGetBestBody[] =
+"CREATE PROCEDURE GetBestBody(hid integer, OUT bestbody integer)\n"
+"DETERMINISTIC\n"
+"BEGIN\n"
+" DECLARE best INT;\n"
+" DECLARE CONTINUE HANDLER FOR NOT FOUND\n"
+" SET bestbody = 0 ;\n"
+" \n"
+" # Get body with lowest id (RTF before HTML)\n"
+" SELECT tag INTO bestbody FROM properties WHERE hierarchyid=hid AND tag IN (0x1009, 0x1013) ORDER BY tag LIMIT 1;\n"
+"END;\n";
+
+static const char szStreamObj[] =
+"# Read a type-5 (Message) item from the database, output properties and subobjects\n"
+"CREATE PROCEDURE StreamObj(IN rootid integer, IN maxdepth integer, IN mode integer)\n"
+"BEGIN\n"
+"DECLARE no_more_rows BOOLEAN;\n"
+"DECLARE subid INT;\n"
+"DECLARE subsubid INT;\n"
+"DECLARE subtype INT;\n"
+"DECLARE cur_hierarchy CURSOR FOR\n"
+" SELECT id,hierarchy.type FROM hierarchy WHERE parent=rootid AND type=7; \n"
+"DECLARE CONTINUE HANDLER FOR NOT FOUND\n"
+" SET no_more_rows = TRUE;\n"
+
+" call GetProps(rootid, mode);\n"
+
+" call PrepareGetProps(rootid);\n"
+
+" SELECT id,hierarchy.type FROM hierarchy WHERE parent=rootid;\n"
+
+" OPEN cur_hierarchy;\n"
+
+" the_loop: LOOP\n"
+" FETCH cur_hierarchy INTO subid, subtype;\n"
+
+" IF no_more_rows THEN\n"
+" CLOSE cur_hierarchy;\n"
+" LEAVE the_loop;\n"
+" END IF;\n"
+
+" IF subtype = 7 THEN\n"
+" BEGIN\n"
+" DECLARE CONTINUE HANDLER FOR NOT FOUND set subsubid = 0;\n"
+
+" IF maxdepth > 0 THEN\n"
+" SELECT id INTO subsubid FROM hierarchy WHERE parent=subid LIMIT 1;\n"
+" SELECT id, hierarchy.type FROM hierarchy WHERE parent = subid LIMIT 1;\n"
+
+" IF subsubid != 0 THEN\n"
+" # Recurse into submessage (must be type 5 since attachments can only contain nested messages)\n"
+" call StreamObj(subsubid, maxdepth-1, mode);\n"
+" END IF;\n"
+" ELSE\n"
+" # Maximum depth reached. Output a zero-length subset to indicate that we're\n"
+" # not recursing further.\n"
+" SELECT id, hierarchy.type FROM hierarchy WHERE parent=subid LIMIT 0;\n"
+" END IF;\n"
+" END;\n"
+
+" END IF;\n"
+" END LOOP the_loop;\n"
+
+"END\n";
+
+static const STOREDPROCS stored_procedures[] = {
+ { "GetProps", szGetProps },
+ { "PrepareGetProps", szPrepareGetProps },
+ { "GetBestBody", szGetBestBody },
+ { "StreamObj", szStreamObj }
+};
+
+std::string ECDatabaseMySQL::m_strDatabaseDir;
+
+std::string zcp_versiontuple::stringify(char sep) const
+{
+ return ::stringify(v_major) + sep + ::stringify(v_minor) + sep +
+ ::stringify(v_micro) + sep + ::stringify(v_rev) + sep +
+ ::stringify(v_schema);
+}
+
+int zcp_versiontuple::compare(const zcp_versiontuple &rhs) const
+{
+ if (v_major < rhs.v_major)
+ return -1;
+ if (v_major > rhs.v_major)
+ return 1;
+ if (v_minor < rhs.v_minor)
+ return -1;
+ if (v_minor > rhs.v_minor)
+ return 1;
+ if (v_micro < rhs.v_micro)
+ return -1;
+ if (v_micro > rhs.v_micro)
+ return 1;
+ if (v_rev < rhs.v_rev)
+ return -1;
+ if (v_rev > rhs.v_rev)
+ return 1;
+ return 0;
+}
+
+ECDatabaseMySQL::ECDatabaseMySQL(ECConfig *lpConfig)
+{
+ m_bMysqlInitialize = false;
+ m_bConnected = false;
+ m_bAutoLock = true;
+ m_lpConfig = lpConfig;
+ m_bSuppressLockErrorLogging = false;
+
+ // Create a mutex handle for mysql
+ pthread_mutexattr_t mattr;
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&m_hMutexMySql, &mattr);
+
+#ifdef DEBUG
+ m_ulTransactionState = 0;
+#endif
+
+}
+
+ECDatabaseMySQL::~ECDatabaseMySQL()
+{
+ Close();
+ // Close the mutex handle of mysql
+ pthread_mutex_destroy(&m_hMutexMySql);
+}
+
+ECRESULT ECDatabaseMySQL::InitLibrary(const char *lpDatabaseDir,
+ const char *lpConfigFile)
+{
+ ECRESULT er = erSuccess;
+ string strDatabaseDir;
+ string strConfigFile;
+ int ret = 0;
+
+ if(lpDatabaseDir) {
+ strDatabaseDir = "--datadir=";
+ strDatabaseDir+= lpDatabaseDir;
+
+ m_strDatabaseDir = lpDatabaseDir;
+ }
+
+ if(lpConfigFile) {
+ strConfigFile = "--defaults-file=";
+ strConfigFile+= lpConfigFile;
+ }
+
+ const char *server_args[] = {
+ "zarafa", /* this string is not used */
+ strConfigFile.c_str(),
+ strDatabaseDir.c_str(),
+ };
+ /*
+ * mysql's function signature stinks, and even their samples
+ * do the cast :(
+ */
+ if ((ret = mysql_library_init(arraySize(server_args),
+ const_cast<char **>(server_args),
+ const_cast<char **>(server_groups))) != 0) {
+ ec_log_crit("Unable to initialize mysql: error 0x%08X", ret);
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+exit:
+ return er;
+}
+
+/**
+ * Initialize anything that has to be initialized at server startup.
+ *
+ * Currently this means we're updating all the stored procedure definitions
+ */
+ECRESULT ECDatabaseMySQL::InitializeDBState(void)
+{
+ ECRESULT ret = InitializeDBStateInner();
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ /* Only do this recovery on the Windows thing */
+ if (ret == erSuccess)
+ return ret;
+
+ /* Start "repair" */
+ ec_log_err("InitializeDBState unsuccessful: %s (%d). Attempting drop, and retry.",
+ GetMAPIErrorMessage(ZarafaErrorToMAPIError(ret, hrSuccess)),
+ ret);
+ ret = DoUpdate("DROP TABLE mysql.proc");
+ if (ret != erSuccess)
+ goto exit;
+ ret = DoUpdate("DROP TABLE mysql.plugin");
+ if (ret != erSuccess)
+ goto exit;
+ ret = DoUpdate("DROP TABLE mysql.innodb_table_stats");
+ if (ret != erSuccess)
+ goto exit;
+ ret = DoUpdate("DROP TABLE mysql.innodb_index_stats");
+ if (ret != erSuccess)
+ goto exit;
+ return InitializeDBStateInner();
+ exit:
+ ec_log_err("Error during drop");
+#endif
+
+ return ret;
+}
+
+ECRESULT ECDatabaseMySQL::InitializeDBStateInner()
+{
+ ECRESULT er = erSuccess;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ // Unsure the stored procedures table is available
+
+ const char szProc[] =
+ "CREATE TABLE IF NOT EXISTS mysql.proc (db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum( 'CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns longblob NOT NULL, body longblob NOT NULL, definer char(141) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'IGNORE_BAD_TABLE_OPTIONS', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH') DEFAULT '' NOT NULL, comment text collate utf8_bin NOT NULL, character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, PRIMARY KEY (db,name,type)) engine=MyISAM character set utf8 comment='Stored Procedures';";
+ const char szPlugin[] =
+ "CREATE TABLE IF NOT EXISTS mysql.plugin (name varchar(64) DEFAULT '' NOT NULL, dl varchar(128) DEFAULT '' NOT NULL, PRIMARY KEY (name)) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci comment='MySQL plugins';";
+ const char szInnoStats[] =
+ "CREATE TABLE IF NOT EXISTS mysql.innodb_table_stats ( database_name VARCHAR(64) NOT NULL, table_name VARCHAR(64) NOT NULL, last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, n_rows BIGINT UNSIGNED NOT NULL, clustered_index_size BIGINT UNSIGNED NOT NULL, sum_of_other_index_sizes BIGINT UNSIGNED NOT NULL, PRIMARY KEY (database_name, table_name)) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0;";
+ const char szInnoIndexStats[] =
+ "CREATE TABLE IF NOT EXISTS mysql.innodb_index_stats ( database_name VARCHAR(64) NOT NULL, table_name VARCHAR(64) NOT NULL, index_name VARCHAR(64) NOT NULL, last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, stat_name VARCHAR(64) NOT NULL, stat_value BIGINT UNSIGNED NOT NULL, sample_size BIGINT UNSIGNED, stat_description VARCHAR(1024) NOT NULL, PRIMARY KEY (database_name, table_name, index_name, stat_name)) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0;";
+
+ er = DoUpdate("CREATE DATABASE IF NOT EXISTS mysql");
+ if(er != erSuccess)
+ goto exit;
+
+ er = DoUpdate(szProc);
+ if(er != erSuccess)
+ goto exit;
+
+ er = DoUpdate(szPlugin);
+ if(er != erSuccess)
+ goto exit;
+
+ er = DoUpdate(szInnoStats);
+ if(er != erSuccess)
+ goto exit;
+
+ er = DoUpdate(szInnoIndexStats);
+ if(er != erSuccess)
+ goto exit;
+#endif
+
+ for (unsigned int i = 0; i < arraySize(stored_procedures); ++i) {
+ er = DoUpdate(std::string("DROP PROCEDURE IF EXISTS ") + stored_procedures[i].szName);
+ if(er != erSuccess)
+ goto exit;
+
+ er = DoUpdate(stored_procedures[i].szSQL);
+ if(er != erSuccess) {
+ int err = mysql_errno(&m_lpMySQL);
+ if (err == ER_DBACCESS_DENIED_ERROR) {
+ ec_log_err("zarafa-server is not allowed to create stored procedures");
+ ec_log_err("Please grant CREATE ROUTINE permissions to the mysql user \"%s\" on the \"%s\" database",
+ m_lpConfig->GetSetting("mysql_user"), m_lpConfig->GetSetting("mysql_database"));
+ } else {
+ ec_log_err("zarafa-server is unable to create stored procedures, error %d", err);
+ }
+ goto exit;
+ }
+ }
+
+exit:
+ return er;
+}
+
+void ECDatabaseMySQL::UnloadLibrary(void)
+{
+ /*
+ * MySQL will timeout waiting for its own threads if the mysql
+ * initialization was done in another thread than the one where
+ * mysql_*_end() is called. [Global problem - it also affects
+ * projects other than Zarafa's.] :(
+ */
+ ec_log_notice("Waiting for mysql_server_end");
+ mysql_server_end();// mysql > 4.1.10 = mysql_library_end();
+ ec_log_notice("Waiting for mysql_library_end");
+ mysql_library_end();
+}
+
+ECRESULT ECDatabaseMySQL::InitEngine()
+{
+ ECRESULT er = erSuccess;
+
+ _ASSERT(m_bMysqlInitialize == false);
+
+ //Init mysql and make a connection
+ if(mysql_init(&m_lpMySQL) == NULL) {
+ ec_log_crit("ECDatabaseMySQL::InitEngine(): mysql_init failed");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ m_bMysqlInitialize = true;
+
+ // Set auto reconnect OFF
+ // mysql < 5.0.4 default on, mysql 5.0.4 > reconnection default off
+ // Zarafa always wants reconnect OFF, because we want to know when the connection
+ // is broken since this creates a new MySQL session, and we want to set some session
+ // variables
+ m_lpMySQL.reconnect = 0;
+
+exit:
+ return er;
+}
+
+std::string ECDatabaseMySQL::GetDatabaseDir()
+{
+ ASSERT(!m_strDatabaseDir.empty());
+ return m_strDatabaseDir;
+}
+
+ECRESULT ECDatabaseMySQL::CheckExistColumn(const std::string &strTable, const std::string &strColumn, bool *lpbExist)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+ DB_RESULT lpDBResult = NULL;
+
+ strQuery = "SELECT 1 FROM information_schema.COLUMNS "
+ "WHERE TABLE_SCHEMA = '" + string(m_lpConfig->GetSetting("mysql_database")) + "' "
+ "AND TABLE_NAME = '" + strTable + "' "
+ "AND COLUMN_NAME = '" + strColumn + "'";
+
+ er = DoSelect(strQuery, &lpDBResult);
+ if (er != erSuccess)
+ goto exit;
+
+ *lpbExist = (FetchRow(lpDBResult) != NULL);
+
+exit:
+ if (lpDBResult)
+ FreeResult(lpDBResult);
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::CheckExistIndex(const std::string &strTable,
+ const std::string &strKey, bool *lpbExist)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+ DB_RESULT lpDBResult = NULL;
+ DB_ROW lpRow = NULL;
+
+ // WHERE not supported in MySQL < 5.0.3
+ strQuery = "SHOW INDEXES FROM " + strTable;
+
+ er = DoSelect(strQuery, &lpDBResult);
+ if (er != erSuccess)
+ goto exit;
+
+ *lpbExist = false;
+ while ((lpRow = FetchRow(lpDBResult)) != NULL) {
+ // 2 is Key_name
+ if (lpRow[2] && strcmp(lpRow[2], strKey.c_str()) == 0) {
+ *lpbExist = true;
+ break;
+ }
+ }
+
+exit:
+ if (lpDBResult)
+ FreeResult(lpDBResult);
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::Connect()
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+ const char *lpMysqlPort = m_lpConfig->GetSetting("mysql_port");
+ const char *lpMysqlSocket = m_lpConfig->GetSetting("mysql_socket");
+ DB_RESULT lpDBResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ unsigned int gcm = 0;
+
+ if (*lpMysqlSocket == '\0')
+ lpMysqlSocket = NULL;
+
+ er = InitEngine();
+ if(er != erSuccess)
+ goto exit;
+
+ if(mysql_real_connect(&m_lpMySQL,
+ m_lpConfig->GetSetting("mysql_host"),
+ m_lpConfig->GetSetting("mysql_user"),
+ m_lpConfig->GetSetting("mysql_password"),
+ m_lpConfig->GetSetting("mysql_database"),
+ (lpMysqlPort)?atoi(lpMysqlPort):0,
+ lpMysqlSocket, CLIENT_MULTI_STATEMENTS) == NULL)
+ {
+ if(mysql_errno(&m_lpMySQL) == ER_BAD_DB_ERROR) // Database does not exist
+ er = ZARAFA_E_DATABASE_NOT_FOUND;
+ else
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::Connect(): mysql connect fail %x", er);
+ goto exit;
+ }
+
+ // Check if the database is available, but empty
+ strQuery = "SHOW tables";
+ er = DoSelect(strQuery, &lpDBResult);
+ if(er != erSuccess)
+ goto exit;
+
+ if(GetNumRows(lpDBResult) == 0) {
+ er = ZARAFA_E_DATABASE_NOT_FOUND;
+ goto exit;
+ }
+
+ if(lpDBResult) {
+ FreeResult(lpDBResult);
+ lpDBResult = NULL;
+ }
+
+ strQuery = "SHOW variables LIKE 'max_allowed_packet'";
+ er = DoSelect(strQuery, &lpDBResult);
+ if(er != erSuccess)
+ goto exit;
+
+ lpDBRow = FetchRow(lpDBResult);
+ /* lpDBRow[0] has the variable name, [1] the value */
+ if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
+ ec_log_warn("Unable to retrieve max_allowed_packet value. Assuming 16M");
+ m_ulMaxAllowedPacket = (unsigned int)MAX_ALLOWED_PACKET;
+ } else {
+ m_ulMaxAllowedPacket = atoui(lpDBRow[1]);
+ }
+
+ m_bConnected = true;
+
+#if HAVE_MYSQL_SET_CHARACTER_SET
+ // function since mysql 5.0.7
+ if (mysql_set_character_set(&m_lpMySQL, "utf8")) {
+ ec_log_err("Unable to set character set to \"utf8\"");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+#else
+ if (Query("set character_set_client = 'utf8'") != 0) {
+ ec_log_warn("Unable to set character_set_client value");
+ goto exit;
+ }
+ if (Query("set character_set_connection = 'utf8'") != 0) {
+ ec_log_warn("Unable to set character_set_connection value");
+ goto exit;
+ }
+ if (Query("set character_set_results = 'utf8'") != 0) {
+ ec_log_warn("Unable to set character_set_results value");
+ goto exit;
+ }
+#endif
+ if (Query("set max_sp_recursion_depth = 255") != 0) {
+ ec_log_err("Unable to set recursion depth");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ // Force to a sane value
+ gcm = atoui(m_lpConfig->GetSetting("mysql_group_concat_max_len"));
+ if(gcm < 1024)
+ gcm = 1024;
+
+ strQuery = (string)"SET SESSION group_concat_max_len = " + stringify(gcm);
+ if(Query(strQuery) != erSuccess ) {
+ ec_log_warn("Unable to set group_concat_max_len value");
+ }
+
+ // changing the SESSION max_allowed_packet is removed since mysql 5.1, and GLOBAL is for SUPER users only, so just give a warning
+ if (m_ulMaxAllowedPacket < MAX_ALLOWED_PACKET)
+ ec_log_warn("max_allowed_packet is smaller than 16M (%d). You are advised to increase this value by adding max_allowed_packet=16M in the [mysqld] section of my.cnf.", m_ulMaxAllowedPacket);
+
+ if (m_lpMySQL.server_version) {
+ // m_lpMySQL.server_version is a C type string (char*) containing something like "5.5.37-0+wheezy1" (MySQL),
+ // "5.5.37-MariaDB-1~wheezy-log" or "10.0.11-MariaDB=1~wheezy-log" (MariaDB)
+ // The following code may look funny, but it is correct, see http://www.cplusplus.com/reference/cstdlib/strtol/
+ long int majorversion = strtol(m_lpMySQL.server_version, NULL, 10);
+ // Check for over/underflow and version.
+ if (errno != ERANGE && majorversion >= 5) {
+ // this option was introduced in mysql 5.0, so let's not even try on 4.1 servers
+ strQuery = "SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_UNSIGNED_SUBTRACTION'";
+ Query(strQuery); // ignore error
+ }
+ }
+
+exit:
+ if(lpDBResult)
+ FreeResult(lpDBResult);
+
+ if (er == erSuccess)
+ g_lpStatsCollector->Increment(SCN_DATABASE_CONNECTS);
+ else
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_CONNECTS);
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::Close()
+{
+ ECRESULT er = erSuccess;
+
+ //INFO: No locking here
+
+ m_bConnected = false;
+
+ // Close mysql data connection and deallocate data
+ if(m_bMysqlInitialize)
+ mysql_close(&m_lpMySQL);
+
+ m_bMysqlInitialize = false;
+
+ return er;
+}
+
+// Get database ownership
+void ECDatabaseMySQL::Lock()
+{
+ pthread_mutex_lock(&m_hMutexMySql);
+}
+
+// Release the database ownership
+void ECDatabaseMySQL::UnLock()
+{
+ pthread_mutex_unlock(&m_hMutexMySql);
+}
+
+bool ECDatabaseMySQL::isConnected() {
+
+ return m_bConnected;
+}
+
+/**
+ * Perform an SQL query on MySQL
+ *
+ * Sends a query to the MySQL server, and does a reconnect if the server connection is lost before or during
+ * the SQL query. The reconnect is done only once. If the query fails after the reconnect, the entire call
+ * fails.
+ *
+ * It is up to the caller to get any result information from the query.
+ *
+ * @param[in] strQuery SQL query to perform
+ * @return result (erSuccess or ZARAFA_E_DATABASE_ERROR)
+ */
+ECRESULT ECDatabaseMySQL::Query(const string &strQuery) {
+ ECRESULT er = erSuccess;
+ int err;
+
+ LOG_SQL_DEBUG("SQL [%08lu]: \"%s;\"", m_lpMySQL.thread_id, strQuery.c_str());
+
+ // use mysql_real_query to be binary safe ( http://dev.mysql.com/doc/mysql/en/mysql-real-query.html )
+ err = mysql_real_query( &m_lpMySQL, strQuery.c_str(), strQuery.length() );
+
+ if(err && (mysql_errno(&m_lpMySQL) == CR_SERVER_LOST || mysql_errno(&m_lpMySQL) == CR_SERVER_GONE_ERROR)) {
+ ec_log_warn("SQL [%08lu] info: Try to reconnect", m_lpMySQL.thread_id);
+
+ er = Close();
+ if(er != erSuccess)
+ goto exit;
+
+ er = Connect();
+ if(er != erSuccess)
+ goto exit;
+
+ // Try again
+ err = mysql_real_query( &m_lpMySQL, strQuery.c_str(), strQuery.length() );
+ }
+
+ if(err) {
+ if (!m_bSuppressLockErrorLogging || GetLastError() == DB_E_UNKNOWN)
+ ec_log_err("SQL [%08lu] Failed: %s, Query Size: %lu, Query: \"%s\"", m_lpMySQL.thread_id, mysql_error(&m_lpMySQL), static_cast<unsigned long>(strQuery.size()), strQuery.c_str());
+ er = ZARAFA_E_DATABASE_ERROR;
+ // Don't assert on ER_NO_SUCH_TABLE because it's an anticipated error in the db upgrade code.
+ if (mysql_errno(&m_lpMySQL) != ER_NO_SUCH_TABLE)
+ ASSERT(false);
+ }
+
+exit:
+ return er;
+}
+
+/**
+ * Perform a SELECT operation on the database
+ *
+ * Sends the passed SELECT-like (any operation that outputs a result set) query to the MySQL server and retrieves
+ * the result.
+ *
+ * Setting fStreamResult will delay retrieving data from the network until FetchRow() is called. The only
+ * drawback is that GetRowCount() can therefore not be used unless all rows are fetched first. The main reason to
+ * use this is to conserve memory and increase pipelining (the client can start processing data before the server
+ * has completed the query)
+ *
+ * @param[in] strQuery SELECT query string
+ * @param[out] Result output
+ * @param[in] fStreamResult TRUE if data should be streamed instead of stored
+ * @return result erSuccess or ZARAFA_E_DATABASE_ERROR
+ */
+ECRESULT ECDatabaseMySQL::DoSelect(const string &strQuery, DB_RESULT *lppResult, bool fStreamResult) {
+
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+
+ _ASSERT(strQuery.length()!= 0);
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ if( Query(strQuery) != erSuccess ) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::DoSelect(): query failed");
+ goto exit;
+ }
+
+ if (fStreamResult)
+ lpResult = mysql_use_result(&m_lpMySQL);
+ else
+ lpResult = mysql_store_result(&m_lpMySQL);
+
+ if( lpResult == NULL ) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ if (!m_bSuppressLockErrorLogging || GetLastError() == DB_E_UNKNOWN)
+ ec_log_err("SQL [%08lu] result failed: %s, Query: \"%s\"", m_lpMySQL.thread_id, mysql_error(&m_lpMySQL), strQuery.c_str());
+ }
+
+ g_lpStatsCollector->Increment(SCN_DATABASE_SELECTS);
+
+ if (lppResult)
+ *lppResult = lpResult;
+ else if(lpResult)
+ FreeResult(lpResult);
+
+exit:
+ if (er != erSuccess) {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_SELECTS);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::DoSelectMulti(const string &strQuery) {
+
+ ECRESULT er = erSuccess;
+
+ _ASSERT(strQuery.length()!= 0);
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ if( Query(strQuery) != erSuccess ) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::DoSelectMulti(): select failed");
+ goto exit;
+ }
+
+ m_bFirstResult = true;
+
+ g_lpStatsCollector->Increment(SCN_DATABASE_SELECTS);
+
+exit:
+ if (er != erSuccess) {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_SELECTS);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+/**
+ * Get next resultset from a multi-resultset query
+ *
+ * @param[out] lppResult Resultset
+ * @return result
+ */
+ECRESULT ECDatabaseMySQL::GetNextResult(DB_RESULT *lppResult) {
+
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ int ret = 0;
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ if(!m_bFirstResult)
+ ret = mysql_next_result( &m_lpMySQL );
+
+ m_bFirstResult = false;
+
+ if(ret < 0) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("SQL [%08lu] next_result failed: expected more results", m_lpMySQL.thread_id);
+ goto exit;
+ }
+
+ if(ret > 0) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("SQL [%08lu] next_result of multi-resultset failed: %s", m_lpMySQL.thread_id, mysql_error(&m_lpMySQL));
+ goto exit;
+ }
+
+ lpResult = mysql_store_result( &m_lpMySQL );
+ if(lpResult == NULL) {
+ // I think this can only happen on the first result set of a query since otherwise mysql_next_result() would already fail
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("SQL [%08lu] result failed: %s", m_lpMySQL.thread_id, mysql_error(&m_lpMySQL));
+ goto exit;
+ }
+
+ if (lppResult)
+ *lppResult = lpResult;
+ else {
+ if(lpResult)
+ FreeResult(lpResult);
+ }
+
+exit:
+ if (er != erSuccess) {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_SELECTS);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+/**
+ * Finalize a multi-SQL call
+ *
+ * A stored procedure will output a NULL result set at the end of the stored procedure to indicate
+ * that the results have ended. This must be consumed here, otherwise the database will be left in a bad
+ * state.
+ */
+ECRESULT ECDatabaseMySQL::FinalizeMulti() {
+
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+
+ mysql_next_result(&m_lpMySQL);
+
+ lpResult = mysql_store_result(&m_lpMySQL);
+
+ if(lpResult != NULL) {
+ ec_log_err("SQL [%08lu] result failed: unexpected results received at end of batch", m_lpMySQL.thread_id);
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+exit:
+ if(lpResult)
+ FreeResult(lpResult);
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+/**
+ * Perform a UPDATE operation on the database
+ *
+ * Sends the passed UPDATE query to the MySQL server, and optionally returns the number of affected rows. The
+ * affected rows is the number of rows that have been MODIFIED, which is not necessarily the number of rows that
+ * MATCHED the WHERE-clause.
+ *
+ * @param[in] strQuery UPDATE query string
+ * @param[out] lpulAffectedRows (optional) Receives the number of affected rows
+ * @return result erSuccess or ZARAFA_E_DATABASE_ERROR
+ */
+ECRESULT ECDatabaseMySQL::DoUpdate(const string &strQuery, unsigned int *lpulAffectedRows) {
+
+ ECRESULT er = erSuccess;
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ er = _Update(strQuery, lpulAffectedRows);
+
+ if (er != erSuccess) {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_UPDATES);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ g_lpStatsCollector->Increment(SCN_DATABASE_UPDATES);
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::_Update(const string &strQuery, unsigned int *lpulAffectedRows)
+{
+ ECRESULT er = erSuccess;
+
+ if( Query(strQuery) != erSuccess ) {
+ // FIXME: Add the mysql error system ?
+ // er = nMysqlError;
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::_Update() query failed");
+ goto exit;
+ }
+
+ if(lpulAffectedRows)
+ *lpulAffectedRows = GetAffectedRows();
+
+exit:
+ return er;
+}
+
+/**
+ * Perform an INSERT operation on the database
+ *
+ * Sends the passed INSERT query to the MySQL server, and optionally returns the new insert ID and the number of inserted
+ * rows.
+ *
+ * @param[in] strQuery INSERT query string
+ * @param[out] lpulInsertId (optional) Receives the last insert id
+ * @param[out] lpulAffectedRows (optional) Receives the number of inserted rows
+ * @return result erSuccess or ZARAFA_E_DATABASE_ERROR
+ */
+ECRESULT ECDatabaseMySQL::DoInsert(const string &strQuery, unsigned int *lpulInsertId, unsigned int *lpulAffectedRows)
+{
+ ECRESULT er = erSuccess;
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ er = _Update(strQuery, lpulAffectedRows);
+
+ if (er == erSuccess) {
+ if (lpulInsertId)
+ *lpulInsertId = GetInsertId();
+ } else {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_INSERTS);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ g_lpStatsCollector->Increment(SCN_DATABASE_INSERTS);
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+/**
+ * Perform a DELETE operation on the database
+ *
+ * Sends the passed DELETE query to the MySQL server, and optionally the number of deleted rows
+ *
+ * @param[in] strQuery INSERT query string
+ * @param[out] lpulAffectedRows (optional) Receives the number of deleted rows
+ * @return result erSuccess or ZARAFA_E_DATABASE_ERROR
+ */
+ECRESULT ECDatabaseMySQL::DoDelete(const string &strQuery, unsigned int *lpulAffectedRows) {
+
+ ECRESULT er = erSuccess;
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+ er = _Update(strQuery, lpulAffectedRows);
+
+ if (er != erSuccess) {
+ g_lpStatsCollector->Increment(SCN_DATABASE_FAILED_DELETES);
+ g_lpStatsCollector->SetTime(SCN_DATABASE_LAST_FAILED, time(NULL));
+ }
+
+ g_lpStatsCollector->Increment(SCN_DATABASE_DELETES);
+
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+/*
+ * This function updates a sequence in an atomic fashion - if called correctly;
+ *
+ * To make it work correctly, the state of the database connection should *NOT* be in a transaction; this would delay
+ * committing of the data until a later time, causing other concurrent threads to possibly generate the same ID or lock
+ * while waiting for this transaction to end. So, don't call Begin() before calling this function unless you really
+ * know what you're doing.
+ *
+ * @todo measure sequence update calls, currently it is a update
+ */
+ECRESULT ECDatabaseMySQL::DoSequence(const std::string &strSeqName, unsigned int ulCount, unsigned long long *lpllFirstId) {
+ ECRESULT er = erSuccess;
+ unsigned int ulAffected = 0;
+
+ // Autolock, lock data
+ if(m_bAutoLock)
+ Lock();
+
+#ifdef DEBUG
+#if DEBUG_TRANSACTION
+ if(m_ulTransactionState != 0) {
+ ASSERT(FALSE);
+ }
+#endif
+#endif
+
+ // Attempt to update the sequence in an atomic fashion
+ er = DoUpdate("UPDATE settings SET value=LAST_INSERT_ID(value+1)+" + stringify(ulCount-1) + " WHERE name = '" + strSeqName + "'", &ulAffected);
+ if(er != erSuccess)
+ goto exit;
+
+ // If the setting was missing, insert it now, starting at sequence 1 (not 0 for safety - maybe there's some if(ulSequenceId) code somewhere)
+ if(ulAffected == 0) {
+ er = Query("INSERT INTO settings (name, value) VALUES('" + strSeqName + "',LAST_INSERT_ID(1)+" + stringify(ulCount-1) + ")");
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ *lpllFirstId = mysql_insert_id(&m_lpMySQL);
+
+exit:
+ // Autolock, unlock data
+ if(m_bAutoLock)
+ UnLock();
+
+ return er;
+}
+
+unsigned int ECDatabaseMySQL::GetAffectedRows() {
+
+ return (unsigned int)mysql_affected_rows(&m_lpMySQL);
+}
+
+unsigned int ECDatabaseMySQL::GetInsertId() {
+
+ return (unsigned int)mysql_insert_id(&m_lpMySQL);
+}
+
+void ECDatabaseMySQL::FreeResult(DB_RESULT sResult) {
+
+ _ASSERT(sResult != NULL);
+
+ if(sResult)
+ mysql_free_result((MYSQL_RES *)sResult);
+}
+
+unsigned int ECDatabaseMySQL::GetNumRows(DB_RESULT sResult) {
+
+ return (unsigned int)mysql_num_rows((MYSQL_RES *)sResult);
+}
+
+unsigned int ECDatabaseMySQL::GetNumRowFields(DB_RESULT sResult) {
+
+ return mysql_num_fields((MYSQL_RES *)sResult);
+}
+
+unsigned int ECDatabaseMySQL::GetRowIndex(DB_RESULT sResult, const std::string &strFieldname) {
+
+ MYSQL_FIELD *lpFields;
+ unsigned int cbFields;
+ unsigned int ulIndex = (unsigned int)-1;
+
+ lpFields = mysql_fetch_fields((MYSQL_RES *)sResult);
+ cbFields = mysql_field_count(&m_lpMySQL);
+
+ for (unsigned int i = 0; i < cbFields && ulIndex == (unsigned int)-1; ++i)
+ if (stricmp(lpFields[i].name, strFieldname.c_str()) == 0)
+ ulIndex = i;
+
+ return ulIndex;
+}
+
+DB_ROW ECDatabaseMySQL::FetchRow(DB_RESULT sResult) {
+
+ return mysql_fetch_row((MYSQL_RES *)sResult);
+}
+
+DB_LENGTHS ECDatabaseMySQL::FetchRowLengths(DB_RESULT sResult) {
+
+ return (DB_LENGTHS)mysql_fetch_lengths((MYSQL_RES *)sResult);
+}
+
+/**
+ * For some reason, MySQL only supports up to 3 bytes of utf-8 data. This means
+ * that data outside the BMP is not supported. This function filters the passed utf-8 string
+ * and removes the non-BMP characters. Since it should be extremely uncommon to have useful
+ * data outside the BMP, this should be acceptable.
+ *
+ * Note: BMP stands for Basic Multilingual Plane (first 0x10000 code points in unicode)
+ *
+ * If somebody points out a useful use case for non-BMP characters in the future, then we'll
+ * have to rethink this.
+ *
+ */
+std::string ECDatabaseMySQL::FilterBMP(const std::string &strToFilter)
+{
+ const char *c = strToFilter.c_str();
+ std::string::size_type pos = 0;
+ std::string strFiltered;
+
+ while(pos < strToFilter.size()) {
+ // Copy 1, 2, and 3-byte utf-8 sequences
+ int len;
+
+ if((c[pos] & 0x80) == 0)
+ len = 1;
+ else if((c[pos] & 0xE0) == 0xC0)
+ len = 2;
+ else if((c[pos] & 0xF0) == 0xE0)
+ len = 3;
+ else if((c[pos] & 0xF8) == 0xF0)
+ len = 4;
+ else if((c[pos] & 0xFC) == 0xF8)
+ len = 5;
+ else if((c[pos] & 0xFE) == 0xFC)
+ len = 6;
+ else {
+ // Invalid utf-8 ?
+ len = 1;
+ }
+
+ if(len < 4) {
+ strFiltered.append(&c[pos], len);
+ }
+
+ pos += len;
+ }
+
+ return strFiltered;
+}
+
+std::string ECDatabaseMySQL::Escape(const std::string &strToEscape)
+{
+ ULONG size = strToEscape.length()*2+1;
+ char *szEscaped = new char[size];
+ std::string escaped;
+
+ memset(szEscaped, 0, size);
+
+ mysql_real_escape_string(&this->m_lpMySQL, szEscaped, strToEscape.c_str(), strToEscape.length());
+
+ escaped = szEscaped;
+
+ delete [] szEscaped;
+
+ return escaped;
+}
+
+std::string ECDatabaseMySQL::EscapeBinary(unsigned char *lpData, unsigned int ulLen)
+{
+ ULONG size = ulLen*2+1;
+ char *szEscaped = new char[size];
+ std::string escaped;
+
+ memset(szEscaped, 0, size);
+
+ mysql_real_escape_string(&this->m_lpMySQL, szEscaped, (const char *)lpData, ulLen);
+
+ escaped = szEscaped;
+
+ delete [] szEscaped;
+
+ return "'" + escaped + "'";
+}
+
+std::string ECDatabaseMySQL::EscapeBinary(const std::string& strData)
+{
+ return EscapeBinary((unsigned char *)strData.c_str(), strData.size());
+}
+
+void ECDatabaseMySQL::ResetResult(DB_RESULT sResult) {
+
+ mysql_data_seek((MYSQL_RES *)sResult, 0);
+}
+
+const char *ECDatabaseMySQL::GetError(void)
+{
+ if(m_bMysqlInitialize == false)
+ return "MYSQL not initialized";
+
+ return mysql_error(&m_lpMySQL);
+}
+
+DB_ERROR ECDatabaseMySQL::GetLastError()
+{
+ DB_ERROR dberr;
+
+ switch (mysql_errno(&m_lpMySQL)) {
+ case ER_LOCK_WAIT_TIMEOUT:
+ dberr = DB_E_LOCK_WAIT_TIMEOUT;
+ break;
+
+ case ER_LOCK_DEADLOCK:
+ dberr = DB_E_LOCK_DEADLOCK;
+ break;
+
+ default:
+ dberr = DB_E_UNKNOWN;
+ break;
+ }
+
+ return dberr;
+}
+
+bool ECDatabaseMySQL::SuppressLockErrorLogging(bool bSuppress)
+{
+ std::swap(bSuppress, m_bSuppressLockErrorLogging);
+ return bSuppress;
+}
+
+ECRESULT ECDatabaseMySQL::Begin() {
+ ECRESULT er = erSuccess;
+
+ er = Query("BEGIN");
+
+#ifdef DEBUG
+#if DEBUG_TRANSACTION
+ ec_log_debug("%08X: BEGIN", &m_lpMySQL);
+ if(m_ulTransactionState != 0) {
+ ec_log_debug("BEGIN ALREADY ISSUED");
+ ASSERT(("BEGIN ALREADY ISSUED", FALSE));
+ }
+ m_ulTransactionState = 1;
+#endif
+#endif
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::Commit() {
+ ECRESULT er = erSuccess;
+ er = Query("COMMIT");
+
+#ifdef DEBUG
+#if DEBUG_TRANSACTION
+ ec_log_debug("%08X: COMMIT", &m_lpMySQL);
+ if(m_ulTransactionState != 1) {
+ ec_log_debug("NO BEGIN ISSUED");
+ ASSERT(("NO BEGIN ISSUED", FALSE));
+ }
+ m_ulTransactionState = 0;
+#endif
+#endif
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::Rollback() {
+ ECRESULT er = erSuccess;
+#ifdef HAVE_OFFLINE_SUPPORT
+ const char *lpQuery = "SHOW ENGINE INNODB STATUS";
+ int err = 0;
+#endif
+
+ er = Query("ROLLBACK");
+
+#ifdef DEBUG
+#if DEBUG_TRANSACTION
+ ec_log_debug("%08X: ROLLBACK", &m_lpMySQL);
+ if(m_ulTransactionState != 1) {
+ ec_log_debug("NO BEGIN ISSUED");
+ ASSERT(("NO BEGIN ISSUED", FALSE));
+ }
+ m_ulTransactionState = 0;
+#endif
+#endif
+#ifdef HAVE_OFFLINE_SUPPORT
+ // We'll log the innodb status after each rollback. This shouldn't
+ // happen too much though.
+ err = mysql_real_query(&m_lpMySQL, lpQuery, strlen(lpQuery));
+ if (!err) {
+ MYSQL_RES *lpResult = mysql_use_result(&m_lpMySQL);
+ if (lpResult) {
+ MYSQL_ROW row = mysql_fetch_row(lpResult);
+ if (row) {
+ unsigned int fields = mysql_num_fields(lpResult);
+ for (unsigned int i = 0; i < fields; ++i)
+ if (row[i])
+ ec_log_err("%s", row[i]);
+ }
+ mysql_free_result(lpResult);
+ }
+ }
+#endif
+
+ return er;
+}
+
+unsigned int ECDatabaseMySQL::GetMaxAllowedPacket() {
+ return m_ulMaxAllowedPacket;
+}
+
+void ECDatabaseMySQL::ThreadInit() {
+ mysql_thread_init();
+}
+
+void ECDatabaseMySQL::ThreadEnd() {
+ mysql_thread_end();
+}
+
+ECRESULT ECDatabaseMySQL::IsInnoDBSupported()
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ er = DoSelect("SHOW ENGINES", &lpResult);
+ if(er != erSuccess) {
+ ec_log_crit("Unable to query supported database engines. Error: %s", GetError());
+ goto exit;
+ }
+
+ while ((lpDBRow = FetchRow(lpResult)) != NULL) {
+ if (stricmp(lpDBRow[0], "InnoDB") != 0)
+ continue;
+
+ if (stricmp(lpDBRow[1], "DISABLED") == 0) {
+ // mysql has run with innodb enabled once, but disabled this.. so check your log.
+ ec_log_crit("INNODB engine is disabled. Please re-enable the INNODB engine. Check your MySQL log for more information or comment out skip-innodb in the mysql configuration file.");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ } else if (stricmp(lpDBRow[1], "YES") != 0 && stricmp(lpDBRow[1], "DEFAULT") != 0) {
+ // mysql is incorrectly configured or compiled.
+ ec_log_crit("INNODB engine is not supported. Please enable the INNODB engine in the mysql configuration file.");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+ break;
+ }
+ if (lpDBRow == NULL) {
+ ec_log_crit("Unable to find the \"InnoDB\" engine from the mysql server. Probably INNODB is not supported.");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+exit:
+ if(lpResult)
+ FreeResult(lpResult);
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::CreateDatabase()
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ const char *lpDatabase = m_lpConfig->GetSetting("mysql_database");
+ const char *lpMysqlPort = m_lpConfig->GetSetting("mysql_port");
+ const char *lpMysqlSocket = m_lpConfig->GetSetting("mysql_socket");
+
+ if(*lpMysqlSocket == '\0')
+ lpMysqlSocket = NULL;
+
+ // Zarafa database tables
+ static const sSQLDatabase_t sDatabaseTables[] = {
+ {"acl", Z_TABLEDEF_ACL},
+ {"hierarchy", Z_TABLEDEF_HIERARCHY},
+ {"names", Z_TABLEDEF_NAMES},
+ {"mvproperties", Z_TABLEDEF_MVPROPERTIES},
+ {"tproperties", Z_TABLEDEF_TPROPERTIES},
+ {"properties", Z_TABLEDEF_PROPERTIES},
+ {"delayedupdate", Z_TABLEDEF_DELAYEDUPDATE},
+ {"receivefolder", Z_TABLEDEF_RECEIVEFOLDER},
+
+ {"stores", Z_TABLEDEF_STORES},
+ {"users", Z_TABLEDEF_USERS},
+ {"outgoingqueue", Z_TABLEDEF_OUTGOINGQUEUE},
+ {"lob", Z_TABLEDEF_LOB},
+ {"searchresults", Z_TABLEDEF_SEARCHRESULTS},
+ {"changes", Z_TABLEDEF_CHANGES},
+ {"syncs", Z_TABLEDEF_SYNCS},
+ {"versions", Z_TABLEDEF_VERSIONS},
+ {"indexedproperties", Z_TABLEDEF_INDEXED_PROPERTIES},
+ {"settings", Z_TABLEDEF_SETTINGS},
+
+ {"object", Z_TABLEDEF_OBJECT},
+ {"objectproperty", Z_TABLEDEF_OBJECT_PROPERTY},
+ {"objectmvproperty", Z_TABLEDEF_OBJECT_MVPROPERTY},
+ {"objectrelation", Z_TABLEDEF_OBJECT_RELATION},
+
+ {"singleinstances", Z_TABLEDEF_REFERENCES },
+ {"abchanges", Z_TABLEDEF_ABCHANGES },
+ {"syncedmessages", Z_TABLEDEFS_SYNCEDMESSAGES },
+ {"clientupdatestatus", Z_TABLEDEF_CLIENTUPDATESTATUS },
+ };
+
+ // Zarafa database default data
+ static const sSQLDatabase_t sDatabaseData[] = {
+ {"users", Z_TABLEDATA_USERS},
+ {"stores", Z_TABLEDATA_STORES},
+ {"hierarchy", Z_TABLEDATA_HIERARCHY},
+ {"properties", Z_TABLEDATA_PROPERTIES},
+ {"acl", Z_TABLEDATA_ACL},
+ {"settings", Z_TABLEDATA_SETTINGS},
+ {"indexedproperties", Z_TABLEDATA_INDEXED_PROPERTIES},
+ };
+
+ er = InitEngine();
+ if(er != erSuccess)
+ goto exit;
+
+ // Connect
+ if(mysql_real_connect(&m_lpMySQL,
+ m_lpConfig->GetSetting("mysql_host"),
+ m_lpConfig->GetSetting("mysql_user"),
+ m_lpConfig->GetSetting("mysql_password"),
+ NULL,
+ (lpMysqlPort)?atoi(lpMysqlPort):0,
+ lpMysqlSocket, 0) == NULL)
+ {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::CreateDatabase(): mysql connect failed");
+ goto exit;
+ }
+
+ if(lpDatabase == NULL) {
+ ec_log_crit("Unable to create database: Unknown database");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ ec_log_notice("Creating database \"%s\"", lpDatabase);
+
+ er = IsInnoDBSupported();
+ if(er != erSuccess)
+ goto exit;
+
+ strQuery = "CREATE DATABASE IF NOT EXISTS `"+std::string(m_lpConfig->GetSetting("mysql_database"))+"`";
+ if(Query(strQuery) != erSuccess){
+ ec_log_crit("Unable to create database: %s", GetError());
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ strQuery = "USE `"+std::string(m_lpConfig->GetSetting("mysql_database"))+"`";
+ er = DoInsert(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Database tables
+ for (size_t i = 0; i < ARRAY_SIZE(sDatabaseTables); ++i) {
+ ec_log_info("Creating table \"%s\"", sDatabaseTables[i].lpComment);
+ er = DoInsert(sDatabaseTables[i].lpSQL);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ // Add the default table data
+ for (size_t i = 0; i < ARRAY_SIZE(sDatabaseData); ++i) {
+ ec_log_info("Add table data for \"%s\"", sDatabaseData[i].lpComment);
+ er = DoInsert(sDatabaseData[i].lpSQL);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ er = InsertServerGUID(this);
+ if(er != erSuccess)
+ goto exit;
+
+ // Add the release id in the database
+ er = UpdateDatabaseVersion(Z_UPDATE_RELEASE_ID);
+ if(er != erSuccess)
+ goto exit;
+
+ // Loop throught the update list
+ for (size_t i = Z_UPDATE_RELEASE_ID;
+ i < ARRAY_SIZE(sUpdateList); ++i)
+ {
+ er = UpdateDatabaseVersion(sUpdateList[i].ulVersion);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+
+ ec_log_notice("Database has been created");
+
+exit:
+ return er;
+}
+
+static inline bool row_has_null(DB_ROW row, size_t z)
+{
+ if (row == NULL)
+ return true;
+ while (z-- > 0)
+ if (row[z] == NULL)
+ return true;
+ return false;
+}
+
+ECRESULT ECDatabaseMySQL::GetDatabaseVersion(zcp_versiontuple *dbv)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ bool have_micro;
+
+ /* Check if the "micro" column already exists (it does since v64) */
+ er = DoSelect("SELECT databaserevision FROM versions WHERE databaserevision>=64 LIMIT 1", &lpResult);
+ if (er != erSuccess)
+ goto exit;
+ have_micro = GetNumRows(lpResult) > 0;
+ FreeResult(lpResult);
+
+ strQuery = "SELECT major, minor";
+ strQuery += have_micro ? ", micro" : ", 0";
+ strQuery += ", revision, databaserevision FROM versions ORDER BY major DESC, minor DESC";
+ if (have_micro)
+ strQuery += ", micro DESC";
+ strQuery += ", revision DESC, databaserevision DESC LIMIT 1";
+
+ er = DoSelect(strQuery, &lpResult);
+ if(er != erSuccess && mysql_errno(&m_lpMySQL) != ER_NO_SUCH_TABLE)
+ goto exit;
+
+ if(er != erSuccess || GetNumRows(lpResult) == 0) {
+ // Ok, maybe < than version 5.10
+ // check version
+
+ strQuery = "SHOW COLUMNS FROM properties";
+ er = DoSelect(strQuery, &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ lpDBRow = FetchRow(lpResult);
+ er = ZARAFA_E_UNKNOWN_DATABASE;
+
+ while (lpDBRow != NULL) {
+ if (lpDBRow[0] != NULL && stricmp(lpDBRow[0], "storeid") == 0) {
+ dbv->v_major = 5;
+ dbv->v_minor = 0;
+ dbv->v_rev = 0;
+ dbv->v_schema = 0;
+ er = erSuccess;
+ break;
+ }
+ lpDBRow = FetchRow(lpResult);
+ }
+
+ goto exit;
+ }
+
+ lpDBRow = FetchRow(lpResult);
+ if (row_has_null(lpDBRow, 5)) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("ECDatabaseMySQL::GetDatabaseVersion(): NULL row or columns");
+ goto exit;
+ }
+
+ dbv->v_major = strtoul(lpDBRow[0], NULL, 0);
+ dbv->v_minor = strtoul(lpDBRow[1], NULL, 0);
+ dbv->v_micro = strtoul(lpDBRow[2], NULL, 0);
+ dbv->v_rev = strtoul(lpDBRow[3], NULL, 0);
+ dbv->v_schema = strtoul(lpDBRow[4], NULL, 0);
+
+exit:
+ if(lpResult)
+ FreeResult(lpResult);
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::IsUpdateDone(unsigned int ulDatabaseRevision, unsigned int ulRevision)
+{
+ ECRESULT er = ZARAFA_E_NOT_FOUND;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+
+ strQuery = "SELECT major,minor,revision,databaserevision FROM versions WHERE databaserevision = " + stringify(ulDatabaseRevision);
+ if (ulRevision > 0)
+ strQuery += " AND revision = " + stringify(ulRevision);
+
+ strQuery += " ORDER BY major DESC, minor DESC, revision DESC, databaserevision DESC LIMIT 1";
+
+ er = DoSelect(strQuery, &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ if(GetNumRows(lpResult) != 1)
+ er = ZARAFA_E_NOT_FOUND;
+
+exit:
+ if(lpResult != NULL)
+ FreeResult(lpResult);
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::GetFirstUpdate(unsigned int *lpulDatabaseRevision)
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ er = DoSelect("SELECT MIN(databaserevision) FROM versions", &lpResult);
+ if(er != erSuccess && mysql_errno(&m_lpMySQL) != ER_NO_SUCH_TABLE)
+ goto exit;
+ else if(er == erSuccess)
+ lpDBRow = FetchRow(lpResult);
+
+ er = erSuccess;
+
+ if (lpDBRow == NULL || lpDBRow[0] == NULL ) {
+ *lpulDatabaseRevision = 0;
+ }else
+ *lpulDatabaseRevision = atoui(lpDBRow[0]);
+
+
+exit:
+ if (lpResult != NULL)
+ FreeResult(lpResult);
+ return er;
+}
+
+/**
+ * Update the database to the current version.
+ *
+ * @param[in] bForceUpdate possebly force upgrade
+ * @param[out] strReport error message
+ *
+ * @return Zarafa error code
+ */
+ECRESULT ECDatabaseMySQL::UpdateDatabase(bool bForceUpdate, std::string &strReport)
+{
+ ECRESULT er = erSuccess;
+ bool bUpdated = false;
+ bool bSkipped = false;
+ unsigned int ulDatabaseRevisionMin = 0;
+ zcp_versiontuple stored_ver;
+ zcp_versiontuple program_ver(PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_MICRO, PROJECT_VERSION_REVISION, Z_UPDATE_LAST);
+ int cmp;
+
+ er = GetDatabaseVersion(&stored_ver);
+ if(er != erSuccess)
+ goto exit;
+
+
+ er = GetFirstUpdate(&ulDatabaseRevisionMin);
+ if(er != erSuccess)
+ goto exit;
+
+ //default error
+ strReport = "Unable to upgrade zarafa from version " +
+ stored_ver.stringify() + " to " + program_ver.stringify();
+
+ // Check version
+ cmp = stored_ver.compare(program_ver);
+ if (cmp == 0 && stored_ver.v_schema == Z_UPDATE_LAST) {
+ // up to date
+ goto exit;
+ } else if (cmp > 0) {
+ // Start a old server with a new database
+ strReport = "Database version (" + stored_ver.stringify(',') +
+ ") is newer than the server version (" + program_ver.stringify(',') + ")";
+ er = ZARAFA_E_INVALID_VERSION;
+ goto exit;
+ }
+
+ this->m_bForceUpdate = bForceUpdate;
+
+ if (bForceUpdate)
+ ec_log_warn("Manually forced the database upgrade because the option \"--force-database-upgrade\" was given.");
+
+ // Loop throught the update list
+ for (size_t i = ulDatabaseRevisionMin;
+ i < ARRAY_SIZE(sUpdateList); ++i)
+ {
+ if ( (ulDatabaseRevisionMin > 0 && IsUpdateDone(sUpdateList[i].ulVersion) == hrSuccess) ||
+ (sUpdateList[i].ulVersionMin != 0 && stored_ver.v_schema >= sUpdateList[i].ulVersion &&
+ stored_ver.v_schema >= sUpdateList[i].ulVersionMin) ||
+ (sUpdateList[i].ulVersionMin != 0 && IsUpdateDone(sUpdateList[i].ulVersionMin, PROJECT_VERSION_REVISION) == hrSuccess))
+ {
+ // Update already done, next
+ continue;
+ }
+
+ ec_log_info("Start: %s", sUpdateList[i].lpszLogComment);
+
+ er = Begin();
+ if(er != erSuccess)
+ goto exit;
+
+ bSkipped = false;
+ er = sUpdateList[i].lpFunction(this);
+ if (er == ZARAFA_E_IGNORE_ME) {
+ bSkipped = true;
+ er = erSuccess;
+ } else if (er == ZARAFA_E_USER_CANCEL) {
+ goto exit; // Reason should be logged in the update itself.
+ } else if (er != hrSuccess) {
+ Rollback();
+ ec_log_err("Failed: Rollback database");
+ goto exit;
+ }
+
+ er = UpdateDatabaseVersion(sUpdateList[i].ulVersion);
+ if(er != erSuccess)
+ goto exit;
+
+ er = Commit();
+ if(er != erSuccess)
+ goto exit;
+ ec_log_notice("%s: %s", bSkipped ? "Skipped" : "Done", sUpdateList[i].lpszLogComment);
+ bUpdated = true;
+ }
+
+ // Ok, no changes for the database, but for update history we add a version record
+ if(bUpdated == false) {
+ // Update version table
+ er = UpdateDatabaseVersion(Z_UPDATE_LAST);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+exit:
+
+ return er;
+}
+
+ECRESULT ECDatabaseMySQL::UpdateDatabaseVersion(unsigned int ulDatabaseRevision)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT result;
+ bool have_micro;
+
+ /* Check for "micro" column (present in v64+) */
+ er = DoSelect("SELECT databaserevision FROM versions WHERE databaserevision>=64 LIMIT 1", &result);
+ if (er != erSuccess)
+ goto exit;
+ have_micro = GetNumRows(result) > 0;
+ FreeResult(result);
+
+ // Insert version number
+ strQuery = "INSERT INTO versions (major, minor, ";
+ if (have_micro)
+ strQuery += "micro, ";
+ strQuery += "revision, databaserevision, updatetime) VALUES(";
+ strQuery += stringify(PROJECT_VERSION_MAJOR) + std::string(", ") + stringify(PROJECT_VERSION_MINOR) + std::string(", ");
+ if (have_micro)
+ strQuery += stringify(PROJECT_VERSION_MICRO) + std::string(", ");
+ strQuery += std::string("'") + std::string(PROJECT_SVN_REV_STR) + std::string("', ") + stringify(ulDatabaseRevision) + ", FROM_UNIXTIME("+stringify(time(NULL))+") )";
+
+ er = DoInsert(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+/**
+ * Validate all database tables
+*/
+ECRESULT ECDatabaseMySQL::ValidateTables()
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ list<std::string> listTables;
+ list<std::string> listErrorTables;
+ list<std::string>::const_iterator iterTables;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ er = DoSelect("SHOW TABLES", &lpResult);
+ if(er != erSuccess) {
+ ec_log_err("Unable to get all tables from the mysql database. %s", GetError());
+ goto exit;
+ }
+
+ // Get all tables of the database
+ while( (lpDBRow = FetchRow(lpResult))) {
+ if (lpDBRow == NULL || lpDBRow[0] == NULL) {
+ ec_log_err("Wrong table information.");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ listTables.insert(listTables.end(), lpDBRow[0]);
+ }
+ if(lpResult) FreeResult(lpResult);
+ lpResult = NULL;
+
+ for (iterTables = listTables.begin(); iterTables != listTables.end(); ++iterTables) {
+ er = DoSelect("CHECK TABLE " + *iterTables, &lpResult);
+ if(er != erSuccess) {
+ ec_log_err("Unable to check table \"%s\"", iterTables->c_str());
+ goto exit;
+ }
+
+ lpDBRow = FetchRow(lpResult);
+ if (lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL) {
+ ec_log_err("Wrong check table information.");
+ er = ZARAFA_E_DATABASE_ERROR;
+ goto exit;
+ }
+
+ ec_log_info("%30s | %15s | %s", lpDBRow[0], lpDBRow[2], lpDBRow[3]);
+ if (strcmp(lpDBRow[2], "error") == 0)
+ listErrorTables.insert(listErrorTables.end(), lpDBRow[0]);
+
+ if(lpResult) FreeResult(lpResult);
+ lpResult = NULL;
+ }
+
+ if (!listErrorTables.empty())
+ {
+ ec_log_notice("Rebuilding tables.");
+#ifdef HAVE_OFFLINE_SUPPORT
+ ECDBUpdateProgress *lpProgress = NULL;
+ unsigned int cUpdate = 0;
+
+ ECDBUpdateProgress::GetInstance(listErrorTables.size(), this, &lpProgress);
+#endif
+ for (iterTables = listErrorTables.begin();
+ iterTables != listErrorTables.end(); ++iterTables) {
+#ifdef HAVE_OFFLINE_SUPPORT
+ if (lpProgress) {
+ er = lpProgress->Start(cUpdate);
+ if(er != erSuccess) {
+ ec_log_err("Rebuild of tables canceled by user.");
+ goto exit;
+ }
+ }
+#endif
+
+ er = DoUpdate("ALTER TABLE " + *iterTables + " FORCE");
+ if(er != erSuccess) {
+ ec_log_crit("Unable to fix table \"%s\"", iterTables->c_str());
+ break;
+ }
+#ifdef HAVE_OFFLINE_SUPPORT
+ if (lpProgress)
+ lpProgress->Finish(cUpdate++);
+#endif
+ }
+#ifdef HAVE_OFFLINE_SUPPORT
+ ECDBUpdateProgress::DestroyInstance();
+#endif
+ if(er != erSuccess) {
+ ec_log_crit("Rebuild tables failed. Error code 0x%08x", er);
+ } else {
+ ec_log_notice("Rebuilding tables done.");
+ }
+ }// if (!listErrorTables.empty())
+
+exit:
+ if(lpResult)
+ FreeResult(lpResult);
+
+ return er;
+}
diff --git a/ECDatabaseUpdate.cpp b/ECDatabaseUpdate.cpp
new file mode 100644
index 000000000000..8fc8bdebc1a9
--- /dev/null
+++ b/ECDatabaseUpdate.cpp
@@ -0,0 +1,2719 @@
+/*
+ * Copyright 2005 - 2015 Zarafa B.V. and its licensors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <zarafa/platform.h>
+
+#include "ECDatabase.h"
+#include "ECDatabaseUpdate.h"
+
+#include <zarafa/stringutil.h>
+
+#include <zarafa/ECDefs.h>
+#include "ECDBDef.h"
+#include "ECUserManagement.h"
+
+#include <zarafa/ecversion.h>
+
+#include <mapidefs.h>
+#include <mapitags.h>
+#include "ECConversion.h"
+#include "SOAPUtils.h"
+#include "ECSearchFolders.h"
+
+#include "ZarafaICS.h"
+
+#include <zarafa/charset/convert.h>
+#include "ECStringCompat.h"
+#include "ECMAPI.h"
+
+#include <zlib.h>
+#include <zarafa/mapiext.h>
+#include <edkmdb.h>
+
+#ifdef HAVE_OFFLINE_SUPPORT
+#include "ECDBUpdateProgress.h"
+
+#define PROGRESS_INIT(_curupdate) \
+ ECDBUpdateProgress *__lpProgress = NULL; \
+ const unsigned int __ulCurUpdate = (_curupdate); \
+ er = ECDBUpdateProgress::GetInstance(Z_UPDATE_CONVERT_NAMES, lpDatabase, &__lpProgress); \
+ if (er == erSuccess) \
+ er = __lpProgress->Start(__ulCurUpdate); \
+ if (er != erSuccess) \
+ goto exit;
+
+#define PROGRESS_DONE \
+ er = __lpProgress->Finish(__ulCurUpdate); \
+ if (er != erSuccess) \
+ goto exit;
+
+#define INTERMEDIATE_PROGRESS(_cur, _total) \
+ er = __lpProgress->SetIntermediateProgress((_cur), (_total)); \
+ if (er != erSuccess) \
+ goto exit;
+
+#define INTERMEDIATE_PROGRESS_(_progress) \
+ er = __lpProgress->SetIntermediateProgress((_progress)); \
+ if (er != erSuccess) \
+ goto exit;
+
+#else
+
+#define PROGRESS_INIT(...)
+#define PROGRESS_DONE
+#define INTERMEDIATE_PROGRESS(...)
+#define INTERMEDIATE_PROGRESS_(...)
+
+#endif
+
+extern int searchfolder_restart_required; // HACK
+
+/*
+ Zarafa database upgrade
+
+ Version 4.20 (not include)
+ * Add table object
+ * Add table objectproperty
+ * Add table objectrelation
+ * Change user table structure
+ * Converting users and group
+
+ Optional 4.20 / 5.0 (not include)
+ * Change database engine to INNODB
+ * Remove key val_string on the table mvproperties and properties
+ * Change the primary key, "ht" key and "hierarchyid" key on the hierarchy table
+ * Change externid to field to VARBINARY(255) and add externid key
+
+ Version 4.21 (not include)
+ * change the "parent" key on the hierarchy table
+
+ Version 5.00 (not included)
+ * Add column storeid in properties table and update the ids
+ * Add freebusy folders in public store
+ * Set the permissions on the free/busy folders
+
+ Version 5.10
+ * Add table version
+ * Add table searchfolders
+ * Update non-login from 10 to 1
+ * (Beta update) Add flags in search table
+ * rebuild searchfolders
+
+ Version 5.20
+ * Create table changes
+ * Create table syncs
+ * Create table indexedproperties
+ * Create table settings
+ * Insert server guid into settings table
+ * Create from object id a sourcekeys and add them into indexedproperties
+
+ Version 6.00
+ * Create from object id an entryid and add them into indexedproperties
+ * Update Search criteria
+ * Update users table to have 'type' field instead of 'isgroup' and 'isnonactive'
+ * Update users table to have 'signature' field
+ * Add source_key_auto_increment setting
+ * Fix unique key in users table
+
+ Version 6.10
+ * Add company column
+ * Add company in user table
+ * Add company in objectproperty table
+
+ Version 6.20
+ * Move public folders and remove favorites
+
+ Version 6.30
+ * Add externid column to object table (changed between beta's)
+ * Add reference table for Single Instance Attachments
+ * Add distributed lock when upgrading to 6.30 (distributed only on clean 6.30)
+ * Add abchanges table to hold ab sourcekeys (since they don't fit in the changes table anymore)
+ * Set tag column in singleinstance to correct tag value (beta's have wrong value)
+
+ Version 6.40
+ * Rename object_type columns to objectclass, and fix their contents with the new defined values (in 2 steps)
+ * Add objectmvproperties table (for offline synced addressbook details)
+ * Add syncedmessages table for keeping track of messages that were synchronized with a restriction
+ * Update the primary key on the 'outgoingqueue' table
+ * Update the primary key on the 'acl' table
+ * Update externid in users and object table to be blob, so mysql 4.1 leaves trailing 0's
+ * Update changes table primary key
+ * Update mvproperties table primary key
+ * Update objectclass for DB plugin groups to be security enabled
+ * Update objectrelation table to switch send-as settings
+
+ Version 7.00
+ * Print error howto "convert", or if admin forced do the upgrade of tables with text fields to unicode.
+ * Update stores table to store the username in a char field.
+ * Update rules xml blobs to unicode.
+ * Update searchfolder xml blobs to unicode.
+
+ Version 7.0.1
+ * update receive folder to unicode and increase the messageclass column size
+
+ Version 7.1.0
+ * update WLINK entries to new record key format
+
+ Version independed
+ * Add setting for IMAP
+ * Change primary key in changetable and add an extra move key
+ * Force addressbook resync
+*/
+
+struct SObject {
+ SObject(unsigned int id, unsigned int type) {ulId = id; ulType = type;}
+ bool operator<(const SObject &rhs) const {return (ulId < rhs.ulId || (ulId == rhs.ulId && ulType < rhs.ulType));}
+ unsigned int ulId;
+ unsigned int ulType;
+};
+
+struct SRelation {
+ SRelation(unsigned int objectId, unsigned int parentObjectId, unsigned int relationType) {
+ ulObjectId = objectId; ulParentObjectId = parentObjectId; ulRelationType = relationType;
+ }
+ unsigned int ulObjectId;
+ unsigned int ulParentObjectId;
+ unsigned int ulRelationType;
+};
+
+// 1
+ECRESULT UpdateDatabaseCreateVersionsTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_VERSIONS);
+
+ return er;
+}
+
+// 2
+ECRESULT UpdateDatabaseCreateSearchFolders(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_SEARCHRESULTS);
+
+ return er;
+}
+
+// 3
+ECRESULT UpdateDatabaseFixUserNonActive(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("UPDATE users SET nonactive=1 WHERE nonactive=10");
+
+ return er;
+}
+
+// 4
+ECRESULT UpdateDatabaseCreateSearchFoldersFlags(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE searchresults ADD COLUMN flags int(11) unsigned NOT NULL default '0'");
+
+ return er;
+}
+
+// 5
+ECRESULT UpdateDatabasePopulateSearchFolders(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ searchfolder_restart_required = 1;
+
+ return er;
+}
+
+// 6
+ECRESULT UpdateDatabaseCreateChangesTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_CHANGES);
+
+ return er;
+}
+
+// 7
+ECRESULT UpdateDatabaseCreateSyncsTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_SYNCS);
+
+ return er;
+}
+
+// 8
+ECRESULT UpdateDatabaseCreateIndexedPropertiesTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_INDEXED_PROPERTIES);
+
+ return er;
+}
+
+// 9
+ECRESULT UpdateDatabaseCreateSettingsTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_SETTINGS);
+
+ return er;
+}
+
+ECRESULT InsertServerGUID(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ GUID guid;
+
+ if (CoCreateGuid(&guid) != S_OK) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("InsertServerGUID(): CoCreateGuid failed");
+ goto exit;
+ }
+
+ er = lpDatabase->DoInsert("INSERT INTO `settings` VALUES ('server_guid', " + lpDatabase->EscapeBinary((unsigned char *)&guid, sizeof(GUID)) + ")");
+ if(er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 10
+ECRESULT UpdateDatabaseCreateServerGUID(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = InsertServerGUID(lpDatabase);
+
+ return er;
+}
+
+// 11
+ECRESULT UpdateDatabaseCreateSourceKeys(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLenths = NULL;
+
+
+ strQuery = "SELECT `value` FROM `settings` WHERE `name` = 'server_guid'";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ lpDBLenths = lpDatabase->FetchRowLengths(lpResult);
+ if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBLenths == NULL || lpDBLenths[0] != sizeof(GUID)) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseCreateSourceKeys(): row or columns NULL");
+ goto exit;
+ }
+
+ //Insert source keys for folders
+ strQuery = "INSERT INTO indexedproperties (tag, hierarchyid, val_binary) SELECT 26080, h.id, CONCAT(" + lpDatabase->EscapeBinary((unsigned char*)lpDBRow[0], sizeof(GUID));
+ strQuery += ", CHAR(h.id&0xFF, h.id>>8&0xFF, h.id>>16&0xFF, h.id>>24&0xFF))";
+ strQuery += " FROM hierarchy AS h WHERE h.type = 3";
+
+ er = lpDatabase->DoInsert(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ //Insert source keys for messages
+ strQuery = "INSERT INTO indexedproperties (tag, hierarchyid, val_binary) SELECT 26080, h.id, CONCAT(" + lpDatabase->EscapeBinary((unsigned char*)lpDBRow[0], sizeof(GUID));
+ strQuery += ", CHAR(h.id&0xFF, h.id>>8&0xFF, h.id>>16&0xFF, h.id>>24&0xFF))";
+ strQuery += " FROM hierarchy AS h LEFT JOIN hierarchy AS p ON h.parent = p.id WHERE h.type = 5 AND p.type = 3";
+
+ er = lpDatabase->DoInsert(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+exit:
+ if(lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 12
+ECRESULT UpdateDatabaseConvertEntryIDs(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLenths = NULL;
+ int i, nStores;
+
+ strQuery = "SELECT `guid`, `hierarchy_id` FROM `stores`";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ nStores = lpDatabase->GetNumRows(lpResult);
+
+ ec_log_notice(" Stores to convert: %d", nStores);
+
+ for (i = 0; i < nStores; ++i) {
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ lpDBLenths = lpDatabase->FetchRowLengths(lpResult);
+ if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL ||
+ lpDBLenths == NULL || lpDBLenths[0] != sizeof(GUID) )
+ {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_crit(" Failed to convert store \"%s\"", (lpDBRow && lpDBRow[1])?lpDBRow[1]:"Unknown");
+
+ if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBLenths == NULL || lpDBLenths[0] != sizeof(GUID) )
+ ec_log_crit(" The table \"stores\" includes a wrong GUID");
+ else
+ ec_log_crit(" Check the data in table \"stores\"");
+
+ goto exit;
+ }
+
+ ec_log_notice(" Converting entryids of store %d", atoui(lpDBRow[1]));
+
+ er = CreateRecursiveStoreEntryIds(lpDatabase, atoui(lpDBRow[1]), (unsigned char*)lpDBRow[0]);
+ if(er != erSuccess) {
+ ec_log_crit(" Failed to convert store %d", atoui(lpDBRow[1]));
+ goto exit;
+ }
+
+ }
+
+exit:
+ if(lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+ECRESULT CreateRecursiveStoreEntryIds(ECDatabase *lpDatabase, unsigned int ulStoreHierarchyId, unsigned char* lpStoreGuid)
+{
+ ECRESULT er = erSuccess;
+ string strQuery, strInsertQuery, strDefaultQuery;
+ string strInValues;
+ DB_RESULT lpDBResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ // FIXME: use ECListInt and ECListIntIterator (in ECGenericObjectTable.h)
+ std::list<unsigned int> lstFolders; // The list of folders
+ std::list<unsigned int>::iterator iterFolders;
+
+ // Insert the entryids
+ strDefaultQuery = "REPLACE INTO indexedproperties (tag, hierarchyid, val_binary) ";
+ strDefaultQuery+= "SELECT 0x0FFF, h.id, CONCAT('\\0\\0\\0\\0', "+ lpDatabase->EscapeBinary(lpStoreGuid, sizeof(GUID));
+ strDefaultQuery+= ", '\\0\\0\\0\\0', CHAR(h.type&0xFF, h.type>>8&0xFF, h.type>>16&0xFF, h.type>>24&0xFF), ";
+ strDefaultQuery+= "CHAR(h.id&0xFF, h.id>>8&0xFF, h.id>>16&0xFF, h.id>>24&0xFF), '\\0\\0\\0\\0')";
+ strDefaultQuery+= " FROM hierarchy AS h WHERE h.id IN ";
+
+ // Add the master id
+ lstFolders.push_back( ulStoreHierarchyId );
+
+ iterFolders = lstFolders.begin();
+ while (iterFolders != lstFolders.end()) {
+
+ // Make parent list
+ strInValues.clear();
+
+ while(iterFolders != lstFolders.end()) {
+
+ strInValues += stringify(*iterFolders);
+ ++iterFolders;
+ if (strDefaultQuery.size() + strInValues.size() * 2 > lpDatabase->GetMaxAllowedPacket())
+ break;
+ if(iterFolders != lstFolders.end())
+ strInValues += ",";
+ }
+
+ // Remove the added entries
+ lstFolders.erase(lstFolders.begin(), iterFolders);
+
+ // Insert the entryids
+ er = lpDatabase->DoInsert(strDefaultQuery + "(" + strInValues + ")");
+ if(er != erSuccess)
+ goto exit;
+
+
+ // Get the new parents
+ strQuery= "SELECT id FROM hierarchy WHERE parent IN ( "+strInValues+")";
+
+ er = lpDatabase->DoSelect(strQuery, &lpDBResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while(true) {
+ lpDBRow = lpDatabase->FetchRow(lpDBResult);
+
+ if (lpDBRow == NULL)
+ break;
+
+ if (lpDBRow[0] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("CreateRecursiveStoreEntryIds(): column is NULL");
+ goto exit;
+ }
+
+ lstFolders.push_back(atoui(lpDBRow[0]));
+ }
+
+ if (lpDBResult) {
+ lpDatabase->FreeResult(lpDBResult);
+ lpDBResult = NULL;
+ }
+ iterFolders = lstFolders.begin();
+ } //while
+
+exit:
+ return er;
+}
+
+// 13
+ECRESULT UpdateDatabaseSearchCriteria(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+ DB_RESULT lpDBResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ unsigned int ulStoreLast = 0;
+ unsigned int ulStoreId = 0;
+
+ struct searchCriteria *lpNewSearchCriteria = NULL;
+
+ // Search for all folders with PR_EC_SEARCHCRIT that are not deleted
+ strQuery = "SELECT properties.storeid, hierarchy.id, properties.val_string FROM hierarchy LEFT JOIN properties ON properties.hierarchyid=hierarchy.id AND properties.tag=" + stringify(PROP_ID(PR_EC_SEARCHCRIT)) +" AND properties.type=" + stringify(PROP_TYPE(PR_EC_SEARCHCRIT)) + " WHERE hierarchy.type=3 AND hierarchy.flags=2 ORDER BY properties.storeid";
+
+ er = lpDatabase->DoSelect(strQuery, &lpDBResult);
+ if(er != erSuccess)
+ goto exit;
+
+ while(true) {
+ lpDBRow = lpDatabase->FetchRow(lpDBResult);
+ if (lpDBRow == NULL)
+ break;
+
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) continue;
+
+ ulStoreId = atoui(lpDBRow[0]);
+
+ if (ulStoreLast != ulStoreId) {
+ ec_log_notice(" Convert store %d", ulStoreId);
+ ulStoreLast = ulStoreId;
+ }
+
+ // convert the entryidlist
+ if (ConvertSearchCriteria52XTo6XX(lpDatabase, lpDBRow[2], &lpNewSearchCriteria) != erSuccess)
+ {
+ ec_log_crit(" WARNING: Unable to convert the search criteria of folder %d", atoui(lpDBRow[1]));
+ goto next;
+ }
+
+ // Save criteria in new format
+ er = ECSearchFolders::SaveSearchCriteria(lpDatabase, atoui(lpDBRow[0]), atoui(lpDBRow[1]), lpNewSearchCriteria);
+ if (er != erSuccess) {
+ ec_log_crit(" Unable to save the new search criteria of folder %d", atoui(lpDBRow[1]));
+ goto exit;
+ }
+next: //Free
+ if (lpNewSearchCriteria) {
+ FreeSearchCriteria(lpNewSearchCriteria);
+ lpNewSearchCriteria = NULL;
+ }
+ }
+
+exit:
+
+ if (lpDBResult)
+ lpDatabase->FreeResult(lpDBResult);
+
+ if (lpNewSearchCriteria)
+ FreeSearchCriteria(lpNewSearchCriteria);
+
+ return er;
+}
+
+// 14
+ECRESULT UpdateDatabaseAddUserObjectType(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ /*
+ * First we create the object_type column and initialize the values
+ * based on the isgroup and nonactive columns. Once that is done we should
+ * drop the columns. This will make the users table use the same format as
+ * the DBUserPlugin which already made use of the object_type column.
+ */
+ er = lpDatabase->DoUpdate("ALTER TABLE users ADD COLUMN object_type int(11) NOT NULL default '0'");
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("UPDATE users SET object_type=5 WHERE nonactive != 0"); /* USEROBJECT_TYPE_NONACTIVE */
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("UPDATE users SET object_type=2 WHERE isgroup != 0"); /* USEROBJECT_TYPE_GROUP */
+ if(er != erSuccess)
+ goto exit;
+
+ /*
+ * All other entries should be considered as users.
+ * This is safe since there are at this time no other valid object types.
+ */
+ er = lpDatabase->DoUpdate("UPDATE users SET object_type=1 WHERE object_type = 0"); /* USEROBJECT_TYPE_USER */
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE users DROP COLUMN nonactive");
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE users DROP INDEX externid, DROP COLUMN isgroup, ADD INDEX externid (`externid`, `object_type`)");
+ if(er != erSuccess)
+ goto exit;
+
+ /*
+ * Another change is that for the DB plugin, the 'objects' table should now show type 5 for nonactive users (instead of 1)
+ */
+
+ er = lpDatabase->DoUpdate("UPDATE object SET objecttype=5 WHERE id IN (SELECT objectid FROM objectproperty WHERE propname='isnonactive' AND value != 0)");
+ if(er != erSuccess)
+ goto exit;
+
+exit:
+
+ return er;
+}
+
+// 15
+ECRESULT UpdateDatabaseAddUserSignature(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE users ADD COLUMN signature varchar(255) NOT NULL default '0'");
+
+ return er;
+}
+
+// 16
+ECRESULT UpdateDatabaseAddSourceKeySetting(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("INSERT INTO `settings` VALUES ('source_key_auto_increment' , (SELECT CHAR(MAX(`id`)&0xFF, MAX(`id`)>>8&0xFF, MAX(`id`)>>16&0xFF, MAX(`id`)>>24&0xFF, 0x00, 0x00, 0x00, 0x00) FROM `hierarchy`))");
+
+ return er;
+}
+
+// 17
+ECRESULT UpdateDatabaseRestrictExternId(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ /*
+ * The previous upgrade script created an INDEX instead of an UNIQUE INDEX,
+ * this will result in incorrect behavior when multiple entries with the
+ * same externid and object_type are inserted.
+ */
+ er = lpDatabase->DoUpdate("ALTER TABLE users DROP INDEX externid, ADD UNIQUE INDEX externid (`externid`, `object_type`)");
+
+ return er;
+}
+
+// 18
+ECRESULT UpdateDatabaseAddUserCompany(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE users ADD COLUMN company int(11) NOT NULL default '0'");
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoInsert("INSERT INTO `users` (`externid`, `object_type`, `signature`, `company`) VALUES (NULL, 4, '', 0)");
+ if(er != erSuccess)
+ goto exit;
+exit:
+ return er;
+}
+
+// 19
+ECRESULT UpdateDatabaseAddObjectRelationType(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE objectrelation ADD COLUMN relationtype tinyint(11) unsigned NOT NULL");
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE objectrelation DROP PRIMARY KEY");
+ if(er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE objectrelation ADD PRIMARY KEY (`objectid`, `parentobjectid`, `relationtype`)");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("UPDATE objectrelation SET relationtype = " + stringify(OBJECTRELATION_GROUP_MEMBER));
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+
+ return er;
+}
+
+// 20
+ECRESULT UpdateDatabaseDelUserCompany(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoDelete(
+ "DELETE FROM `users` "
+ "WHERE externid IS NULL "
+ "AND object_type = 4");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoDelete(
+ "DELETE FROM `objectproperty` "
+ "WHERE `propname` = 'companyid' "
+ "AND `value` = 'default'");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 21
+ECRESULT UpdateDatabaseAddCompanyToStore(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE stores ADD COLUMN user_name varbinary(255) NOT NULL default ''");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("ALTER TABLE stores ADD COLUMN company smallint(11) NOT NULL default 0");
+ if (er != erSuccess)
+ goto exit;
+
+ /*
+ * The user_name column should contain the actual username, but resolving the username for each
+ * entry will be quite tiresome without much to gain. Instead we just push the userid as
+ * username and only force the real username for new entries.
+ * The company column contains the company id to which the user belongs, we can fetch this
+ * information from the 'users' table. Note that this will always be correct regardless of
+ * hosted is enabled or disabled since the default value in the 'users' table is 0.
+ */
+ er = lpDatabase->DoUpdate("UPDATE stores SET user_name = user_id, company = IFNULL( (SELECT company FROM users WHERE users.id = user_id), 0)");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 22
+ECRESULT UpdateDatabaseAddIMAPSequenceNumber(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+
+ er = lpDatabase->DoSelect("SELECT * FROM settings WHERE name='imapseq'", &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ if(lpDatabase->GetNumRows(lpResult) == 0) {
+ er = lpDatabase->DoInsert("INSERT INTO settings (name, value) VALUES('imapseq',(SELECT max(id)+1 FROM hierarchy))");
+ if(er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if(lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 23
+ECRESULT UpdateDatabaseKeysChanges(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ BOOL bFirst = TRUE;
+ unsigned int ulRows = 0;
+
+ // Remove duplicates
+ do {
+ bFirst = TRUE;
+
+ er = lpDatabase->DoSelect("SELECT id FROM changes GROUP BY parentsourcekey, change_type, sourcekey HAVING COUNT(*) > 1", &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ ulRows = lpDatabase->GetNumRows(lpResult);
+ if(ulRows > 0) {
+ strQuery = "DELETE FROM changes WHERE id IN (";
+ while(true) {
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+
+ if (lpDBRow == NULL)
+ break;
+
+ if (lpDBRow[0] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseKeysChanges(): column is NULL");
+ goto exit;
+ }
+
+ if (!bFirst)
+ strQuery += ",";
+
+ bFirst = FALSE;
+
+ strQuery += lpDBRow[0];
+ }
+ strQuery += ")";
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+ if(lpResult) {
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+ }
+
+ }while(ulRows > 0);
+
+ // Change index
+ er = lpDatabase->DoUpdate("ALTER TABLE changes DROP PRIMARY KEY, DROP KEY `duplicate`, ADD PRIMARY KEY(`parentsourcekey`,`change_type`,`sourcekey`), ADD UNIQUE KEY `changeid` (`id`), ADD KEY `moved` (`moved_from`)");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ if(lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 24, Move public folders and remove favorites
+ECRESULT UpdateDatabaseMoveFoldersInPublicFolder(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ unsigned int ulStoreId = 0;
+ unsigned int ulSubtreeFolder = 0;
+ unsigned int ulPublicFolder = 0;
+ unsigned int ulFavoriteFolder = 0;
+ unsigned int ulAffRows = 0;
+ bool bUseStoreAcls = false;
+
+ // Find public stores (For every company)
+ // Join the subtree, publicfolders and favorites entryid
+
+ strQuery ="SELECT s.hierarchy_id, isub.hierarchyid, ipf.hierarchyid, iff.hierarchyid FROM users AS u "
+ "JOIN stores AS s ON s.user_id=u.id "
+ "JOIN properties AS psub ON "
+ "psub.tag = 0x35E0 AND psub.type = 0x102 AND psub.storeid = s.hierarchy_id " // PR_IPM_SUBTREE_ENTRYID
+ "JOIN indexedproperties AS isub ON "
+ "isub.tag=0xFFF AND isub.val_binary = psub.val_binary "
+ "LEFT JOIN properties AS pf ON "
+ "pf.tag = 0x6631 AND pf.type = 0x102 AND pf.storeid = s.hierarchy_id " //PR_IPM_PUBLIC_FOLDERS_ENTRYID
+ "LEFT JOIN indexedproperties AS ipf ON "
+ "ipf.tag=0xFFF AND ipf.val_binary = pf.val_binary "
+ "LEFT JOIN properties AS ff ON "
+ "ff.tag = 0x6630 AND ff.type = 0x102 AND ff.storeid = s.hierarchy_id " //PR_IPM_FAVORITES_ENTRYID
+ "LEFT JOIN indexedproperties AS iff ON "
+ "iff.tag=0xFFF AND iff.val_binary = ff.val_binary "
+ "WHERE u.object_type=4 OR u.id = 1"; // object_type=USEROBJECT_TYPE_COMPANY or id=ZARAFA_UID_EVERYONE
+
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if(er != erSuccess)
+ goto exit;
+
+ while(true) {
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+
+ if (lpDBRow == NULL)
+ break;
+
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
+ ec_log_crit(" Skip store: Unable to get the store information for storeid \"%s\"", (lpDBRow[0])?lpDBRow[0]:"Unknown");
+ continue;
+ }
+
+ ulAffRows = 0;
+ bUseStoreAcls = false;
+ ulStoreId = atoui(lpDBRow[0]);
+ ulSubtreeFolder = atoui(lpDBRow[1]);
+ ulPublicFolder = (lpDBRow[2])?atoui(lpDBRow[2]):0;
+ ulFavoriteFolder = (lpDBRow[3])?atoui(lpDBRow[3]):0;
+
+ if (ulPublicFolder > 0) {
+
+ // Move the publicfolder folders and messages to the subtree
+ strQuery ="UPDATE hierarchy SET parent="+stringify(ulSubtreeFolder)+" WHERE parent="+stringify(ulPublicFolder);
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Mark the old folder as deleted
+ strQuery = "UPDATE hierarchy SET flags=flags|1024 WHERE id="+stringify(ulPublicFolder);
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Remove acl's from the subtree folder
+ strQuery = "DELETE FROM acl WHERE hierarchy_id="+stringify(ulSubtreeFolder);
+ er = lpDatabase->DoDelete(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Move the Public folder acls to the subtree folder
+ strQuery = "UPDATE acl SET hierarchy_id="+stringify(ulSubtreeFolder)+" WHERE hierarchy_id="+stringify(ulPublicFolder);
+ er = lpDatabase->DoUpdate(strQuery, &ulAffRows);
+ if(er != erSuccess)
+ goto exit;
+
+ if (ulAffRows == 0)
+ bUseStoreAcls = true;
+
+ } else {
+ bUseStoreAcls = true;
+ }
+
+ if (bUseStoreAcls) {
+ // Move the store acls to the subtree folder
+ strQuery = "UPDATE acl SET hierarchy_id="+stringify(ulSubtreeFolder)+" WHERE hierarchy_id="+stringify(ulStoreId);
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ if(ulFavoriteFolder > 0) {
+
+ // Move the favoritefolder folders and messages to the subtree
+ strQuery ="UPDATE hierarchy SET parent="+stringify(ulSubtreeFolder)+" WHERE parent="+stringify(ulFavoriteFolder);
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Mark the old folder as deleted
+ strQuery = "UPDATE hierarchy SET flags=flags|1024 WHERE id="+stringify(ulFavoriteFolder);
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Remove acl's from the favorite folder
+ strQuery = "DELETE FROM acl WHERE hierarchy_id="+stringify(ulFavoriteFolder);
+ er = lpDatabase->DoDelete(strQuery);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ // Remove acl's from the store
+ strQuery = "DELETE FROM acl WHERE hierarchy_id="+stringify(ulStoreId);
+ er = lpDatabase->DoDelete(strQuery);
+ if(er != erSuccess)
+ goto exit;
+
+ // Remove the unused properties
+ strQuery = "DELETE FROM properties "
+ "WHERE (tag = 0x6631 AND type=0x102 AND storeid = "+stringify(ulStoreId) + ") OR " //PR_IPM_PUBLIC_FOLDERS_ENTRYID
+ "(tag = 0x6630 AND type = 0x102 AND storeid = "+stringify(ulStoreId) + ")";//PR_IPM_FAVORITES_ENTRYID
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if(er != erSuccess)
+ goto exit;
+ }
+
+ if(lpResult) {
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+ }
+
+
+exit:
+ if(lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 25
+ECRESULT UpdateDatabaseAddExternIdToObject(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+ unsigned int ulNewId = 0;
+ unsigned int ulNewParentId = 0;
+ bool bFirstResult;
+
+ std::list<SObject> sObjectList;
+ std::list<SObject>::const_iterator sObjectIter;
+
+ std::map<SObject,unsigned int> sObjectMap;
+ std::map<SObject,unsigned int>::const_iterator sObjectMapIter;
+
+ std::list<SRelation> sRelationList;
+ std::list<SRelation>::const_iterator sRelationIter;
+
+#define Z_TABLEDEF_OBJECT_R630 "CREATE TABLE object ( \
+ `id` int(11) unsigned NOT NULL auto_increment, \
+ `externid` varbinary(255), \
+ `objecttype` int(11) unsigned NOT NULL default '0', \
+ PRIMARY KEY (`id`, `objecttype`), \
+ UNIQUE KEY id (`id`), \
+ UNIQUE KEY externid (`externid`, `objecttype`) \
+ ) ENGINE=InnoDB;"
+
+ // Create the new object table.
+ strQuery = Z_TABLEDEF_OBJECT_R630;
+ strQuery.replace(strQuery.find("object"), 6, "object_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+
+ // Create the new objectproperty table.
+ strQuery = Z_TABLEDEF_OBJECT_PROPERTY;
+ strQuery.replace(strQuery.find("objectproperty"), 14, "objectproperty_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+
+ // Create the new objectrelation table.
+ strQuery = Z_TABLEDEF_OBJECT_RELATION;
+ strQuery.replace(strQuery.find("objectrelation"), 14, "objectrelation_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+
+ // Create a list of all current objects from the object table.
+ strQuery = "SELECT id, objecttype FROM object ORDER BY objecttype";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err(" object table contains invalid NULL records");
+ goto exit;
+ }
+
+ sObjectList.push_back(SObject(atoi(lpDBRow[0]), atoi(lpDBRow[1])));
+ }
+
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+
+ // Recreate the objects in the object_temp table and on the fly create the queries to regenerate
+ // their properties in the objectpropert_temp table.
+ for (sObjectIter = sObjectList.begin(); sObjectIter != sObjectList.end(); ++sObjectIter) {
+ strQuery = (string)"INSERT INTO object_temp (objecttype, externid) VALUES (" + stringify(sObjectIter->ulType) + ", '" + stringify(sObjectIter->ulId) + "')";
+ er = lpDatabase->DoInsert(strQuery, &ulNewId);
+ if (er != erSuccess)
+ goto exit;
+
+ // Add to the map for later use
+ sObjectMap[*sObjectIter] = ulNewId;
+
+ // Find the properties for this object
+ strQuery = (string)"SELECT propname, value FROM objectproperty WHERE objectid=" + stringify(sObjectIter->ulId);
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery.clear();
+ bFirstResult = true;
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+ if (lpDBLen == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseAddExternIdToObject(): FetchRowLengths failed");
+ goto exit;
+ }
+
+ if (lpDBRow[0] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseAddExternIdToObject(): column NULL");
+ goto exit;
+ }
+
+ if (strQuery.empty())
+ strQuery = "INSERT INTO objectproperty_temp (objectid, propname, value) VALUES ";
+
+ if (!bFirstResult)
+ strQuery += ",";
+ else
+ bFirstResult = false;
+
+ strQuery +=
+ "(" + stringify(ulNewId) + "," +
+ lpDatabase->EscapeBinary((unsigned char*)lpDBRow[0], lpDBLen[0]) + ",";
+
+ if (lpDBRow[1] == NULL)
+ strQuery += "NULL)";
+ else
+ strQuery += lpDatabase->EscapeBinary((unsigned char*)lpDBRow[1], lpDBLen[1]) + ")";
+ }
+
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+ if (!strQuery.empty()) {
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+ er = lpDatabase->DoDelete("DELETE FROM objectproperty WHERE objectid=" + stringify(sObjectIter->ulId));
+ if (er != erSuccess)
+ goto exit;
+ }
+
+
+ // Now repopulate the objectrelation table.
+ strQuery = "SELECT objectid, parentobjectid, relationtype FROM objectrelation";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_crit(" objectrelation table contains invalid NULL records");
+ goto exit;
+ }
+
+ sRelationList.push_back(SRelation(atoi(lpDBRow[0]), atoi(lpDBRow[1]), atoi(lpDBRow[2])));
+ }
+
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+ strQuery.clear();
+ bFirstResult = true;
+ for (sRelationIter = sRelationList.begin(); sRelationIter != sRelationList.end(); ++sRelationIter) {
+ // Find the new parentId, if not found: ignore so they disappear .. would have been invalid relations anyway.
+ switch (sRelationIter->ulRelationType) {
+ case OBJECTRELATION_QUOTA_USERRECIPIENT:
+ case OBJECTRELATION_USER_SENDAS:
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulParentObjectId, 1 /* USEROBJECT_TYPE_USER */));
+ if (sObjectMapIter == sObjectMap.end())
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulParentObjectId, 5 /* USEROBJECT_TYPE_NONACTIVE */));
+ if (sObjectMapIter == sObjectMap.end())
+ continue;
+ ulNewParentId = sObjectMapIter->second;
+ break;
+
+ case OBJECTRELATION_GROUP_MEMBER:
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulParentObjectId, 2 /* USEROBJECT_TYPE_GROUP */));
+ if (sObjectMapIter == sObjectMap.end())
+ continue;
+ ulNewParentId = sObjectMapIter->second;
+ break;
+
+ case OBJECTRELATION_COMPANY_VIEW:
+ case OBJECTRELATION_COMPANY_ADMIN:
+ case OBJECTRELATION_QUOTA_COMPANYRECIPIENT:
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulParentObjectId, 4 /* USEROBJECT_TYPE_COMPANY */));
+ if (sObjectMapIter == sObjectMap.end())
+ continue;
+ ulNewParentId = sObjectMapIter->second;
+ break;
+
+ case OBJECTRELATION_ADDRESSLIST_MEMBER:
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulParentObjectId, 6 /* USEROBJECT_TYPE_ADDRESSLIST */));
+ if (sObjectMapIter == sObjectMap.end())
+ continue;
+ ulNewParentId = sObjectMapIter->second;
+ break;
+ }
+
+ // Find the new object id
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulObjectId, 1 /* USEROBJECT_TYPE_USER */));
+ if (sObjectMapIter == sObjectMap.end())
+ sObjectMapIter = sObjectMap.find(SObject(sRelationIter->ulObjectId, 5)); // USEROBJECT_TYPE_NONACTIVE
+ if (sObjectMapIter == sObjectMap.end())
+ continue;
+ ulNewId = sObjectMapIter->second;
+
+ // Update strQuery for this relation
+ if (strQuery.empty())
+ strQuery = "INSERT INTO objectrelation_temp (objectid,parentobjectid,relationtype) VALUES ";
+
+ if (!bFirstResult)
+ strQuery += ",";
+ else
+ bFirstResult = false;
+
+ strQuery += "(" + stringify(ulNewId) + "," + stringify(ulNewParentId) + "," + stringify(sRelationIter->ulRelationType) + ")";
+ }
+
+ if (!strQuery.empty()) {
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+
+
+ // Now delete the old tables and rename the new ones
+ er = lpDatabase->DoDelete("DROP TABLE object, objectproperty, objectrelation");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("RENAME TABLE object_temp TO object, "
+ "objectproperty_temp TO objectproperty, "
+ "objectrelation_temp TO objectrelation");
+
+exit:
+ // Delete the temporary tables if they exist at this point
+ lpDatabase->DoDelete("DROP TABLE IF EXISTS object_temp, objectproperty_temp, objectrelation_temp");
+
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 26
+ECRESULT UpdateDatabaseCreateReferences(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ struct {
+ const uLong cbUncompressed;
+ const uLong cbCompressed;
+ const char *lpCompressed;
+ } data = {
+ 8678,
+ 271,
+ "\x78\x9c\xed\xd8\xbd\x4a\x03\x41\x14\x86\xe1\x77\x67\x7f\x32\x71"
+ "\x2d\x82\x24\x82\x36\x26\x60\x63\x15\xc1\xd2\x42\x2c\x2d\x84\x90"
+ "\xce\x26\xb2\x46\xd1\x45\xd9\x42\xa3\x60\x97\xc2\xab\xf0\x92\xbc"
+ "\x00\x2f\xc1\x5c\x84\xe0\x38\x1b\x83\x91\x54\x22\xc2\x2a\x7c\x4f"
+ "\x33\x67\xbf\x81\x39\xa7\xdd\xf3\x16\xd4\x97\x43\x68\x04\xb0\x0d"
+ "\x13\x5a\xf0\xf4\xb8\x4b\xc9\xec\x50\xf7\xc7\x73\x02\x1d\x66\x26"
+ "\x2c\xe8\x76\xa1\xbd\x18\x8a\x88\x88\x88\x88\x88\x88\xc8\x9f\x12"
+ "\xf8\x9f\xff\x25\x7f\x5a\xc2\xf2\x6b\x6c\x60\x9d\x88\x70\x6c\x61"
+ "\x05\x83\x19\x27\x1f\x85\xeb\xf5\x0f\x0e\xf7\xfb\x47\xae\xea\x89"
+ "\x7f\xe4\x7f\x4e\x2d\x22\x22\x22\x22\x22\x22\xf2\x4b\x5e\x02\x1a"
+ "\x55\xcf\x50\xa5\x80\x88\x01\x1b\xbe\x6a\xb1\xf9\x99\xf6\x58\xf5"
+ "\x37\xa5\x01\x71\x64\x9a\x5b\xed\x6f\xf2\xaf\xa5\x79\x71\x33\xca"
+ "\x8a\xe1\x59\x7e\x4a\x8c\x1d\x5e\xdc\x16\x97\xbe\x4c\x88\x46\xd9"
+ "\x39\x35\xd2\xbb\xec\xea\xf8\x24\x2f\xb2\xeb\x7b\xa2\x34\x4d\x99"
+ "\xae\x5c\xbc\xb0\x03\xb1\x4d\x12\xa6\x1b\x17\xcf\xf8\x20\x89\x7c"
+ "\x60\xbf\x04\xb5\xd4\xda\x72\x63\xb3\xf6\xd0\x84\xd7\x3d\x70\xf3"
+ "\x7e\x6e\xd6\xcc\xf9\x4e\x6e\xde\xc6\xf1\x0e\x4f\x19\x36\x95"
+ };
+ Bytef *lpUncompressed = NULL;
+ uLong cbUncompressed = data.cbUncompressed;
+ std::string strLobPath;
+ FILE *fdLob = NULL;
+#endif
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_REFERENCES);
+ if (er != erSuccess)
+ goto exit;
+
+ /*
+ * Create all attachment references from hierarchy table, let
+ * instanceid be equal to hierarchyid to minimize the impact
+ * on the upgrade.
+ */
+ strQuery =
+ "INSERT INTO `singleinstances` (`instanceid`, `hierarchyid`, `tag`) "
+ "SELECT id, id, " + stringify(PROP_ID(PR_ATTACH_DATA_BIN)) + " "
+ "FROM `hierarchy` "
+ "WHERE type = " + stringify(MAPI_ATTACH);
+
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ /* We need to rename the column in `lob` */
+#ifdef HAVE_OFFLINE_SUPPORT
+ lpUncompressed = new Bytef[cbUncompressed];
+
+ if (uncompress(lpUncompressed, &cbUncompressed, (const Bytef*)data.lpCompressed, data.cbCompressed) != Z_OK) {
+ er = ZARAFA_E_UNABLE_TO_COMPLETE;
+ goto exit;
+ }
+
+ strLobPath = lpDatabase->GetDatabaseDir() + "/zarafa/lob.frm";
+ fdLob = fopen(strLobPath.c_str(), "wb");
+ if (fdLob == NULL) {
+ er = ZARAFA_E_UNABLE_TO_COMPLETE;
+ goto exit;
+ }
+
+ fwrite(lpUncompressed, 1, cbUncompressed, fdLob);
+ fclose(fdLob);
+
+ er = lpDatabase->DoUpdate("FLUSH TABLES");
+ if (er != erSuccess)
+ goto exit;
+#else
+ strQuery =
+ "ALTER TABLE `lob` "
+ "CHANGE COLUMN `hierarchyid` `instanceid` int(11) unsigned NOT NULL";
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+#endif
+
+exit:
+#ifdef HAVE_OFFLINE_SUPPORT
+ delete[] lpUncompressed;
+#endif
+
+ return er;
+}
+
+// 27
+ECRESULT UpdateDatabaseLockDistributed(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert("INSERT INTO settings VALUES ('lock_distributed_zarafa', 'upgrade')");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 28
+ECRESULT UpdateDatabaseCreateABChangesTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+ int ulId = 0;
+ list<int> syncIds;
+ list<string> queries;
+ bool fFirst = true;
+ string strSyncId;
+
+ list<string>::const_iterator queryIter;
+ list<int>::const_iterator syncIdIter;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_ABCHANGES);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "SELECT id, sourcekey, parentsourcekey, change_type FROM changes WHERE change_type & " + stringify(ICS_AB) + " AND parentsourcekey=0x00000000AC21A95040D3EE48B319FBA75330442500000000040000000100000000000000";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ // Extract the AB changes from the changes table.
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBLen[1] == 0 || lpDBRow[2] == NULL || lpDBLen[2] == 0) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_crit(" changes table contains invalid NULL records");
+ goto exit;
+ }
+
+ ulId = atoi(lpDBRow[0]);
+ syncIds.push_back(ulId);
+
+ strQuery = "INSERT INTO abchanges (id, sourcekey, parentsourcekey, change_type";
+ strQuery += (string)") VALUES (" + lpDBRow[0] + ", " +
+ lpDatabase->EscapeBinary((unsigned char*)lpDBRow[1], lpDBLen[1]) + ", " +
+ lpDatabase->EscapeBinary((unsigned char*)lpDBRow[2], lpDBLen[2]) + ", " +
+ lpDBRow[3];
+ strQuery += ")";
+ queries.push_back(strQuery);
+ }
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+
+ // Populate the abchanges table with the extracted data
+ for (queryIter = queries.begin(); queryIter != queries.end(); ++queryIter) {
+ er = lpDatabase->DoInsert(*queryIter);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+
+ // Remove the extracted changes from the changes table
+ strQuery = "DELETE FROM changes WHERE id IN (";
+ for (syncIdIter = syncIds.begin(); syncIdIter != syncIds.end(); ++syncIdIter) {
+ strSyncId = stringify(*syncIdIter, false);
+
+ if (strQuery.length() + strSyncId.length() + 2 >= lpDatabase->GetMaxAllowedPacket()) { // we need to be able to add a ',' and a ')';
+ strQuery += ")";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "DELETE FROM changes WHERE id IN (";
+ fFirst = true;
+ }
+
+ if (!fFirst)
+ strQuery += ",";
+ fFirst = false;
+
+ strQuery += strSyncId;
+ }
+ if (!fFirst) {
+ strQuery += ")";
+ er = lpDatabase->DoInsert(strQuery);
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ if (er != erSuccess)
+ lpDatabase->DoDelete("DROP TABLE IF EXISTS abchanges");
+
+ return er;
+}
+
+// 29
+ECRESULT UpdateDatabaseSetSingleinstanceTag(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+
+ // Force all tag values to PR_ATTACH_DATA_BIN. Up to now, no other values can be present in the table.
+ strQuery = "UPDATE `singleinstances` SET `tag` = " + stringify(PROP_ID(PR_ATTACH_DATA_BIN));
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 30
+ECRESULT UpdateDatabaseCreateSyncedMessagesTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEFS_SYNCEDMESSAGES);
+
+ return er;
+}
+
+// 31
+ECRESULT UpdateDatabaseForceAbResync(ECDatabase *lpDatabase)
+{
+#ifdef HAVE_OFFLINE_SUPPORT
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ // Remove the PR_EC_AB_SYNC_STATUS property.
+ strQuery = "DELETE p.* FROM properties AS p JOIN stores AS s ON p.storeid=s.hierarchy_id AND p.hierarchyid=s.hierarchy_id WHERE p.tag=0x67a2 AND p.type=0x102";
+ er = lpDatabase->DoDelete(strQuery);
+#else
+ ECRESULT er = ZARAFA_E_IGNORE_ME;
+#endif
+
+ return er;
+}
+
+// 32
+ECRESULT UpdateDatabaseRenameObjectTypeToObjectClass(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ // rename columns in users and object tables
+ strQuery =
+ "ALTER TABLE `users` "
+ "CHANGE COLUMN `object_type` `objectclass` int(11) unsigned NOT NULL";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ // Note: type also changes from int to tinyint here
+ strQuery =
+ "ALTER TABLE `object` "
+ "CHANGE COLUMN `objecttype` `objectclass` int(11) unsigned NOT NULL";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 33
+ECRESULT UpdateDatabaseConvertObjectTypeToObjectClass(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+ std::string strQuery, strUpdate;
+ bool bFirst = true;
+ std::map<unsigned int, unsigned int> mapTypes;
+ std::map<unsigned int, unsigned int>::const_iterator iTypes;
+ std::list<std::string> lstUpdates;
+
+ // make internal SYSTEM a objectclass_t user
+ er = lpDatabase->DoUpdate("UPDATE `users` SET `objectclass` = "+stringify(ACTIVE_USER)+" WHERE `externid` is NULL AND `objectclass` = 1");
+ if (er != erSuccess)
+ goto exit;
+
+ // make internal EVERYONE a objectclass_t security group
+ er = lpDatabase->DoUpdate("UPDATE `users` SET `objectclass` = "+stringify(DISTLIST_SECURITY)+" WHERE `externid` is NULL AND `objectclass` = 2");
+ if (er != erSuccess)
+ goto exit;
+
+ // database stored typed, convert to the new objectclass_t values
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(1, ACTIVE_USER)); // USEROBJECT_TYPE_USER
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(2, DISTLIST_GROUP)); // USEROBJECT_TYPE_GROUP
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(3, NONACTIVE_CONTACT)); // USEROBJECT_TYPE_CONTACT (unused, but who knows..)
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(4, CONTAINER_COMPANY)); // USEROBJECT_TYPE_COMPANY
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(5, NONACTIVE_USER)); // USEROBJECT_TYPE_NONACTIVE
+ mapTypes.insert(std::pair<unsigned int, unsigned int>(6, CONTAINER_ADDRESSLIST)); // USEROBJECT_TYPE_ADDRESSLIST
+
+ for (iTypes = mapTypes.begin(); iTypes != mapTypes.end(); ++iTypes) {
+ // extern id, because it links to object table for DB plugin
+ // on LDAP plugin, object table is empty.
+ er = lpDatabase->DoSelect("SELECT `externid`, `objectclass` FROM `users` WHERE `externid` is not NULL AND `objectclass` = "+stringify(iTypes->first), &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ strUpdate = "(";
+ bFirst = true;
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+ if (lpDBRow[0] == NULL || lpDBLen == NULL || lpDBLen[0] == 0) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_crit(" users table contains invalid NULL records for type %d", iTypes->first);
+ goto exit;
+ }
+
+ if (!bFirst)
+ strUpdate += ",";
+
+ strUpdate += lpDatabase->EscapeBinary((unsigned char*)lpDBRow[0], lpDBLen[0]);
+ bFirst = false;
+ }
+ strUpdate += ")";
+
+ if (bFirst)
+ continue; // we had no results for this type, continue with the next
+
+ // save all queries in a list, so we don't cross-update types
+
+ strQuery =
+ "UPDATE `users` SET `objectclass`=" + stringify(iTypes->second) + " "
+ "WHERE `externid` IN " + strUpdate + " "
+ "AND `objectclass` = " + stringify(iTypes->first);
+ lstUpdates.push_back(strQuery);
+
+ strQuery =
+ "UPDATE `object` SET `objectclass`=" + stringify(iTypes->second) + " "
+ "WHERE `externid` IN " + strUpdate + " "
+ "AND `objectclass` = " + stringify(iTypes->first);
+ lstUpdates.push_back(strQuery);
+ }
+
+ // process all type updates
+ for (std::list<std::string>::const_iterator iu = lstUpdates.begin();
+ iu != lstUpdates.end(); ++iu) {
+ er = lpDatabase->DoUpdate(*iu);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 34
+ECRESULT UpdateDatabaseAddMVPropertyTable(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoInsert(Z_TABLEDEF_OBJECT_MVPROPERTY);
+
+ return er;
+}
+
+// 35
+ECRESULT UpdateDatabaseCompanyNameToCompanyId(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ string strQuery;
+ map<string, string> mapIdToName;
+ std::map<std::string, std::string>::const_iterator iter;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+
+ // find all companies
+ strQuery = "SELECT object.externid, objectproperty.value FROM objectproperty JOIN object ON objectproperty.objectid=object.id WHERE objectproperty.propname = 'companyname'";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL)
+ continue;
+
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+
+ mapIdToName.insert(pair<string,string>(string(lpDBRow[0], lpDBLen[0]), string(lpDBRow[1], lpDBLen[1])));
+ }
+
+ // update objects to link via externid in companyid, not companyname anymore
+ for (iter = mapIdToName.begin(); iter != mapIdToName.end(); ++iter) {
+ strQuery = "UPDATE objectproperty SET value = 0x" + bin2hex(iter->first) +
+ " WHERE propname='companyid' AND value = '" + iter->second + "'";
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 36
+ECRESULT UpdateDatabaseOutgoingQueuePrimarykey(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er= lpDatabase->DoUpdate("ALTER TABLE outgoingqueue DROP PRIMARY KEY, ADD PRIMARY KEY (`hierarchy_id`,`flags`,`store_id`)");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 37
+ECRESULT UpdateDatabaseACLPrimarykey(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er= lpDatabase->DoUpdate("ALTER TABLE acl DROP PRIMARY KEY, ADD PRIMARY KEY (`hierarchy_id`,`id`,`type`)");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 38
+ECRESULT UpdateDatabaseBlobExternId(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ strQuery = "ALTER TABLE `object` "
+ "DROP KEY `externid`, "
+ "MODIFY `externid` blob, "
+ "ADD UNIQUE KEY `externid` (`externid`(255), `objectclass`)";
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "ALTER TABLE `users` "
+ "DROP KEY `externid`, "
+ "MODIFY `externid` blob, "
+ "ADD UNIQUE KEY `externid` (`externid`(255), `objectclass`)";
+
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 39
+ECRESULT UpdateDatabaseKeysChanges2(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ // Change index
+ er = lpDatabase->DoUpdate("ALTER TABLE changes DROP PRIMARY KEY, ADD PRIMARY KEY(`parentsourcekey`,`sourcekey`,`change_type`)");
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+/**
+ * Update the primary key in mvproperties
+ *
+ * Change the primary key to get more performance in mysql because the mysql can not
+ * choose the right key we change the index into one primary key.
+ *
+ * @remarks We are checking extra for the 'hi' key because some upgrade issues
+ *
+ * @param[in] lpDatabase ECDatabase object pointer to update.
+ * @retval erSuccess
+ * Update is done.
+ * @retval ZARAFA_E_DATABASE_ERROR
+ * Update failed
+ */
+// 40
+ECRESULT UpdateDatabaseMVPropertiesPrimarykey(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ bool bUpdate = false;
+
+ er = lpDatabase->DoSelect("SHOW KEYS FROM mvproperties", &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ // Result: | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL)
+ continue;
+
+ if(stricmp(lpDBRow[2], "hi") == 0) {
+ bUpdate = true;
+ break;
+ }
+ }
+
+ if (bUpdate) {
+ er = lpDatabase->DoUpdate("ALTER TABLE mvproperties DROP PRIMARY KEY, ADD PRIMARY KEY (`hierarchyid`,`tag`,`type`,`orderid`), DROP KEY `hi`");
+ if (er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 41
+ECRESULT UpdateDatabaseFixDBPluginGroups(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ er = lpDatabase->DoUpdate("UPDATE object SET objectclass="+stringify(DISTLIST_SECURITY)+" WHERE objectclass="+stringify(DISTLIST_GROUP));
+ if (er != erSuccess)
+ goto exit;
+
+exit:
+ return er;
+}
+
+// 42
+ECRESULT UpdateDatabaseFixDBPluginSendAs(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+ list<std::pair<string, string> > lstRelations;
+ std::list<std::pair<std::string, std::string> >::const_iterator iRelations;
+
+ // relation 6 == OBJECTRELATION_USER_SENDAS
+ er = lpDatabase->DoSelect("SELECT objectid, parentobjectid FROM objectrelation WHERE relationtype=6", &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL)
+ continue;
+
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+
+ lstRelations.push_back(pair<string,string>(string(lpDBRow[0], lpDBLen[0]), string(lpDBRow[1], lpDBLen[1])));
+ }
+
+ er = lpDatabase->DoDelete("DELETE FROM objectrelation WHERE relationtype=6");
+ if (er != erSuccess)
+ goto exit;
+
+ for (iRelations = lstRelations.begin(); iRelations != lstRelations.end(); ++iRelations) {
+ er = lpDatabase->DoUpdate("INSERT INTO objectrelation (objectid, parentobjectid, relationtype) VALUES ("+iRelations->second+", "+iRelations->first+", 6)");
+ if (er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+/**
+ * IMAP used to store subscriptions on the store. This gave problems
+ * when multi-threaded IMAP clients (Thunderbird) subscribed on
+ * folders in one thread, and requested the list on another
+ * thread. This would have returned the old subscribed list, since the
+ * store doesn't have an update notification system like normal
+ * folders do. Moved to the Inbox, since this folder is always
+ * present and easy to find on the server and client.
+ *
+ * @param[in] lpDatabase ECDatabase object pointer to update.
+ * @return ECRESULT erSuccess or ZARAFA_E_DATABASE_ERROR
+ */
+// 43
+ECRESULT UpdateDatabaseMoveSubscribedList(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ map<string, string> mapStoreInbox;
+ std::map<std::string, std::string>::const_iterator i;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ DB_LENGTHS lpDBLen = NULL;
+
+ er = lpDatabase->DoSelect("SELECT storeid, objid FROM receivefolder WHERE messageclass='IPM'", &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL)
+ continue;
+
+ lpDBLen = lpDatabase->FetchRowLengths(lpResult);
+
+ mapStoreInbox.insert(pair<string,string>(string(lpDBRow[0], lpDBLen[0]), string(lpDBRow[1], lpDBLen[1])));
+ }
+
+ for (i = mapStoreInbox.begin(); i != mapStoreInbox.end(); ++i) {
+ // Remove property if it's already there (possible if you run new gateway against old server before upgrade)
+ er = lpDatabase->DoDelete("DELETE FROM properties WHERE storeid="+i->first+" AND hierarchyid="+i->first+" AND tag=0x6784 AND type=0x0102");
+ if (er != erSuccess)
+ goto exit;
+
+ // does not return an error if property was not in the database
+ er = lpDatabase->DoUpdate("UPDATE properties SET hierarchyid="+i->second+
+ " WHERE storeid="+i->first+" AND hierarchyid="+i->first+" AND tag=0x6784 AND type=0x0102");
+ if (er != erSuccess)
+ goto exit;
+ }
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 44
+ECRESULT UpdateDatabaseSyncTimeIndex(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ bool bHaveIndex;
+
+ // There are upgrade paths where the sync_time key already exists.
+ er = lpDatabase->CheckExistIndex("syncs", "sync_time", &bHaveIndex);
+ if (er == erSuccess && !bHaveIndex)
+ er = lpDatabase->DoUpdate("ALTER TABLE syncs ADD INDEX sync_time (`sync_time`)");
+
+ return er;
+}
+
+// 45
+ECRESULT UpdateDatabaseAddStateKey(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+#ifndef HAVE_OFFLINE_SUPPORT
+ bool bHaveIndex;
+
+ // There are upgrade paths where the state key already exists.
+ er = lpDatabase->CheckExistIndex("changes", "state", &bHaveIndex);
+ if (er == erSuccess && !bHaveIndex)
+ er = lpDatabase->DoUpdate("ALTER TABLE changes ADD UNIQUE KEY `state` (`parentsourcekey`,`id`)");
+#else
+
+ er = ZARAFA_E_IGNORE_ME;
+#endif
+
+ return er;
+}
+
+// 46
+ECRESULT UpdateDatabaseConvertToUnicode(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+#ifndef HAVE_OFFLINE_SUPPORT
+ if (lpDatabase->m_bForceUpdate) {
+#endif
+ PROGRESS_INIT(Z_UPDATE_CONVERT_TO_UNICODE)
+
+ // Admin requested a forced upgrade, converting known tables
+
+ /*
+ * Since we inserted the company guid into the objectproperty
+ * table, this convert may break, since the binary data won't
+ * be valid utf-8 data in mysql. So we convert the 'companyid'
+ * properties from this table into a hexed version, which is
+ * plain text and will not break.
+ *
+ * We need to do this first, since the begin/commit will work
+ * on this statement, and won't on the following alter table
+ * commands.
+ */
+ strQuery = "UPDATE objectproperty SET value = hex(value) WHERE propname = 'companyid'";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ // Convert tables to unicode
+
+ strQuery = "ALTER TABLE mvproperties MODIFY val_string longtext CHARSET utf8 COLLATE utf8_general_ci";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ // No need to convert the properties table as that will be done on the fly in update 50 (Z_UPDATE_CONVERT_PROPERTIES)
+
+ // db-plugin
+ strQuery = "ALTER TABLE objectproperty MODIFY propname VARCHAR(255) CHARSET utf8 COLLATE utf8_general_ci, MODIFY value TEXT CHARSET utf8 COLLATE utf8_general_ci";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ /*
+ * Another similar change is to the SYSADMIN property; it used
+ * to be 12345:XXXXXXXX with XXXXX being a binary externid. That
+ * has changed to be a hexed version, so, 12345:HHHHHHHHHH, with
+ * HHHH being the hexed version of XXXXX
+ */
+ strQuery = "UPDATE objectproperty SET value = concat(substr(value,1,instr(value,';')-1),';',hex(substr(value,instr(value,';')+1))) WHERE propname = 'companyadmin'";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "ALTER TABLE objectmvproperty MODIFY propname VARCHAR(255) CHARSET utf8 COLLATE utf8_general_ci, MODIFY value TEXT CHARSET utf8 COLLATE utf8_general_ci";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ /*
+ * Other tables containing varchar's are not converted, all data in those fields are us-ascii anyway:
+ * - receivefolder
+ * - stores (specially handled in next update
+ * - settings
+ */
+ PROGRESS_DONE
+#ifndef HAVE_OFFLINE_SUPPORT
+ } else {
+ ec_log_crit("Will not upgrade your database from 6.40.x to 7.0.");
+ ec_log_crit("The recommended upgrade procedure is to use the zarafa7-upgrade commandline tool.");
+ ec_log_crit("Please consult the Zarafa administrator manual on how to correctly upgrade your database.");
+ ec_log_crit("Alternatively you may try to upgrade using --force-database-upgrade,");
+ ec_log_crit("but no progress and estimates within the updates will be available.");
+ er = ZARAFA_E_USER_CANCEL;
+ goto exit;
+ }
+#endif
+
+exit:
+ return er;
+}
+
+// 47
+ECRESULT UpdateDatabaseConvertStoreUsername(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ PROGRESS_INIT(Z_UPDATE_CONVERT_STORE_USERNAME)
+
+ er = lpDatabase->DoUpdate("UPDATE stores SET user_name = CAST(CONVERT(user_name USING latin1) AS CHAR(255) CHARACTER SET utf8)");
+ if (er == erSuccess)
+ er = lpDatabase->DoUpdate("ALTER TABLE stores MODIFY user_name VARCHAR(255) CHARACTER SET utf8 NOT NULL DEFAULT ''");
+
+ PROGRESS_DONE
+
+#ifdef HAVE_OFFLINE_SUPPORT
+exit:
+#endif
+ return er;
+}
+
+// 48
+ECRESULT UpdateDatabaseConvertRules(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ convert_context converter;
+ char *lpszConverted = NULL;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ unsigned int ulTotal = 0;
+ unsigned int ulCurrent = 0;
+#endif
+ PROGRESS_INIT(Z_UPDATE_CONVERT_RULES)
+
+ er = lpDatabase->DoSelect("SELECT p.hierarchyid, p.storeid, p.val_binary FROM properties AS p JOIN receivefolder AS r ON p.hierarchyid=r.objid AND p.storeid=r.storeid JOIN stores AS s ON r.storeid=s.hierarchy_id WHERE p.tag=0x3fe1 AND p.type=0x102 AND r.messageclass='IPM'", &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ ulTotal = lpDatabase->GetNumRows(lpResult);
+#endif
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseConvertRules(): column NULL");
+ goto exit;
+ }
+
+ // Use WTF-1252 here since the pre-unicode rule serializer didn't pass the SOAP_C_UTFSTRING flag, causing
+ // gsoap to encode the data as UTF8, eventhough it was already encoded as WINDOWS-1252.
+ lpszConverted = ECStringCompat::WTF1252_to_UTF8(NULL, lpDBRow[2], &converter);
+
+ er = lpDatabase->DoUpdate("UPDATE properties SET val_binary='" + lpDatabase->Escape(lpszConverted) + "' WHERE hierarchyid=" + lpDBRow[0] + " AND storeid=" + lpDBRow[1] + " AND tag=0x3fe1 AND type=0x102");
+ if (er != erSuccess)
+ goto exit;
+
+ INTERMEDIATE_PROGRESS(++ulCurrent, ulTotal)
+
+ delete[] lpszConverted;
+ lpszConverted = NULL;
+ }
+
+ PROGRESS_DONE
+
+exit:
+ delete[] lpszConverted;
+
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 49
+ECRESULT UpdateDatabaseConvertSearchFolders(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+
+ std::string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+ convert_context converter;
+ char *lpszConverted = NULL;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ unsigned int ulTotal = 0;
+ unsigned int ulCurrent = 0;
+#endif
+ PROGRESS_INIT(Z_UPDATE_CONVERT_SEARCH_FOLDERS)
+
+ strQuery = "SELECT h.id, p.storeid, p.val_string FROM hierarchy AS h JOIN properties AS p ON p.hierarchyid=h.id AND p.tag=" + stringify(PROP_ID(PR_EC_SEARCHCRIT)) +" AND p.type=" + stringify(PROP_TYPE(PR_EC_SEARCHCRIT)) + " WHERE h.type=3 AND h.flags=2";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ ulTotal = lpDatabase->GetNumRows(lpResult);
+#endif
+
+ while ((lpDBRow = lpDatabase->FetchRow(lpResult))) {
+ if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseConvertSearchFolders(): column NULL");
+ goto exit;
+ }
+
+ // Use WTF-1252 here since the pre-unicode rule serializer didn't pass the SOAP_C_UTFSTRING flag, causing
+ // gsoap to encode the data as UTF8, eventhough it was already encoded as WINDOWS-1252.
+ lpszConverted = ECStringCompat::WTF1252_to_WINDOWS1252(NULL, lpDBRow[2], &converter);
+
+ er = lpDatabase->DoUpdate("UPDATE properties SET val_string='" + lpDatabase->Escape(lpszConverted) + "' WHERE hierarchyid=" + lpDBRow[0] + " AND storeid=" + lpDBRow[1] + " AND tag=" + stringify(PROP_ID(PR_EC_SEARCHCRIT)) +" AND type=" + stringify(PROP_TYPE(PR_EC_SEARCHCRIT)));
+ if (er != erSuccess)
+ goto exit;
+
+ INTERMEDIATE_PROGRESS(++ulCurrent, ulTotal)
+
+ delete[] lpszConverted;
+ lpszConverted = NULL;
+ }
+
+ PROGRESS_DONE
+
+exit:
+ delete[] lpszConverted;
+
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 50
+ECRESULT UpdateDatabaseConvertProperties(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ unsigned int ulTotal = 0;
+ unsigned int ulCurrent = 0;
+#endif
+ PROGRESS_INIT(Z_UPDATE_CONVERT_PROPERTIES)
+
+ // Create the temporary properties table
+ strQuery = Z_TABLEDEF_PROPERTIES;
+ strQuery.replace(strQuery.find("CREATE TABLE"), strlen("CREATE TABLE"), "CREATE TABLE IF NOT EXISTS");
+ strQuery.replace(strQuery.find("properties"), strlen("properties"), "properties_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ strQuery = "SELECT MAX(hierarchyid) FROM properties";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ if (lpDBRow == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseConvertProperties(): row non existing");
+ goto exit;
+ }
+
+ ulTotal = lpDBRow[0] ? atoui(lpDBRow[0]) : 0;
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+#endif
+
+ while (true) {
+ strQuery = "INSERT IGNORE INTO properties_temp (hierarchyid,tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo) SELECT hierarchyid,tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo FROM properties ORDER BY hierarchyid ASC LIMIT 10000";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "DELETE FROM properties ORDER BY hierarchyid ASC LIMIT 10000";
+ er = lpDatabase->DoDelete(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->Commit();
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->Begin();
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "SELECT MIN(hierarchyid) FROM properties";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ if (lpDBRow == NULL || lpDBRow[0] == NULL)
+ break;
+
+ INTERMEDIATE_PROGRESS(atoui(lpDBRow[0]), ulTotal)
+
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+ }
+
+ // update webaccess settings which were already utf8 in our latin1 table
+ strQuery = "UPDATE properties_temp JOIN hierarchy ON properties_temp.hierarchyid=hierarchy.id AND hierarchy.parent IS NULL SET val_string = CAST(CAST(CONVERT(val_string USING latin1) AS binary) AS CHAR CHARACTER SET utf8) WHERE properties_temp.type=0x1e AND properties_temp.tag=26480";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("RENAME TABLE properties TO properties_old, properties_temp TO properties");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoDelete("DROP TABLE properties_old");
+
+ PROGRESS_DONE
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+ return er;
+}
+
+// 51
+ECRESULT UpdateDatabaseCreateCounters(ECDatabase *lpDatabase)
+{
+ const struct {
+ ULONG ulPropTag;
+ ULONG ulChildType;
+ ULONG ulChildFlagMask;
+ ULONG ulChildFlags;
+ const char* lpszValue;
+ } counter_info[] = {
+ { PR_CONTENT_COUNT, MAPI_MESSAGE, MAPI_ASSOCIATED|MSGFLAG_DELETED, 0, "COUNT(*)" },
+ { PR_CONTENT_UNREAD, MAPI_MESSAGE, MAPI_ASSOCIATED|MSGFLAG_DELETED|MSGFLAG_READ, 0, "SUM(IF(flags&1,0,1))" },
+ { PR_ASSOC_CONTENT_COUNT, MAPI_MESSAGE, MAPI_ASSOCIATED|MSGFLAG_DELETED, MAPI_ASSOCIATED, "0" },
+ { PR_DELETED_MSG_COUNT, MAPI_MESSAGE, MAPI_ASSOCIATED|MSGFLAG_DELETED, MSGFLAG_DELETED, "0" },
+ { PR_DELETED_ASSOC_MSG_COUNT, MAPI_MESSAGE, MAPI_ASSOCIATED|MSGFLAG_DELETED, MAPI_ASSOCIATED|MSGFLAG_DELETED, "0" },
+ { PR_SUBFOLDERS, MAPI_FOLDER, MSGFLAG_DELETED, 0, "0" },
+ { PR_FOLDER_CHILD_COUNT, MAPI_FOLDER, MSGFLAG_DELETED, 0, "0" },
+ { PR_DELETED_FOLDER_COUNT, MAPI_FOLDER, MSGFLAG_DELETED, MSGFLAG_DELETED, "0" }
+ };
+
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+#ifdef HAVE_OFFLINE_SUPPORT
+ unsigned int ulCurrent = 0;
+#endif
+ PROGRESS_INIT(Z_UPDATE_CREATE_COUNTERS)
+
+ for (unsigned i = 0; i < 8; ++i) {
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_ulong) "
+ "SELECT parent.id,"+stringify(PROP_ID(counter_info[i].ulPropTag))+","+stringify(PROP_TYPE(counter_info[i].ulPropTag))+",count(child.id) "
+ "FROM hierarchy AS parent "
+ "LEFT JOIN hierarchy AS child ON parent.id=child.parent AND "
+ "parent.type=3 and child.type="+stringify(counter_info[i].ulChildType)+" AND "
+ "child.flags & "+stringify(counter_info[i].ulChildFlagMask)+"="+stringify(counter_info[i].ulChildFlags)+" "
+ "GROUP BY parent.id";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ INTERMEDIATE_PROGRESS(++ulCurrent, 16)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_ulong) "
+ "SELECT folderid,"+stringify(PROP_ID(counter_info[i].ulPropTag))+","+stringify(PROP_TYPE(counter_info[i].ulPropTag))+","+counter_info[i].lpszValue+" FROM searchresults GROUP BY folderid";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ INTERMEDIATE_PROGRESS(++ulCurrent, 16)
+ }
+
+ PROGRESS_DONE
+
+exit:
+ return er;
+}
+
+// 52
+ECRESULT UpdateDatabaseCreateCommonProps(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ PROGRESS_INIT(Z_UPDATE_CREATE_COMMON_PROPS)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_hi,val_lo,val_ulong) "
+ "SELECT h.id,"+stringify(PROP_ID(PR_CREATION_TIME))+","+stringify(PROP_TYPE(PR_CREATION_TIME))+",(UNIX_TIMESTAMP(h.createtime) * 10000000 + 116444736000000000) >> 32,(UNIX_TIMESTAMP(h.createtime) * 10000000 + 116444736000000000) & 0xffffffff, NULL "
+ "FROM hierarchy AS h "
+ "WHERE h.type IN (3,5,7)";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(.25)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_hi,val_lo,val_ulong) "
+ "SELECT h.id,"+stringify(PROP_ID(PR_LAST_MODIFICATION_TIME))+","+stringify(PROP_TYPE(PR_LAST_MODIFICATION_TIME))+",(UNIX_TIMESTAMP(h.modtime) * 10000000 + 116444736000000000) >> 32,(UNIX_TIMESTAMP(h.modtime) * 10000000 + 116444736000000000) & 0xffffffff, NULL "
+ "FROM hierarchy AS h "
+ "WHERE h.type IN (3,5,7)";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(.5)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_hi,val_lo,val_ulong) "
+ "SELECT h.id,"+stringify(PROP_ID(PR_MESSAGE_FLAGS))+","+stringify(PROP_TYPE(PR_MESSAGE_FLAGS))+",NULL, NULL, h.flags "
+ "FROM hierarchy AS h "
+ "WHERE h.type IN (3,5,7)";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(.75)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_hi,val_lo,val_ulong) "
+ "SELECT h.id,"+stringify(PROP_ID(PR_FOLDER_TYPE))+","+stringify(PROP_TYPE(PR_FOLDER_TYPE))+",NULL, NULL, h.flags & 0x3 "
+ "FROM hierarchy AS h "
+ "WHERE h.type=3";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(1)
+
+ PROGRESS_DONE
+
+exit:
+ return er;
+}
+
+// 53
+ECRESULT UpdateDatabaseCheckAttachments(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ PROGRESS_INIT(Z_UPDATE_CHECK_ATTACHMENTS)
+
+ strQuery = "REPLACE INTO properties(hierarchyid,tag,type,val_ulong) "
+ "SELECT h.id,"+stringify(PROP_ID(PR_HASATTACH))+","+stringify(PROP_TYPE(PR_HASATTACH))+",IF(att.id,1,0) "
+ "FROM hierarchy AS h "
+ "LEFT JOIN hierarchy AS att ON h.id=att.parent AND att.type=7 AND h.type=5 "
+ "GROUP BY h.id";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(.5)
+
+ strQuery = "UPDATE properties AS p "
+ "JOIN hierarchy AS h ON p.hierarchyid=h.id AND h.type=5 "
+ "LEFT JOIN hierarchy AS c ON c.type=7 AND c.parent=p.hierarchyid "
+ "SET p.val_ulong = IF(c.id,p.val_ulong|"+stringify(MSGFLAG_DELETED)+", p.val_ulong & ~"+stringify(MSGFLAG_DELETED)+") "
+ "WHERE p.tag="+stringify(PROP_ID(PR_MESSAGE_FLAGS))+" AND p.type="+stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+ INTERMEDIATE_PROGRESS_(1)
+
+ PROGRESS_DONE
+
+exit:
+ return er;
+}
+
+// 54
+ECRESULT UpdateDatabaseCreateTProperties(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+#ifndef HAVE_OFFLINE_SUPPORT
+ // Create the tproperties table
+ er = lpDatabase->DoInsert(Z_TABLEDEF_TPROPERTIES);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "INSERT IGNORE INTO tproperties (folderid,hierarchyid,tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo) "
+ "SELECT h.id, p.hierarchyid, p.tag, p.type, p.val_ulong, LEFT(p.val_string,255), LEFT(p.val_binary,255), p.val_double, p.val_longint, p.val_hi, p.val_lo "
+ "FROM properties AS p "
+ "JOIN hierarchy AS tmp ON p.hierarchyid = tmp.id AND p.tag NOT IN (" + stringify(PROP_ID(PR_BODY_HTML)) + "," + stringify(PROP_ID(PR_RTF_COMPRESSED)) + ")"
+ "LEFT JOIN hierarchy AS h ON tmp.parent = h.id AND h.type = 3";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+#else
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ unsigned int ulTotal = 0;
+ unsigned int ulCurrent = 0;
+
+ PROGRESS_INIT(Z_UPDATE_CREATE_TPROPERTIES)
+
+ // Create the tproperties table
+ strQuery = Z_TABLEDEF_TPROPERTIES;
+ strQuery.replace(strQuery.find("CREATE TABLE"), strlen("CREATE TABLE"), "CREATE TABLE IF NOT EXISTS");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "SELECT MAX(hierarchyid) FROM properties";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ if (lpDBRow == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseCreateTProperties(): row non existing");
+ goto exit;
+ }
+
+ ulTotal = lpDBRow[0] ? atoui(lpDBRow[0]) : 0;
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+ while (ulCurrent <= ulTotal) {
+ unsigned int ulInserted, ulAffected;
+
+ strQuery = "INSERT IGNORE INTO tproperties (folderid,hierarchyid,tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo) "
+ "SELECT h.id, p.hierarchyid, p.tag, p.type, p.val_ulong, LEFT(p.val_string,255), LEFT(p.val_binary,255), p.val_double, p.val_longint, p.val_hi, p.val_lo "
+ "FROM properties AS p "
+ "JOIN hierarchy AS tmp ON p.hierarchyid = tmp.id AND p.tag NOT IN (" + stringify(PROP_ID(PR_BODY_HTML)) + "," + stringify(PROP_ID(PR_RTF_COMPRESSED)) + ") "
+ " AND p.hierarchyid >= " + stringify(ulCurrent) + " AND p.hierarchyid < " + stringify(ulCurrent + 500) + " "
+ "LEFT JOIN hierarchy AS h ON tmp.parent = h.id AND h.type = 3";
+ er = lpDatabase->DoInsert(strQuery, &ulInserted, &ulAffected);
+ if (er != erSuccess)
+ goto exit;
+
+ ulCurrent += 500;
+ INTERMEDIATE_PROGRESS(ulCurrent, ulTotal)
+ }
+
+ PROGRESS_DONE
+#endif
+
+exit:
+#ifdef HAVE_OFFLINE_SUPPORT
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+#endif
+
+ return er;
+}
+
+// 55
+ECRESULT UpdateDatabaseConvertHierarchy(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ PROGRESS_INIT(Z_UPDATE_CONVERT_HIERARCHY)
+
+ // Create the temporary properties table
+ strQuery = Z_TABLEDEF_HIERARCHY;
+ strQuery.replace(strQuery.find("hierarchy"), strlen("hierarchy"), "hierarchy_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ // Folders can be 0, 1, 2 and 0x400 (Deleted)
+ // Messages can be 0x40 (associated) and Deleted
+ // Other can be Deleted
+ strQuery = "INSERT INTO hierarchy_temp (id, parent, type, flags, owner) SELECT id, parent, type, CASE type WHEN 3 THEN flags & 0x403 WHEN 5 THEN flags & 0x440 ELSE flags & 0x400 END, owner FROM hierarchy";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("RENAME TABLE hierarchy TO hierarchy_old, hierarchy_temp TO hierarchy");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoDelete("DROP TABLE hierarchy_old");
+
+ PROGRESS_DONE
+
+exit:
+ lpDatabase->DoDelete("DROP TABLE IF EXISTS hierarchy_temp");
+
+ return er;
+}
+
+// 56
+ECRESULT UpdateDatabaseCreateDeferred(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ PROGRESS_INIT(Z_UPDATE_CREATE_DEFERRED)
+
+ // Create the deferred table
+ er = lpDatabase->DoInsert(Z_TABLEDEF_DELAYEDUPDATE);
+
+ PROGRESS_DONE
+
+#ifdef HAVE_OFFLINE_SUPPORT
+exit:
+#endif
+ return er;
+}
+
+// 57
+ECRESULT UpdateDatabaseConvertChanges(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+#ifndef HAVE_OFFLINE_SUPPORT
+ bool bDropColumn;
+
+ // In some upgrade paths the moved_from column doesn't exist. We'll
+ // check so no error (which we could ignore) will be logged.
+ er = lpDatabase->CheckExistColumn("changes", "moved_from", &bDropColumn);
+ if (er == erSuccess && bDropColumn) {
+ strQuery = "ALTER TABLE changes DROP COLUMN moved_from, DROP key moved";
+ er = lpDatabase->DoDelete(strQuery);
+ }
+
+#else
+ DB_RESULT lpResult = NULL;
+ DB_ROW lpDBRow = NULL;
+ unsigned int ulTotal = 0;
+ unsigned int ulCurrent = 0;
+
+ PROGRESS_INIT(Z_UPDATE_CONVERT_CHANGES)
+
+ // Create the temporary properties table
+ strQuery = Z_TABLEDEF_CHANGES;
+ strQuery.replace(strQuery.find("CREATE TABLE"), strlen("CREATE TABLE"), "CREATE TABLE IF NOT EXISTS");
+ strQuery.replace(strQuery.find("changes"), strlen("changes"), "changes_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "SELECT MAX(id) FROM changes";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ if (lpDBRow == NULL) {
+ er = ZARAFA_E_DATABASE_ERROR;
+ ec_log_err("UpdateDatabaseConvertChanges(): row non existing");
+ goto exit;
+ }
+
+ ulTotal = lpDBRow[0] ? atoui(lpDBRow[0]) : 0;
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+
+ while (true) {
+ strQuery = "INSERT INTO changes_temp (id, sourcekey, parentsourcekey, change_type, flags, sourcesync) SELECT id, sourcekey, parentsourcekey, change_type, flags, sourcesync FROM changes ORDER BY id ASC LIMIT 10000";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "DELETE FROM changes ORDER BY id ASC LIMIT 10000";
+ er = lpDatabase->DoDelete(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->Commit();
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->Begin();
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "SELECT MIN(id) FROM changes";
+ er = lpDatabase->DoSelect(strQuery, &lpResult);
+ if (er != erSuccess)
+ goto exit;
+
+ lpDBRow = lpDatabase->FetchRow(lpResult);
+ if (lpDBRow == NULL || lpDBRow[0] == NULL)
+ break;
+
+ INTERMEDIATE_PROGRESS(atoui(lpDBRow[0]), ulTotal)
+ lpDatabase->FreeResult(lpResult);
+ lpResult = NULL;
+ }
+
+ er = lpDatabase->DoUpdate("RENAME TABLE changes TO changes_old, changes_temp TO changes");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoDelete("DROP TABLE changes_old");
+
+ PROGRESS_DONE
+
+exit:
+ if (lpResult)
+ lpDatabase->FreeResult(lpResult);
+
+#endif
+ return er;
+}
+
+// 58
+ECRESULT UpdateDatabaseConvertNames(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ PROGRESS_INIT(Z_UPDATE_CONVERT_NAMES)
+
+ // CharsetDetect(names)
+
+ // Create the temporary names table
+ strQuery = Z_TABLEDEF_NAMES;
+ strQuery.replace(strQuery.find("names"), strlen("names"), "names_temp");
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ strQuery = "INSERT INTO names_temp (id,nameid,namestring,guid) SELECT id,nameid,CAST(CAST(CONVERT(namestring USING latin1) AS binary) AS CHAR CHARACTER SET utf8),guid FROM names";
+ er = lpDatabase->DoInsert(strQuery);
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoUpdate("RENAME TABLE names TO names_old, names_temp TO names");
+ if (er != erSuccess)
+ goto exit;
+
+ er = lpDatabase->DoDelete("DROP TABLE names_old");
+
+ PROGRESS_DONE
+
+exit:
+ lpDatabase->DoDelete("DROP TABLE IF EXISTS names_temp");
+
+ return er;
+}
+
+// 59
+ECRESULT UpdateDatabaseReceiveFolderToUnicode(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ strQuery = "ALTER TABLE receivefolder MODIFY messageclass varchar(255) CHARSET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT''";
+
+ er = lpDatabase->DoUpdate(strQuery);
+
+ return er;
+}
+
+// 60
+ECRESULT UpdateDatabaseClientUpdateStatus(ECDatabase *lpDatabase)
+{
+ return lpDatabase->DoInsert(Z_TABLEDEF_CLIENTUPDATESTATUS);
+}
+
+// 61
+ECRESULT UpdateDatabaseConvertStores(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ // user_hierarchy_id does not exist on all servers, depends on upgrade path
+ strQuery = "ALTER TABLE stores "
+ "DROP KEY `user_hierarchy_id` ";
+ er = lpDatabase->DoUpdate(strQuery);
+ if (er != erSuccess) {
+ ec_log_err("Ignoring optional index error, and continuing database upgrade");
+ er = erSuccess;
+ }
+
+ strQuery = "ALTER TABLE stores "
+ "DROP PRIMARY KEY, "
+ "ADD COLUMN `type` smallint(6) unsigned NOT NULL default '0', "
+ "ADD PRIMARY KEY (`user_id`, `hierarchy_id`, `type`), "
+ "ADD UNIQUE KEY `id` (`id`)";
+ er = lpDatabase->DoUpdate(strQuery);
+
+ return er;
+}
+
+// 62
+ECRESULT UpdateDatabaseUpdateStores(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ strQuery = "UPDATE stores SET type="+stringify(ECSTORE_TYPE_PUBLIC)+" WHERE user_id=1 OR user_id IN (SELECT id FROM users where objectclass="+stringify(CONTAINER_COMPANY)+")";
+ er = lpDatabase->DoUpdate(strQuery);
+
+ return er;
+}
+
+// 63
+ECRESULT UpdateWLinkRecordKeys(ECDatabase *lpDatabase)
+{
+ ECRESULT er = erSuccess;
+ std::string strQuery;
+
+ strQuery = "update stores " // For each store
+ "join properties as p1 on p1.tag = 0x35E6 and p1.hierarchyid=stores.hierarchy_id " // Get PR_COMMON_VIEWS_ENTRYID
+ "join indexedproperties as i1 on i1.val_binary = p1.val_binary and i1.tag=0xfff " // Get hierarchy for common views
+ "join hierarchy as h2 on h2.parent=i1.hierarchyid " // Get children of common views
+ "join properties as p2 on p2.hierarchyid=h2.id and p2.tag=0x684d " // Get PR_WLINK_RECKEY for each child
+ "join properties as p3 on p3.hierarchyid=h2.id and p3.tag=0x684c " // Get PR_WLINK_ENTRYID for each child
+ "set p2.val_binary = p3.val_binary " // Set PR_WLINK_RECKEY = PR_WLINK_ENTRYID
+ "where length(p3.val_binary) = 48"; // Where entryid length is 48 (zarafa)
+ er = lpDatabase->DoUpdate(strQuery);
+
+ return er;
+}
+
+/* Edit no. 64 */
+ECRESULT UpdateVersionsTbl(ECDatabase *db)
+{
+ return db->DoUpdate(
+ "alter table `versions` "
+ "add column `micro` int(11) unsigned not null default 0 after `minor`, "
+ "drop primary key, "
+ "add primary key (`major`, `minor`, `micro`, `revision`, `databaserevision`)");
+}
+
+/* Edit no. 65 */
+ECRESULT UpdateChangesTbl(ECDatabase *db)
+{
+ return db->DoUpdate(
+ "alter table `changes` "
+ "modify change_type int(11) unsigned not null default 0");
+}
+
+/* Edit no. 66 */
+ECRESULT UpdateABChangesTbl(ECDatabase *db)
+{
+ return db->DoUpdate(
+ "alter table `abchanges` "
+ "modify change_type int(11) unsigned not null default 0");
+}
diff --git a/ECDatabaseUpdate.h b/ECDatabaseUpdate.h
new file mode 100644
index 000000000000..ec47c8fd6bc0
--- /dev/null
+++ b/ECDatabaseUpdate.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2005 - 2015 Zarafa B.V. and its licensors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ECDATABASEUPDATE_H
+#define ECDATABASEUPDATE_H
+
+#include <zarafa/ECLogger.h>
+
+ECRESULT UpdateDatabaseCreateVersionsTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateSearchFolders(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseFixUserNonActive(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateSearchFoldersFlags(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabasePopulateSearchFolders(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseCreateChangesTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateSyncsTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateIndexedPropertiesTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateSettingsTable(ECDatabase *lpDatabase);
+ECRESULT InsertServerGUID(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateServerGUID(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateSourceKeys(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseConvertEntryIDs(ECDatabase *lpDatabase);
+ECRESULT CreateRecursiveStoreEntryIds(ECDatabase *lpDatabase, unsigned int ulStoreHierarchyId, unsigned char* lpStoreGuid);
+ECRESULT UpdateDatabaseSearchCriteria(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddUserObjectType(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddUserSignature(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddSourceKeySetting(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseRestrictExternId(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseAddUserCompany(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddObjectRelationType(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseDelUserCompany(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddCompanyToStore(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseAddIMAPSequenceNumber(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseKeysChanges(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseMoveFoldersInPublicFolder(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseAddExternIdToObject(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateReferences(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseLockDistributed(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateABChangesTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseSetSingleinstanceTag(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseCreateSyncedMessagesTable(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseForceAbResync(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseRenameObjectTypeToObjectClass(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertObjectTypeToObjectClass(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddMVPropertyTable(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCompanyNameToCompanyId(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseOutgoingQueuePrimarykey(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseACLPrimarykey(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseBlobExternId(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseKeysChanges2(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseMVPropertiesPrimarykey(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseFixDBPluginGroups(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseFixDBPluginSendAs(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseMoveSubscribedList(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseSyncTimeIndex(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseAddStateKey(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseConvertToUnicode(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseConvertStoreUsername(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertRules(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertSearchFolders(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseConvertProperties(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateCounters(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateCommonProps(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCheckAttachments(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateTProperties(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertHierarchy(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseCreateDeferred(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertChanges(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertNames(ECDatabase *lpDatabase);
+
+ECRESULT UpdateDatabaseReceiveFolderToUnicode(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseClientUpdateStatus(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseConvertStores(ECDatabase *lpDatabase);
+ECRESULT UpdateDatabaseUpdateStores(ECDatabase *lpDatabase);
+
+ECRESULT UpdateWLinkRecordKeys(ECDatabase *lpDatabase);
+ECRESULT UpdateVersionsTbl(ECDatabase *db);
+ECRESULT UpdateChangesTbl(ECDatabase *db);
+ECRESULT UpdateABChangesTbl(ECDatabase *db);
+
+#endif // #ifndef ECDATABASEUPDATE_H
diff --git a/PKGBUILD b/PKGBUILD
index d9b55e2313cd..a5012cf4422b 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -7,7 +7,7 @@ groups=('zarafa'
replaces=('zarafa-server-arm')
pkgver=7.2.4.29
_pkgmajver=7.2
-pkgrel=112
+pkgrel=113
pkgdesc="Open Source Groupware Solution"
arch=('armv7h'
'armv6h'
@@ -132,15 +132,27 @@ source=("https://download.zarafa.com/community/final/${_pkgmajver}/${pkgver}/zcp
'zarafa-tools::git+https://github.com/zarafagroupware/zarafa-tools.git'
'python-zarafa::git+https://github.com/zarafagroupware/python-zarafa.git'
'zarafa-inspector::git+https://github.com/zarafagroupware/zarafa-inspector.git'
- 'zarafa-pietma::git+https://git.pietma.com/pietma/com-pietma-zarafa.git#tag=v0.13')
+ 'zarafa-pietma::git+https://git.pietma.com/pietma/com-pietma-zarafa.git#tag=v0.13'
+ 'ECDBDef.h'
+ 'ECDatabaseMySQL.cpp'
+ 'ECDatabaseUpdate.h'
+ 'ECDatabaseUpdate.cpp')
md5sums=('0790d8314fa4aef9788e5020be832535'
'SKIP'
'SKIP'
'SKIP'
+ 'SKIP'
+ 'SKIP'
+ 'SKIP'
+ 'SKIP'
'SKIP')
prepare() {
cd ${srcdir}/zcp-${pkgver}
+ cp -f ${srcdir}/ECDBDef.h provider/libserver/
+ cp -f ${srcdir}/ECDatabaseMySQL.cpp provider/libserver/
+ cp -f ${srcdir}/ECDatabaseUpdate.h provider/libserver/
+ cp -f ${srcdir}/ECDatabaseUpdate.cpp provider/libserver/
return 0
}