/* * 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 . * */ #include #include "ECDatabase.h" #include "ECDatabaseUpdate.h" #include #include #include "ECDBDef.h" #include "ECUserManagement.h" #include #include #include #include "ECConversion.h" #include "SOAPUtils.h" #include "ECSearchFolders.h" #include "ZarafaICS.h" #include #include "ECStringCompat.h" #include "ECMAPI.h" #include #include #include #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 lstFolders; // The list of folders std::list::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 sObjectList; std::list::const_iterator sObjectIter; std::map sObjectMap; std::map::const_iterator sObjectMapIter; std::list sRelationList; std::list::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 syncIds; list queries; bool fFirst = true; string strSyncId; list::const_iterator queryIter; list::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 mapTypes; std::map::const_iterator iTypes; std::list 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(1, ACTIVE_USER)); // USEROBJECT_TYPE_USER mapTypes.insert(std::pair(2, DISTLIST_GROUP)); // USEROBJECT_TYPE_GROUP mapTypes.insert(std::pair(3, NONACTIVE_CONTACT)); // USEROBJECT_TYPE_CONTACT (unused, but who knows..) mapTypes.insert(std::pair(4, CONTAINER_COMPANY)); // USEROBJECT_TYPE_COMPANY mapTypes.insert(std::pair(5, NONACTIVE_USER)); // USEROBJECT_TYPE_NONACTIVE mapTypes.insert(std::pair(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::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 mapIdToName; std::map::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(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 > lstRelations; std::list >::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(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 mapStoreInbox; std::map::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(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"); }