// -------------------------------------------------------------------------------- // DownOE5.cpp // -------------------------------------------------------------------------------- #include "pch.hxx" #include "utility.h" #include "migrate.h" #include "migerror.h" #include "structs.h" #include "resource.h" #include #include const static BYTE rgbZero[4] = {0}; //-------------------------------------------------------------------------- // PFNREADTYPEDATA //-------------------------------------------------------------------------- typedef void (APIENTRY *PFNREADTYPEDATA)(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs); //-------------------------------------------------------------------------- // g_rgpfnReadTypeData //-------------------------------------------------------------------------- extern const PFNREADTYPEDATA g_rgpfnReadTypeData[CDT_LASTTYPE]; //-------------------------------------------------------------------------- // ReadTypeData //-------------------------------------------------------------------------- #define ReadTypeData(_pbSource, _cbLength, _pColumn, _pRecord, _pcPtrRefs) \ (*(g_rgpfnReadTypeData[(_pColumn)->type]))(_pbSource, _cbLength, (_pColumn), _pRecord, _pcPtrRefs) //-------------------------------------------------------------------------- inline void ReadTypeDataFILETIME(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == sizeof(FILETIME)); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(FILETIME)); } //-------------------------------------------------------------------------- inline void ReadTypeDataFIXSTRA(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == pColumn->cbSize); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, pColumn->cbSize); } //-------------------------------------------------------------------------- inline void ReadTypeDataVARSTRA(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert((LPSTR)((LPBYTE)pbSource)[cbLength - 1] == '\0'); *((LPSTR *)((LPBYTE)pRecord + pColumn->ofBinding)) = (LPSTR)((LPBYTE)pbSource); (*pcPtrRefs)++; } //-------------------------------------------------------------------------- inline void ReadTypeDataBYTE(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == sizeof(BYTE)); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(BYTE)); } //-------------------------------------------------------------------------- inline void ReadTypeDataDWORD(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == sizeof(DWORD)); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(DWORD)); } //-------------------------------------------------------------------------- inline void ReadTypeDataWORD(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == sizeof(WORD)); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(WORD)); } //-------------------------------------------------------------------------- inline void ReadTypeDataSTREAM(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(cbLength == sizeof(FILEADDRESS)); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(FILEADDRESS)); } //-------------------------------------------------------------------------- inline void ReadTypeDataVARBLOB(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { LPBLOB pBlob = (LPBLOB)((LPBYTE)pRecord + pColumn->ofBinding); pBlob->cbSize = cbLength; if (pBlob->cbSize > 0) { pBlob->pBlobData = pbSource; (*pcPtrRefs)++; } else pBlob->pBlobData = NULL; } //-------------------------------------------------------------------------- inline void ReadTypeDataFIXBLOB(LPBYTE pbSource, DWORD cbLength, LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs) { Assert(pColumn->cbSize == cbLength); CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, pColumn->cbSize); } //-------------------------------------------------------------------------- const PFNREADTYPEDATA g_rgpfnReadTypeData[CDT_LASTTYPE] = { (PFNREADTYPEDATA)ReadTypeDataFILETIME, (PFNREADTYPEDATA)ReadTypeDataFIXSTRA, (PFNREADTYPEDATA)ReadTypeDataVARSTRA, (PFNREADTYPEDATA)ReadTypeDataBYTE, (PFNREADTYPEDATA)ReadTypeDataDWORD, (PFNREADTYPEDATA)ReadTypeDataWORD, (PFNREADTYPEDATA)ReadTypeDataSTREAM, (PFNREADTYPEDATA)ReadTypeDataVARBLOB, (PFNREADTYPEDATA)ReadTypeDataFIXBLOB }; // -------------------------------------------------------------------------------- // DowngradeReadMsgInfoV5 // -------------------------------------------------------------------------------- HRESULT DowngradeReadMsgInfoV5(LPRECORDBLOCKV5 pRecord, LPMESSAGEINFO pMsgInfo) { // Locals HRESULT hr=S_OK; DWORD i; DWORD cColumns; DWORD cbRead=0; DWORD cbLength; DWORD cbData; DWORD cPtrRefs; LPBYTE pbData; LPBYTE pbSource; LPDWORD prgdwOffset=(LPDWORD)((LPBYTE)pRecord + sizeof(RECORDBLOCKV5)); // Trace TraceCall("DowngradeReadMsgInfoV5"); // Set cbData cbData = (pRecord->cbRecord - sizeof(RECORDBLOCKV5) - (pRecord->cColumns * sizeof(DWORD))); // Allocate IF_NULLEXIT(pbData = (LPBYTE)g_pMalloc->Alloc(cbData)); // Free This pMsgInfo->pvMemory = pbData; // Set pbData pbSource = (LPBYTE)((LPBYTE)pRecord + sizeof(RECORDBLOCKV5) + (pRecord->cColumns * sizeof(DWORD))); // Copy the data CopyMemory(pbData, pbSource, cbData); // Compute number of columns to read cColumns = min(pRecord->cColumns, MSGCOL_LASTID); // Read the Record for (i=0; i cbData) { hr = TraceResult(MIGRATE_E_BADRECORDFORMAT); goto exit; } // ReadTypeData ReadTypeData(pbData + cbRead, cbLength, &g_MessageTableSchema.prgColumn[i], pMsgInfo, &cPtrRefs); // Increment cbRead cbRead += cbLength; } exit: // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeLocalStoreFileV5 // -------------------------------------------------------------------------------- HRESULT DowngradeLocalStoreFileV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo, LPMEMORYFILE pFile, LPPROGRESSINFO pProgress) { // Locals HRESULT hr=S_OK; DWORD cRecords=0; CHAR szIdxPath[MAX_PATH + MAX_PATH]; CHAR szMbxPath[MAX_PATH + MAX_PATH]; HANDLE hIdxFile=NULL; HANDLE hMbxFile=NULL; MESSAGEINFO MsgInfo={0}; IDXFILEHEADER IdxHeader; MBXFILEHEADER MbxHeader; MBXMESSAGEHEADER MbxMessage; IDXMESSAGEHEADER IdxMessage; LPRECORDBLOCKV5 pRecord; LPSTREAMBLOCK pStmBlock; LPBYTE pbData; DWORD faRecord; DWORD faIdxWrite; DWORD faMbxWrite; DWORD faStreamBlock; DWORD cbAligned; DWORD faMbxCurrent; LPTABLEHEADERV5 pHeader=(LPTABLEHEADERV5)pFile->pView; // Trace TraceCall("DowngradeLocalStoreFileV5"); // Set idx path ReplaceExtension(pInfo->szFilePath, ".idx", szIdxPath, ARRAYSIZE(szIdxPath)); // Set mbx path ReplaceExtension(pInfo->szFilePath, ".mbx", szMbxPath, ARRAYSIZE(szMbxPath)); // Delete Both Files DeleteFile(szIdxPath); DeleteFile(szMbxPath); // Open the idx file hIdxFile = CreateFile(szIdxPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hIdxFile) { hIdxFile = NULL; hr = TraceResult(MIGRATE_E_CANTOPENFILE); goto exit; } // Initialize Idx Header ZeroMemory(&IdxHeader, sizeof(IDXFILEHEADER)); IdxHeader.dwMagic = CACHEFILE_MAGIC; IdxHeader.ver = CACHEFILE_VER; IdxHeader.verBlob = 1; // this will force the .idx blobs to be rebuilt when imn 1.0 or oe v4.0 is run again // Write the header IF_FAILEXIT(hr = MyWriteFile(hIdxFile, 0, &IdxHeader, sizeof(IDXFILEHEADER))); // Open the mbx file hMbxFile = CreateFile(szMbxPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hMbxFile) { hMbxFile = NULL; hr = TraceResult(MIGRATE_E_CANTOPENFILE); goto exit; } // Initialize MBX Header ZeroMemory(&MbxHeader, sizeof(MBXFILEHEADER)); MbxHeader.dwMagic = MSGFILE_MAGIC; MbxHeader.ver = MSGFILE_VER; // Write the header IF_FAILEXIT(hr = MyWriteFile(hMbxFile, 0, &MbxHeader, sizeof(MBXFILEHEADER))); // Set First Record faRecord = pHeader->faFirstRecord; // Set faIdxWrite faIdxWrite = sizeof(IDXFILEHEADER); // Set faMbxWrite faMbxWrite = sizeof(MBXFILEHEADER); // While we have a record while(faRecord) { // Bad Length if (faRecord + sizeof(RECORDBLOCKV5) > pFile->cbSize) { hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); goto exit; } // Cast the Record pRecord = (LPRECORDBLOCKV5)((LPBYTE)pFile->pView + faRecord); // Invalid Record Signature if (faRecord != pRecord->faRecord) { hr = TraceResult(MIGRATE_E_BADRECORDSIGNATURE); goto exit; } // Bad Length if (faRecord + pRecord->cbRecord > pFile->cbSize) { hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); goto exit; } // Load MsgInfo IF_FAILEXIT(hr = DowngradeReadMsgInfoV5(pRecord, &MsgInfo)); // No Stream ? if (0 == MsgInfo.faStream) goto NextRecord; // Set msgidLast if ((DWORD)MsgInfo.idMessage > MbxHeader.msgidLast) MbxHeader.msgidLast = (DWORD)MsgInfo.idMessage; // Zero Out the Message Structures ZeroMemory(&MbxMessage, sizeof(MBXMESSAGEHEADER)); ZeroMemory(&IdxMessage, sizeof(IDXMESSAGEHEADER)); // Fill MbxMessage MbxMessage.dwMagic = MSGHDR_MAGIC; MbxMessage.msgid = (DWORD)MsgInfo.idMessage; // Fixup the Flags if (FALSE == ISFLAGSET(MsgInfo.dwFlags, ARF_READ)) FLAGSET(IdxMessage.dwState, MSG_UNREAD); if (ISFLAGSET(MsgInfo.dwFlags, ARF_VOICEMAIL)) FLAGSET(IdxMessage.dwState, MSG_VOICEMAIL); if (ISFLAGSET(MsgInfo.dwFlags, ARF_REPLIED)) FLAGSET(IdxMessage.dwState, MSG_REPLIED); if (ISFLAGSET(MsgInfo.dwFlags, ARF_FORWARDED)) FLAGSET(IdxMessage.dwState, MSG_FORWARDED); if (ISFLAGSET(MsgInfo.dwFlags, ARF_FLAGGED)) FLAGSET(IdxMessage.dwState, MSG_FLAGGED); if (ISFLAGSET(MsgInfo.dwFlags, ARF_RCPTSENT)) FLAGSET(IdxMessage.dwState, MSG_RCPTSENT); if (ISFLAGSET(MsgInfo.dwFlags, ARF_NOSECUI)) FLAGSET(IdxMessage.dwState, MSG_NOSECUI); if (ISFLAGSET(MsgInfo.dwFlags, ARF_NEWSMSG)) FLAGSET(IdxMessage.dwState, MSG_NEWSMSG); if (ISFLAGSET(MsgInfo.dwFlags, ARF_UNSENT)) FLAGSET(IdxMessage.dwState, MSG_UNSENT); if (ISFLAGSET(MsgInfo.dwFlags, ARF_SUBMITTED)) FLAGSET(IdxMessage.dwState, MSG_SUBMITTED); if (ISFLAGSET(MsgInfo.dwFlags, ARF_RECEIVED)) FLAGSET(IdxMessage.dwState, MSG_RECEIVED); // Save faMbxCurrent faMbxCurrent = faMbxWrite; // Validate alignment Assert((faMbxCurrent % 4) == 0); // Write the mbx header IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxCurrent, &MbxMessage, sizeof(MBXMESSAGEHEADER))); // Increment faMbxWrite faMbxWrite += sizeof(MBXMESSAGEHEADER); // Initialize dwMsgSize MbxMessage.dwMsgSize = sizeof(MBXMESSAGEHEADER); // Set faStreamBlock faStreamBlock = MsgInfo.faStream; // While we have stream block while(faStreamBlock) { // Bad Length if (faStreamBlock + sizeof(STREAMBLOCK) > pFile->cbSize) { hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); goto exit; } // Cast the Record pStmBlock = (LPSTREAMBLOCK)((LPBYTE)pFile->pView + faStreamBlock); // Invalid Record Signature if (faStreamBlock != pStmBlock->faThis) { hr = TraceResult(MIGRATE_E_BADSTREAMBLOCKSIGNATURE); goto exit; } // Bad Length if (faStreamBlock + pStmBlock->cbBlock > pFile->cbSize) { hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); goto exit; } // Set pbData pbData = (LPBYTE)((LPBYTE)(pStmBlock) + sizeof(STREAMBLOCK)); // Write into the stream IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxWrite, pbData, pStmBlock->cbData)); // Increment dwBodySize MbxMessage.dwBodySize += pStmBlock->cbData; // Increment dwMsgSize MbxMessage.dwMsgSize += pStmBlock->cbData; // Increment faMbxWrite faMbxWrite += pStmBlock->cbData; // Goto Next Block faStreamBlock = pStmBlock->faNext; } // Pad the Message on a dword boundary cbAligned = (faMbxWrite % 4); // cbAligned ? if (cbAligned) { // Reset cbAligned cbAligned = 4 - cbAligned; // Write the mbx header IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxWrite, (LPVOID)rgbZero, cbAligned)); // Increment faMbxWrite faMbxWrite += cbAligned; // Increment MbxMessage.dwMsgSize += cbAligned; } // Validate alignment Assert((faMbxWrite % 4) == 0); // Write the mbx header again IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxCurrent, &MbxMessage, sizeof(MBXMESSAGEHEADER))); // Fill IdxMessage IdxMessage.dwLanguage = (DWORD)MAKELONG(MsgInfo.wLanguage, MsgInfo.wHighlight); IdxMessage.msgid = (DWORD)MsgInfo.idMessage; IdxMessage.dwOffset = faMbxCurrent; IdxMessage.dwMsgSize = MbxMessage.dwMsgSize; IdxMessage.dwHdrOffset = 0; IdxMessage.dwSize = sizeof(IDXMESSAGEHEADER); IdxMessage.dwHdrSize = 0; IdxMessage.rgbHdr[4] = 0; // Write the mbx header IF_FAILEXIT(hr = MyWriteFile(hIdxFile, faIdxWrite, &IdxMessage, sizeof(IDXMESSAGEHEADER))); // Increment faIdxWrite faIdxWrite += IdxMessage.dwSize; // Increment cRecords cRecords++; NextRecord: // Progress IncrementProgress(pProgress, pInfo); // Cleanup SafeMemFree(MsgInfo.pvMemory); // Goto Next faRecord = pRecord->faNext; } // Set the Record Counts MbxHeader.cMsg = cRecords; IdxHeader.cMsg = cRecords; // Set the Flags IdxHeader.dwFlags = 1; // STOREINIT_MAIL MbxHeader.dwFlags = 1; // STOREINIT_MAIL // Get the Size of the idx file IdxHeader.cbValid = ::GetFileSize(hIdxFile, NULL); if (0xFFFFFFFF == IdxHeader.cbValid) { hr = TraceResult(MIGRATE_E_CANTGETFILESIZE); goto exit; } // Get the Size of the mbx file MbxHeader.cbValid = ::GetFileSize(hMbxFile, NULL); if (0xFFFFFFFF == MbxHeader.cbValid) { hr = TraceResult(MIGRATE_E_CANTGETFILESIZE); goto exit; } // Write the header IF_FAILEXIT(hr = MyWriteFile(hIdxFile, 0, &IdxHeader, sizeof(IDXFILEHEADER))); // Write the header IF_FAILEXIT(hr = MyWriteFile(hMbxFile, 0, &MbxHeader, sizeof(MBXFILEHEADER))); exit: // Cleanup SafeCloseHandle(hIdxFile); SafeCloseHandle(hMbxFile); SafeMemFree(MsgInfo.pvMemory); // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeRecordV5 // -------------------------------------------------------------------------------- HRESULT DowngradeRecordV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo, LPMEMORYFILE pFile, LPCHAINNODEV5 pNode, LPDWORD pcbRecord) { // Locals HRESULT hr=S_OK; DWORD cbRecord=0; DWORD cbOffsets; DWORD cbData; DWORD cb; LPBYTE pbData; LPBYTE pbStart; MESSAGEINFO MsgInfo={0}; RECORDBLOCKV5B1 RecordOld; LPRECORDBLOCKV5 pRecord; // Trace TraceCall("DowngradeRecordV5"); // Invalid if (pNode->faRecord + sizeof(RECORDBLOCKV5) > pFile->cbSize || 0 == pNode->faRecord) return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); // Access the Record pRecord = (LPRECORDBLOCKV5((LPBYTE)pFile->pView + pNode->faRecord)); // Bad Record if (pRecord->faRecord != pNode->faRecord) return TraceResult(MIGRATE_E_BADRECORDSIGNATURE); // Invalid if (pNode->faRecord + sizeof(RECORDBLOCKV5) + pRecord->cbRecord > pFile->cbSize) return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); // Fill an old record header RecordOld.faRecord = pRecord->faRecord; RecordOld.faNext = pRecord->faNext; RecordOld.faPrevious = pRecord->faPrevious; // Reformat the record if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile) { // Read the v5 record into a msginfo structure IF_FAILEXIT(hr = DowngradeReadMsgInfoV5(pRecord, &MsgInfo)); } // Compute offset table length cbOffsets = (pRecord->cColumns * sizeof(DWORD)); // Cast the datablock pbData = ((LPBYTE)pRecord + sizeof(RECORDBLOCKV5B1)); // Set Size cbData = (pRecord->cbRecord - cbOffsets - sizeof(RECORDBLOCKV5)); // Remove the Offset Table MoveMemory(pbData, ((LPBYTE)pRecord + sizeof(RECORDBLOCKV5) + cbOffsets), cbData); // Reformat the record if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile) { // Set pbStart pbStart = pbData; // DWORD - idMessage CopyMemory(pbData, &MsgInfo.idMessage, sizeof(MsgInfo.idMessage)); pbData += sizeof(MsgInfo.idMessage); // VERSION - dwFlags if (IMSG_PRI_HIGH == MsgInfo.wPriority) FLAGSET(MsgInfo.dwFlags, 0x00000200); else if (IMSG_PRI_LOW == MsgInfo.wPriority) FLAGSET(MsgInfo.dwFlags, 0x00000100); // VERSION - Normalized Subject - if (lstrcmpi(MsgInfo.pszSubject, MsgInfo.pszNormalSubj) != 0) MsgInfo.dwFlags = (DWORD)MAKELONG(MsgInfo.dwFlags, MAKEWORD(0, 4)); // DWORD - dwFlags CopyMemory(pbData, &MsgInfo.dwFlags, sizeof(MsgInfo.dwFlags)); pbData += sizeof(MsgInfo.dwFlags); // FILETIME - ftSent CopyMemory(pbData, &MsgInfo.ftSent, sizeof(MsgInfo.ftSent)); pbData += sizeof(MsgInfo.ftSent); // DWORD - cLines CopyMemory(pbData, &MsgInfo.cLines, sizeof(MsgInfo.cLines)); pbData += sizeof(MsgInfo.cLines); // DWORD - faStream CopyMemory(pbData, &MsgInfo.faStream, sizeof(MsgInfo.faStream)); pbData += sizeof(MsgInfo.faStream); // VERSION - DWORD - cbArticle CopyMemory(pbData, &MsgInfo.cbMessage, sizeof(MsgInfo.cbMessage)); pbData += sizeof(MsgInfo.cbMessage); // FILETIME - ftDownloaded CopyMemory(pbData, &MsgInfo.ftDownloaded, sizeof(MsgInfo.ftDownloaded)); pbData += sizeof(MsgInfo.ftDownloaded); // LPSTR - pszMessageId cb = lstrlen(MsgInfo.pszMessageId) + 1; CopyMemory(pbData, MsgInfo.pszMessageId, cb); pbData += cb; // LPSTR - pszSubject cb = lstrlen(MsgInfo.pszSubject) + 1; CopyMemory(pbData, MsgInfo.pszSubject, cb); pbData += cb; // LPSTR - pszFromHeader cb = lstrlen(MsgInfo.pszFromHeader) + 1; CopyMemory(pbData, MsgInfo.pszFromHeader, cb); pbData += cb; // LPSTR - pszReferences cb = lstrlen(MsgInfo.pszReferences) + 1; CopyMemory(pbData, MsgInfo.pszReferences, cb); pbData += cb; // LPSTR - pszXref cb = lstrlen(MsgInfo.pszXref) + 1; CopyMemory(pbData, MsgInfo.pszXref, cb); pbData += cb; // LPSTR - pszServer cb = lstrlen(MsgInfo.pszServer) + 1; CopyMemory(pbData, MsgInfo.pszServer, cb); pbData += cb; // LPSTR - pszDisplayFrom cb = lstrlen(MsgInfo.pszDisplayFrom) + 1; CopyMemory(pbData, MsgInfo.pszDisplayFrom, cb); pbData += cb; // LPSTR - pszEmailFrom cb = lstrlen(MsgInfo.pszEmailFrom) + 1; CopyMemory(pbData, MsgInfo.pszEmailFrom, cb); pbData += cb; // Going to V4 ? if (DOWNGRADE_V5_TO_V4 == tyMigrate) { // WORD - wLanguage CopyMemory(pbData, &MsgInfo.wLanguage, sizeof(MsgInfo.wLanguage)); pbData += sizeof(MsgInfo.wLanguage); // WORD - wReserved MsgInfo.wHighlight = 0; CopyMemory(pbData, &MsgInfo.wHighlight, sizeof(MsgInfo.wHighlight)); pbData += sizeof(MsgInfo.wHighlight); // DWORD - cbMessage CopyMemory(pbData, &MsgInfo.cbMessage, sizeof(MsgInfo.cbMessage)); pbData += sizeof(MsgInfo.cbMessage); // DWORD - ftReceived CopyMemory(pbData, &MsgInfo.ftReceived, sizeof(MsgInfo.ftReceived)); pbData += sizeof(MsgInfo.ftReceived); // LPSTR - pszDisplayTo cb = lstrlen(MsgInfo.pszDisplayTo) + 1; CopyMemory(pbData, MsgInfo.pszDisplayTo, cb); pbData += cb; } // Add on Reserved cbRecord = (40 + sizeof(RECORDBLOCKV5B1) + (pbData - pbStart)); // Better be smaller Assert(cbRecord <= pRecord->cbRecord); } // Otherwise, much easier else { // Set Size cbRecord = (pRecord->cbRecord - cbOffsets - sizeof(RECORDBLOCKV5)) + sizeof(RECORDBLOCKV5B1); } // Set the Record Size RecordOld.cbRecord = cbRecord; // Write the new record header CopyMemory((LPBYTE)pRecord, &RecordOld, sizeof(RECORDBLOCKV5B1)); // Return size *pcbRecord = cbRecord; exit: // Cleanup SafeMemFree(MsgInfo.pvMemory); // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeIndexV5 // -------------------------------------------------------------------------------- HRESULT DowngradeIndexV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo, LPMEMORYFILE pFile, LPPROGRESSINFO pProgress, DWORD faRootChain, DWORD faChain) { // Locals HRESULT hr=S_OK; LONG i; LPCHAINBLOCKV5 pChain; CHAINBLOCKV5B1 ChainOld; DWORD cbRecord; // Trace TraceCall("DowngradeIndexV5"); // Nothing to validate if (0 == faChain) return S_OK; // Out-of-bounds if (faChain + CB_CHAIN_BLOCKV5 > pFile->cbSize) return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS); // De-ref the block pChain = (LPCHAINBLOCKV5)((LPBYTE)pFile->pView + faChain); // Out-of-Bounds if (pChain->faStart != faChain) return TraceResult(MIGRATE_E_BADCHAINSIGNATURE); // Too many nodes if (pChain->cNodes > BTREE_ORDER) return TraceResult(MIGRATE_E_TOOMANYCHAINNODES); // Validate Minimum Filled Constraint if (pChain->cNodes < BTREE_MIN_CAP && pChain->faStart != faRootChain) return TraceResult(MIGRATE_E_BADMINCAPACITY); // Go to the left IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, pFile, pProgress, faRootChain, pChain->faLeftChain)); // Convert pChain to ChainOld ChainOld.faStart = pChain->faStart; ChainOld.cNodes = pChain->cNodes; ChainOld.faLeftChain = pChain->faLeftChain; // Loop throug right chains for (i=0; icNodes; i++) { // Bump Progress IncrementProgress(pProgress, pInfo); /// Downgrad this record IF_FAILEXIT(hr = DowngradeRecordV5(tyMigrate, pInfo, pFile, &pChain->rgNode[i], &cbRecord)); // Update Old Node ChainOld.rgNode[i].faRecord = pChain->rgNode[i].faRecord; ChainOld.rgNode[i].cbRecord = cbRecord; ChainOld.rgNode[i].faRightChain = pChain->rgNode[i].faRightChain; // Validate the Right Chain IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, pFile, pProgress, faRootChain, pChain->rgNode[i].faRightChain)); } // Write this new chain CopyMemory((LPBYTE)pChain, &ChainOld, CB_CHAIN_BLOCKV5B1); exit: // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeFileV5 // -------------------------------------------------------------------------------- HRESULT DowngradeFileV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo, LPPROGRESSINFO pProgress) { // Locals HRESULT hr=S_OK; MEMORYFILE File={0}; TABLEHEADERV5 HeaderV5; LPTABLEHEADERV5B1 pHeaderV5B1; CHAR szDstFile[MAX_PATH + MAX_PATH]; // Trace TraceCall("DowngradeFileV5"); // Local message file if (FILE_IS_LOCAL_MESSAGES == pInfo->tyFile) { // Get the File Header IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File)); // UpgradeLocalStoreFileV5 IF_FAILEXIT(hr = DowngradeLocalStoreFileV5(tyMigrate, pInfo, &File, pProgress)); } // Old News or Imap file else { // Create xxx.nch file if (FILE_IS_POP3UIDL == pInfo->tyFile) ReplaceExtension(pInfo->szFilePath, ".dat", szDstFile, ARRAYSIZE(szDstFile)); else ReplaceExtension(pInfo->szFilePath, ".nch", szDstFile, ARRAYSIZE(szDstFile)); // Copy the file if (0 == CopyFile(pInfo->szFilePath, szDstFile, FALSE)) { hr = TraceResult(MIGRATE_E_CANTCOPYFILE); goto exit; } // Get the File Header IF_FAILEXIT(hr = OpenMemoryFile(szDstFile, &File)); // Copy Table Header CopyMemory(&HeaderV5, (LPBYTE)File.pView, sizeof(TABLEHEADERV5)); // De-Ref the header pHeaderV5B1 = (LPTABLEHEADERV5B1)File.pView; // Fixup the Header ZeroMemory(pHeaderV5B1, sizeof(TABLEHEADERV5B1)); pHeaderV5B1->dwSignature = HeaderV5.dwSignature; pHeaderV5B1->wMajorVersion = (WORD)HeaderV5.dwMajorVersion; pHeaderV5B1->faRootChain = HeaderV5.rgfaIndex[0]; pHeaderV5B1->faFreeRecordBlock = HeaderV5.faFreeRecordBlock; pHeaderV5B1->faFirstRecord = HeaderV5.faFirstRecord; pHeaderV5B1->faLastRecord = HeaderV5.faLastRecord; pHeaderV5B1->cRecords = HeaderV5.cRecords; pHeaderV5B1->cbAllocated = HeaderV5.cbAllocated; pHeaderV5B1->cbFreed = HeaderV5.cbFreed; pHeaderV5B1->dwReserved1 = 0; pHeaderV5B1->dwReserved2 = 0; pHeaderV5B1->cbUserData = HeaderV5.cbUserData; pHeaderV5B1->cDeletes = 0; pHeaderV5B1->cInserts = 0; pHeaderV5B1->cActiveThreads = 0; pHeaderV5B1->dwReserved3 = 0; pHeaderV5B1->cbStreams = HeaderV5.cbStreams; pHeaderV5B1->faFreeStreamBlock = HeaderV5.faFreeStreamBlock; pHeaderV5B1->faFreeChainBlock = HeaderV5.faFreeChainBlock; pHeaderV5B1->faNextAllocate = HeaderV5.faNextAllocate; pHeaderV5B1->dwNextId = HeaderV5.dwNextId; pHeaderV5B1->AllocateRecord = HeaderV5.AllocateRecord; pHeaderV5B1->AllocateChain = HeaderV5.AllocateChain; pHeaderV5B1->AllocateStream = HeaderV5.AllocateStream; pHeaderV5B1->fCorrupt = FALSE; pHeaderV5B1->fCorruptCheck = TRUE; // DowngradeIndexV5 IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, &File, pProgress, pHeaderV5B1->faRootChain, pHeaderV5B1->faRootChain)); // Reset the version pHeaderV5B1->wMajorVersion = OBJECTDB_VERSION_PRE_V5; // Set the Minor Version if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile) pHeaderV5B1->wMinorVersion = ACACHE_VERSION_PRE_V5; // Folder cache version else if (FILE_IS_LOCAL_FOLDERS == pInfo->tyFile || FILE_IS_IMAP_FOLDERS == pInfo->tyFile) pHeaderV5B1->wMinorVersion = FLDCACHE_VERSION_PRE_V5; // UIDL Cache Version else if (FILE_IS_POP3UIDL == pInfo->tyFile) pHeaderV5B1->wMinorVersion = UIDCACHE_VERSION_PRE_V5; // Bad mojo else Assert(FALSE); } exit: // Cleanup CloseMemoryFile(&File); // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeProcessFileListV5 // -------------------------------------------------------------------------------- HRESULT DowngradeProcessFileListV5(LPFILEINFO pHead, LPDWORD pcMax, LPDWORD pcbNeeded) { // Locals HRESULT hr=S_OK; MEMORYFILE File={0}; LPFILEINFO pCurrent; LPTABLEHEADERV5 pHeader; // Trace TraceCall("DowngradeProcessFileListV5"); // Invalid Arg Assert(pHead); // Init *pcMax = 0; *pcbNeeded = 0; // Loop for (pCurrent=pHead; pCurrent!=NULL; pCurrent=pCurrent->pNext) { // Get the File Header hr = OpenMemoryFile(pCurrent->szFilePath, &File); // Failure ? if (FAILED(hr)) { // Don't Migrate pCurrent->fMigrate = FALSE; // Set hrMigrate pCurrent->hrMigrate = hr; // Reset hr hr = S_OK; // Get the LastError pCurrent->dwLastError = GetLastError(); // Goto Next goto NextFile; } // De-Ref the header pHeader = (LPTABLEHEADERV5)File.pView; // Check the Signature... if (File.cbSize < sizeof(TABLEHEADERV5) || OBJECTDB_SIGNATURE != pHeader->dwSignature || OBJECTDB_VERSION_V5 != pHeader->dwMajorVersion) { // Not a file that should be migrate pCurrent->fMigrate = FALSE; // Set hrMigrate pCurrent->hrMigrate = MIGRATE_E_BADVERSION; // Goto Next goto NextFile; } // Save the Number of record pCurrent->cRecords = pHeader->cRecords; // Initialize counters InitializeCounters(&File, pCurrent, pcMax, pcbNeeded, FALSE); // Yes, Migrate pCurrent->fMigrate = TRUE; NextFile: // Close the File CloseMemoryFile(&File); } // Done return hr; } // -------------------------------------------------------------------------------- // DowngradeDeleteFilesV5 // -------------------------------------------------------------------------------- void DowngradeDeleteFilesV5(LPFILEINFO pHeadFile) { // Locals CHAR szDstFile[MAX_PATH + MAX_PATH]; LPFILEINFO pCurrent; // Trace TraceCall("DowngradeDeleteFilesV5"); // Delete all files for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext) { // Succeeded Assert(SUCCEEDED(pCurrent->hrMigrate)); // Delete the file DeleteFile(pCurrent->szFilePath); } // Done return; } // -------------------------------------------------------------------------------- // DowngradeDeleteIdxMbxNchDatFilesV5 // -------------------------------------------------------------------------------- void DowngradeDeleteIdxMbxNchDatFilesV5(LPFILEINFO pHeadFile) { // Locals CHAR szDstFile[MAX_PATH + MAX_PATH]; LPFILEINFO pCurrent; // Trace TraceCall("DowngradeDeleteIdxMbxNchDatFilesV5"); // Delete all old files for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext) { // If local message file, need to delete the idx file if (FILE_IS_LOCAL_MESSAGES == pCurrent->tyFile) { // Replace file extension ReplaceExtension(pCurrent->szFilePath, ".idx", szDstFile, ARRAYSIZE(szDstFile)); // Delete the file DeleteFile(szDstFile); // Replace file extension ReplaceExtension(pCurrent->szFilePath, ".mbx", szDstFile, ARRAYSIZE(szDstFile)); // Delete the file DeleteFile(szDstFile); } // Otherwise, pop3uidl.dat else if (FILE_IS_POP3UIDL == pCurrent->tyFile) { // Replace file extension ReplaceExtension(pCurrent->szFilePath, ".dat", szDstFile, ARRAYSIZE(szDstFile)); // Delete the file DeleteFile(szDstFile); } // Otherwise, it has a .nch extension else { // Replace file extension ReplaceExtension(pCurrent->szFilePath, ".nch", szDstFile, ARRAYSIZE(szDstFile)); // Delete the file DeleteFile(szDstFile); } } // Done return; } // -------------------------------------------------------------------------------- // DowngradeV5 // -------------------------------------------------------------------------------- HRESULT DowngradeV5(MIGRATETOTYPE tyMigrate, LPCSTR pszStoreRoot, LPPROGRESSINFO pProgress, LPFILEINFO *ppHeadFile) { // Locals HRESULT hr=S_OK; ENUMFILEINFO EnumInfo={0}; LPFILEINFO pCurrent; DWORD cbNeeded; DWORDLONG dwlFree; // Trace TraceCall("DowngradeV5"); // Initialize *ppHeadFile = NULL; // Setup the EnumFile Info EnumInfo.pszExt = ".dbx"; EnumInfo.pszFoldFile = "folders.dbx"; EnumInfo.pszUidlFile = "pop3uidl.dbx"; // Enumerate All ODB files in szStoreRoot... IF_FAILEXIT(hr = EnumerateStoreFiles(pszStoreRoot, DIR_IS_ROOT, NULL, &EnumInfo, ppHeadFile)); // Compute some Counts, and validate that the files are valid to migrate... IF_FAILEXIT(hr = DowngradeProcessFileListV5(*ppHeadFile, &pProgress->cMax, &cbNeeded)); // Delete all source files DowngradeDeleteIdxMbxNchDatFilesV5(*ppHeadFile); // Enought DiskSpace ? IF_FAILEXIT(hr = GetAvailableDiskSpace(pszStoreRoot, &dwlFree)); // Not Enought Diskspace if (((DWORDLONG) cbNeeded) > dwlFree) { // cbNeeded is DWORD and in this case we can downgrade dwlFree to DWORD g_cbDiskNeeded = cbNeeded; g_cbDiskFree = ((DWORD) dwlFree); hr = TraceResult(MIGRATE_E_NOTENOUGHDISKSPACE); goto exit; } // Loop through the files and migrate each one for (pCurrent=*ppHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext) { // Migrate this file ? if (pCurrent->fMigrate) { // Set Progress File SetProgressFile(pProgress, pCurrent); // Downgrade the file hr = pCurrent->hrMigrate = DowngradeFileV5(tyMigrate, pCurrent, pProgress); // Failure ? if (FAILED(pCurrent->hrMigrate)) { // Set Last Error pCurrent->dwLastError = GetLastError(); // Done break; } } } // Failure, delete all destination files if (FAILED(hr)) { // Delete.idx, .mbx and .nch fles DowngradeDeleteIdxMbxNchDatFilesV5(*ppHeadFile); } // Otherwise, delete source files else { // Delete all source files DowngradeDeleteFilesV5(*ppHeadFile); } exit: // Done return hr; }