diff options
author | MartiMcFly | 2016-12-20 22:58:16 +0100 |
---|---|---|
committer | MartiMcFly | 2016-12-20 22:58:16 +0100 |
commit | 6e8c2d6f06bd7e575e2cd4dd5e1d3d5a269bc24e (patch) | |
tree | a498923dfa41a05162ecc93a57bff28f0195778e /ECDatabaseUpdate.cpp | |
parent | 6ff3ed1e7de6fd2a927344e2dca78a0c03897718 (diff) | |
download | aur-6e8c2d6f06bd7e575e2cd4dd5e1d3d5a269bc24e.tar.gz |
database fix which made z-push break
Diffstat (limited to 'ECDatabaseUpdate.cpp')
-rw-r--r-- | ECDatabaseUpdate.cpp | 2719 |
1 files changed, 2719 insertions, 0 deletions
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"); +} |