Windows2003-3790/inetcore/outlookexpress/migrate/downoe5.cpp
2020-09-30 16:53:55 +02:00

1137 lines
37 KiB
C++

// --------------------------------------------------------------------------------
// DownOE5.cpp
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "utility.h"
#include "migrate.h"
#include "migerror.h"
#include "structs.h"
#include "resource.h"
#include <oestore.h>
#include <mimeole.h>
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<cColumns; i++)
{
// Compute cbLength
cbLength = (i + 1 == cColumns) ? (cbData - prgdwOffset[i]) : (prgdwOffset[i + 1] - prgdwOffset[i]);
// Bad-Record
if (prgdwOffset[i] != cbRead || cbRead + cbLength > 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; i<pChain->cNodes; 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;
}