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

2858 lines
91 KiB
C++

// --------------------------------------------------------------------------------
// upoe5.cpp
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "utility.h"
#include "migrate.h"
#include "migerror.h"
#include "structs.h"
#include "resource.h"
#define DEFINE_DIRECTDB
#include <shared.h>
#include <oestore.h>
#include <oerules.h>
#include <mimeole.h>
#include "msident.h"
// --------------------------------------------------------------------------------
// Linearly Incrementing Folder Id
// --------------------------------------------------------------------------------
static DWORD g_idFolderNext=1000;
extern BOOL g_fQuiet;
// --------------------------------------------------------------------------------
// FOLDERIDCHANGE
// --------------------------------------------------------------------------------
typedef struct tagFOLDERIDCHANGE {
FOLDERID idOld;
FOLDERID idNew;
} FOLDERIDCHANGE, *LPFOLDERIDCHANGE;
// --------------------------------------------------------------------------------
// Forward Declarations
// --------------------------------------------------------------------------------
HRESULT SetIMAPSpecialFldrType(LPSTR pszAcctID, LPSTR pszFldrName, SPECIALFOLDER *psfType);
// --------------------------------------------------------------------------------
// SplitMailCacheBlob
// --------------------------------------------------------------------------------
HRESULT SplitMailCacheBlob(IMimePropertySet *pNormalizer, LPBYTE pbCacheInfo,
DWORD cbCacheInfo, LPMESSAGEINFO pMsgInfo, LPSTR *ppszNormal, LPBLOB pOffsets)
{
// Locals
HRESULT hr=S_OK;
ULONG ib;
ULONG cbTree;
ULONG cbProps;
WORD wVersion;
DWORD dw;
DWORD cbMsg;
DWORD dwFlags;
WORD wPriority;
PROPVARIANT Variant;
// Invalid Arg
Assert(pbCacheInfo && cbCacheInfo && pMsgInfo);
// Init
ZeroMemory(pOffsets, sizeof(BLOB));
// Read Version
ib = 0;
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&wVersion, sizeof(wVersion)));
// Version Check
if (wVersion != MSG_HEADER_VERSISON)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Read Flags
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dwFlags, sizeof(dwFlags)));
// IMF_ATTACHMENTS
if (ISFLAGSET(dwFlags, IMF_ATTACHMENTS))
FLAGSET(pMsgInfo->dwFlags, ARF_HASATTACH);
// IMF_SIGNED
if (ISFLAGSET(dwFlags, IMF_SIGNED))
FLAGSET(pMsgInfo->dwFlags, ARF_SIGNED);
// IMF_ENCRYPTED
if (ISFLAGSET(dwFlags, IMF_ENCRYPTED))
FLAGSET(pMsgInfo->dwFlags, ARF_ENCRYPTED);
// IMF_VOICEMAIL
if (ISFLAGSET(dwFlags, IMF_VOICEMAIL))
FLAGSET(pMsgInfo->dwFlags, ARF_VOICEMAIL);
// IMF_NEWS
if (ISFLAGSET(dwFlags, IMF_NEWS))
FLAGSET(pMsgInfo->dwFlags, ARF_NEWSMSG);
// Read Reserved
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
// Read Message Size
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&cbMsg, sizeof(cbMsg)));
// Read Byte Count for the content list
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&cbTree, sizeof(cbTree)));
// Does the user want the tree ?
if (cbTree)
{
pOffsets->pBlobData = (pbCacheInfo + ib);
pOffsets->cbSize = cbTree;
}
// Increment passed the tree
ib += cbTree;
// Read Byte Count for the content list
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&cbProps, sizeof(cbProps)));
// Partial Number
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&pMsgInfo->dwPartial, sizeof(pMsgInfo->dwPartial)));
// Receive Time
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&pMsgInfo->ftReceived, sizeof(pMsgInfo->ftReceived)));
// Sent Time
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&pMsgInfo->ftSent, sizeof(pMsgInfo->ftSent)));
// Priority
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&wPriority, sizeof(wPriority)));
// Pritority
pMsgInfo->wPriority = wPriority;
// Subject
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszSubject = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Init the Normalizer
pNormalizer->InitNew();
// Set the Subject
Variant.vt = VT_LPSTR;
Variant.pszVal = pMsgInfo->pszSubject;
// Set the Property
IF_FAILEXIT(hr = pNormalizer->SetProp(PIDTOSTR(PID_HDR_SUBJECT), 0, &Variant));
// Get the Normalized Subject back out
if (SUCCEEDED(pNormalizer->GetProp(PIDTOSTR(PID_ATT_NORMSUBJ), 0, &Variant)))
*ppszNormal = pMsgInfo->pszNormalSubj = Variant.pszVal;
// Otherwise, just use the subject
else
pMsgInfo->pszNormalSubj = pMsgInfo->pszSubject;
// Display To
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszDisplayTo = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Display From
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszDisplayFrom = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Server
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszServer = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// UIDL
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszUidl = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// User Name
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
//pMsgInfo->pszUserName = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Account Name
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszAcctName = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Partial Id
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszPartialId = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Forward To
IF_FAILEXIT(hr = BlobReadData(pbCacheInfo, cbCacheInfo, &ib, (LPBYTE)&dw, sizeof(dw)));
pMsgInfo->pszForwardTo = (LPSTR)(pbCacheInfo + ib);
ib += dw;
// Sanity Check
Assert(ib == cbCacheInfo);
exit:
// Done
return hr;
}
//--------------------------------------------------------------------------
// GetMsgInfoFromPropertySet
//--------------------------------------------------------------------------
HRESULT GetMsgInfoFromPropertySet(
/* in */ IMimePropertySet *pPropertySet,
/* in,out */ LPMESSAGEINFO pMsgInfo)
{
// Locals
HRESULT hr=S_OK;
IMSGPRIORITY priority;
PROPVARIANT Variant;
SYSTEMTIME st;
FILETIME ftCurrent;
IMimeAddressTable *pAdrTable=NULL;
// Trace
TraceCall("GetMsgInfoFromPropertySet");
// Invalid Args
Assert(pPropertySet && pMsgInfo);
// Default Sent and Received Times...
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ftCurrent);
// Set Variant tyStore
Variant.vt = VT_UI4;
// Priority
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_PRIORITY), 0, &Variant)))
{
// Set Priority
pMsgInfo->wPriority = (WORD)Variant.ulVal;
}
// Partial Numbers...
if (pPropertySet->IsContentType(STR_CNT_MESSAGE, STR_SUB_PARTIAL) == S_OK)
{
// Locals
WORD cParts=0, iPart=0;
// Get Total
if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_TOTAL, NOFLAGS, &Variant)))
cParts = (WORD)Variant.ulVal;
// Get Number
if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_NUMBER, NOFLAGS, &Variant)))
iPart = (WORD)Variant.ulVal;
// Set Parts
pMsgInfo->dwPartial = MAKELONG(cParts, iPart);
}
// Otherwise, check for user property
else if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_COMBINED), NOFLAGS, &Variant)))
{
// Set the Partial Id
pMsgInfo->dwPartial = Variant.ulVal;
}
// Getting some file times
Variant.vt = VT_FILETIME;
// Get Received Time...
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_RECVTIME), 0, &Variant)))
pMsgInfo->ftReceived = Variant.filetime;
else
pMsgInfo->ftReceived = ftCurrent;
// Get Sent Time...
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &Variant)))
pMsgInfo->ftSent = Variant.filetime;
else
pMsgInfo->ftSent = ftCurrent;
// Get Address Table
IF_FAILEXIT(hr = pPropertySet->BindToObject(IID_IMimeAddressTable, (LPVOID *)&pAdrTable));
// Display From
pAdrTable->GetFormat(IAT_FROM, AFT_DISPLAY_FRIENDLY, &pMsgInfo->pszDisplayFrom);
// Display To
pAdrTable->GetFormat(IAT_TO, AFT_DISPLAY_FRIENDLY, &pMsgInfo->pszDisplayTo);
// String Properties
Variant.vt = VT_LPSTR;
// pszDisplayFrom as newsgroups
if (NULL == pMsgInfo->pszDisplayFrom && SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, &Variant)))
pMsgInfo->pszDisplayFrom = Variant.pszVal;
// pszMessageId
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &Variant)))
pMsgInfo->pszMessageId = Variant.pszVal;
// pszXref
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_XREF), NOFLAGS, &Variant)))
pMsgInfo->pszXref = Variant.pszVal;
// pszReferences
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(STR_HDR_REFS), NOFLAGS, &Variant)))
pMsgInfo->pszReferences = Variant.pszVal;
// pszSubject
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &Variant)))
pMsgInfo->pszSubject = Variant.pszVal;
// Normalized Subject
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_NORMSUBJ), NOFLAGS, &Variant)))
pMsgInfo->pszNormalSubj = Variant.pszVal;
// pszAccount
if (SUCCEEDED(pPropertySet->GetProp(STR_ATT_ACCOUNTNAME, NOFLAGS, &Variant)))
pMsgInfo->pszAcctName = Variant.pszVal;
// pszServer
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SERVER), NOFLAGS, &Variant)))
pMsgInfo->pszServer = Variant.pszVal;
// pszUidl
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_UIDL), NOFLAGS, &Variant)))
pMsgInfo->pszUidl = Variant.pszVal;
// pszPartialId
if (pMsgInfo->dwPartial != 0 && SUCCEEDED(pPropertySet->GetProp(STR_PAR_ID, NOFLAGS, &Variant)))
pMsgInfo->pszPartialId = Variant.pszVal;
// ForwardTo
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_FORWARDTO), NOFLAGS, &Variant)))
pMsgInfo->pszForwardTo = Variant.pszVal;
exit:
// Cleanup
SafeRelease(pAdrTable);
// Done
return hr;
}
//--------------------------------------------------------------------------
// GetMsgInfoFromMessage
//--------------------------------------------------------------------------
HRESULT GetMsgInfoFromMessage(IMimeMessage *pMessage, LPMESSAGEINFO pMsgInfo,
LPBLOB pOffsets)
{
// Locals
HRESULT hr=S_OK;
DWORD dwImf;
IMSGPRIORITY priority;
PROPVARIANT Variant;
SYSTEMTIME st;
FILETIME ftCurrent;
CByteStream cByteStm;
IMimePropertySet *pPropertySet=NULL;
// Trace
TraceCall("GetMsgInfoFromMessage");
// Invalid Args
Assert(pMessage && pMsgInfo);
// Get the Root Property Set from the Message
IF_FAILEXIT(hr = pMessage->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pPropertySet));
// File pMsgInfo from pPropertySet
IF_FAILEXIT(hr = GetMsgInfoFromPropertySet(pPropertySet, pMsgInfo));
// Get Message Flags
if (SUCCEEDED(pMessage->GetFlags(&dwImf)))
{
// IMF_ATTACHMENTS
if (ISFLAGSET(dwImf, IMF_ATTACHMENTS))
FLAGSET(pMsgInfo->dwFlags, ARF_HASATTACH);
// IMF_SIGNED
if (ISFLAGSET(dwImf, IMF_SIGNED))
FLAGSET(pMsgInfo->dwFlags, ARF_SIGNED);
// IMF_ENCRYPTED
if (ISFLAGSET(dwImf, IMF_ENCRYPTED))
FLAGSET(pMsgInfo->dwFlags, ARF_ENCRYPTED);
// IMF_VOICEMAIL
if (ISFLAGSET(dwImf, IMF_VOICEMAIL))
FLAGSET(pMsgInfo->dwFlags, ARF_VOICEMAIL);
// IMF_NEWS
if (ISFLAGSET(dwImf, IMF_NEWS))
FLAGSET(pMsgInfo->dwFlags, ARF_NEWSMSG);
}
// Get the Message Size
pMessage->GetMessageSize(&pMsgInfo->cbMessage, 0);
// Create the offset table
if (SUCCEEDED(pMessage->SaveOffsetTable(&cByteStm, 0)))
{
// pull the Bytes out of cByteStm
cByteStm.AcquireBytes(&pOffsets->cbSize, &pOffsets->pBlobData, ACQ_DISPLACE);
}
exit:
// Cleanup
SafeRelease(pPropertySet);
// Done
return hr;
}
//--------------------------------------------------------------------------
// FreeMsgInfo
//--------------------------------------------------------------------------
void FreeMsgInfo(
/* in,out */ LPMESSAGEINFO pMsgInfo)
{
// Trace
TraceCall("FreeMsgInfo");
// Invalid Args
Assert(pMsgInfo && NULL == pMsgInfo->pAllocated);
// Free The Dude
g_pMalloc->Free(pMsgInfo->pszMessageId);
g_pMalloc->Free(pMsgInfo->pszNormalSubj);
g_pMalloc->Free(pMsgInfo->pszSubject);
g_pMalloc->Free(pMsgInfo->pszFromHeader);
g_pMalloc->Free(pMsgInfo->pszReferences);
g_pMalloc->Free(pMsgInfo->pszXref);
g_pMalloc->Free(pMsgInfo->pszServer);
g_pMalloc->Free(pMsgInfo->pszDisplayFrom);
g_pMalloc->Free(pMsgInfo->pszEmailFrom);
g_pMalloc->Free(pMsgInfo->pszDisplayTo);
g_pMalloc->Free(pMsgInfo->pszUidl);
g_pMalloc->Free(pMsgInfo->pszPartialId);
g_pMalloc->Free(pMsgInfo->pszForwardTo);
g_pMalloc->Free(pMsgInfo->pszAcctName);
g_pMalloc->Free(pMsgInfo->pszAcctId);
// Zero It
ZeroMemory(pMsgInfo, sizeof(MESSAGEINFO));
}
// --------------------------------------------------------------------------------
// UpgradeLocalStoreFileV5
// --------------------------------------------------------------------------------
HRESULT UpgradeLocalStoreFileV5(LPFILEINFO pInfo, LPMEMORYFILE pFile,
IDatabase *pDB, LPPROGRESSINFO pProgress, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
CHAR szIdxPath[MAX_PATH];
DWORD i;
LPBYTE pbStream;
LPBYTE pbCacheBlob;
SYSTEMTIME st;
MESSAGEINFO MsgInfo={0};
LPSTR pszNormal=NULL;
MESSAGEINFO MsgInfoFree={0};
DWORD faIdxRead;
IStream *pStream=NULL;
IMimeMessage *pMessage=NULL;
BLOB Offsets;
LPBYTE pbFree=NULL;
MEMORYFILE IdxFile;
LPMEMORYFILE pIdxFile=NULL;
LPMEMORYFILE pMbxFile=pFile;
LPMBXFILEHEADER pMbxHeader=NULL;
LPIDXFILEHEADER pIdxHeader=NULL;
LPIDXMESSAGEHEADER pIdxMessage=NULL;
LPMBXMESSAGEHEADER pMbxMessage=NULL;
IMimePropertySet *pNormalizer=NULL;
LARGE_INTEGER liOrigin={0,0};
// Trace
TraceCall("UpgradeLocalStoreFileV5");
// Get System Time
GetSystemTime(&st);
// Create a Property Set for Normalizing Subjects
IF_FAILEXIT(hr = CoCreateInstance(CLSID_IMimePropertySet, NULL, CLSCTX_INPROC_SERVER, IID_IMimePropertySet, (LPVOID *)&pNormalizer));
// Split the Path
ReplaceExtension(pInfo->szFilePath, ".idx", szIdxPath, ARRAYSIZE(szIdxPath));
// Open the memory file
hr = OpenMemoryFile(szIdxPath, &IdxFile);
if (FAILED(hr))
{
*pfContinue = TRUE;
TraceResult(hr);
goto exit;
}
// Set pIdxFile
pIdxFile = &IdxFile;
// Don't use pFile
pFile = NULL;
// Read the Mbx File Header
pMbxHeader = (LPMBXFILEHEADER)(pMbxFile->pView);
// Read the Idx File Header
pIdxHeader = (LPIDXFILEHEADER)(pIdxFile->pView);
// Validate the Version of th idx file
if (pIdxHeader->ver != CACHEFILE_VER || pIdxHeader->dwMagic != CACHEFILE_MAGIC)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_INVALIDIDXHEADER);
goto exit;
}
// Setup faIdxRead
faIdxRead = sizeof(IDXFILEHEADER);
// Prepare to Loop
for (i=0; i<pIdxHeader->cMsg; i++)
{
// Done
if (faIdxRead >= pIdxFile->cbSize)
break;
// Read an idx message header
pIdxMessage = (LPIDXMESSAGEHEADER)((LPBYTE)pIdxFile->pView + faIdxRead);
// If this message is not marked as deleted...
if (ISFLAGSET(pIdxMessage->dwState, MSG_DELETED))
goto NextMessage;
// Zero Out the MsgInfo Structure
ZeroMemory(&MsgInfo, sizeof(MESSAGEINFO));
// Start filling message
MsgInfo.idMessage = (MESSAGEID)IntToPtr(pIdxMessage->msgid);
// Fixup the Flags
if (FALSE == ISFLAGSET(pIdxMessage->dwState, MSG_UNREAD))
FLAGSET(MsgInfo.dwFlags, ARF_READ);
if (ISFLAGSET(pIdxMessage->dwState, MSG_VOICEMAIL))
FLAGSET(MsgInfo.dwFlags, ARF_VOICEMAIL);
if (ISFLAGSET(pIdxMessage->dwState, MSG_REPLIED))
FLAGSET(MsgInfo.dwFlags, ARF_REPLIED);
if (ISFLAGSET(pIdxMessage->dwState, MSG_FORWARDED))
FLAGSET(MsgInfo.dwFlags, ARF_FORWARDED);
if (ISFLAGSET(pIdxMessage->dwState, MSG_FLAGGED))
FLAGSET(MsgInfo.dwFlags, ARF_FLAGGED);
if (ISFLAGSET(pIdxMessage->dwState, MSG_RCPTSENT))
FLAGSET(MsgInfo.dwFlags, ARF_RCPTSENT);
if (ISFLAGSET(pIdxMessage->dwState, MSG_NOSECUI))
FLAGSET(MsgInfo.dwFlags, ARF_NOSECUI);
if (ISFLAGSET(pIdxMessage->dwState, MSG_NEWSMSG))
FLAGSET(MsgInfo.dwFlags, ARF_NEWSMSG);
if (ISFLAGSET(pIdxMessage->dwState, MSG_UNSENT))
FLAGSET(MsgInfo.dwFlags, ARF_UNSENT);
if (ISFLAGSET(pIdxMessage->dwState, MSG_SUBMITTED))
FLAGSET(MsgInfo.dwFlags, ARF_SUBMITTED);
if (ISFLAGSET(pIdxMessage->dwState, MSG_RECEIVED))
FLAGSET(MsgInfo.dwFlags, ARF_RECEIVED);
// Zero Offsets
ZeroMemory(&Offsets, sizeof(BLOB));
// Do the Blob
if (pIdxHeader->verBlob == MAIL_BLOB_VER)
{
// Get the blob
pbCacheBlob = (LPBYTE)((LPBYTE)pIdxFile->pView + (faIdxRead + (sizeof(IDXMESSAGEHEADER) - 4)));
// Split the Cache Blob
if (FAILED(SplitMailCacheBlob(pNormalizer, pbCacheBlob, pIdxMessage->dwHdrSize, &MsgInfo, &pszNormal, &Offsets)))
goto NextMessage;
// Save the Language
MsgInfo.wLanguage = LOWORD(pIdxMessage->dwLanguage);
// Save the Highlight
MsgInfo.wHighlight = HIWORD(pIdxMessage->dwLanguage);
}
// Bad
if (pIdxMessage->dwOffset > pMbxFile->cbSize)
goto NextMessage;
// Lets read the message header in the mbx file to validate the msgids
pMbxMessage = (LPMBXMESSAGEHEADER)((LPBYTE)pMbxFile->pView + pIdxMessage->dwOffset);
// Set Sizes
MsgInfo.cbMessage = pMbxMessage->dwBodySize;
// Validate the Message Ids
if (pMbxMessage->msgid != pIdxMessage->msgid)
goto NextMessage;
// Check for magic
if (pMbxMessage->dwMagic != MSGHDR_MAGIC)
goto NextMessage;
// Has a Body
FLAGSET(MsgInfo.dwFlags, ARF_HASBODY);
// Create a Virtual Stream
IF_FAILEXIT(hr = pDB->CreateStream(&MsgInfo.faStream));
// Open the Stream
IF_FAILEXIT(hr = pDB->OpenStream(ACCESS_WRITE, MsgInfo.faStream, &pStream));
// Get the stream pointer
pbStream = (LPBYTE)((LPBYTE)pMbxFile->pView + (pIdxMessage->dwOffset + sizeof(MBXMESSAGEHEADER)));
// Write this
IF_FAILEXIT(hr = pStream->Write(pbStream, pMbxMessage->dwBodySize, NULL));
// Commit
IF_FAILEXIT(hr = pStream->Commit(STGC_DEFAULT));
// If not an OE4+ blob, then generate the msginfo from the message
if (pIdxHeader->verBlob != MAIL_BLOB_VER)
{
// Create an IMimeMessage
IF_FAILEXIT(hr = CoCreateInstance(CLSID_IMimeMessage, NULL, CLSCTX_INPROC_SERVER, IID_IMimeMessage, (LPVOID *)&pMessage));
// Rewind
if (FAILED(pStream->Seek(liOrigin, STREAM_SEEK_SET, NULL)))
goto NextMessage;
// Load the Message
if (FAILED(pMessage->Load(pStream)))
goto NextMessage;
// Get MsgInfo from the Message
if (FAILED(GetMsgInfoFromMessage(pMessage, &MsgInfo, &Offsets)))
goto NextMessage;
// Free
pbFree = Offsets.pBlobData;
// Free This MsgInfo
CopyMemory(&MsgInfoFree, &MsgInfo, sizeof(MESSAGEINFO));
}
// Set MsgInfo Offsets
MsgInfo.Offsets = Offsets;
// Save Downloaded Time
SystemTimeToFileTime(&st, &MsgInfo.ftDownloaded);
// Lookup Account Id from the Account Name...
if (MsgInfo.pszAcctName)
{
// Loop through the Accounts
for (DWORD i=0; i<g_AcctTable.cAccounts; i++)
{
// Is this the Account
if (lstrcmpi(g_AcctTable.prgAccount[i].szAcctName, MsgInfo.pszAcctName) == 0)
{
MsgInfo.pszAcctId = g_AcctTable.prgAccount[i].szAcctId;
break;
}
}
}
// Count
pInfo->cMessages++;
if (!ISFLAGSET(MsgInfo.dwFlags, ARF_READ))
pInfo->cUnread++;
// Migrated
FLAGSET(MsgInfo.dwFlags, 0x00000010);
// Store the Record
IF_FAILEXIT(hr = pDB->InsertRecord(&MsgInfo));
NextMessage:
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pInfo);
// Cleanup
SafeRelease(pStream);
SafeRelease(pMessage);
SafeMemFree(pszNormal);
SafeMemFree(pbFree);
FreeMsgInfo(&MsgInfoFree);
// Goto Next Header
Assert(pIdxMessage);
// Update faIdxRead
faIdxRead += pIdxMessage->dwSize;
}
exit:
// Cleanup
SafeRelease(pStream);
SafeRelease(pMessage);
SafeRelease(pNormalizer);
SafeMemFree(pszNormal);
SafeMemFree(pbFree);
FreeMsgInfo(&MsgInfoFree);
if (pIdxFile)
CloseMemoryFile(pIdxFile);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// GetRecordBlock
// --------------------------------------------------------------------------------
HRESULT GetRecordBlock(LPMEMORYFILE pFile, DWORD faRecord, LPRECORDBLOCKV5B1 *ppRecord,
LPBYTE *ppbData, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
// Trace
TraceCall("GetRecordBlock");
// Bad Length
if (faRecord + sizeof(RECORDBLOCKV5B1) > pFile->cbSize)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
goto exit;
}
// Cast the Record
(*ppRecord) = (LPRECORDBLOCKV5B1)((LPBYTE)pFile->pView + faRecord);
// Invalid Record Signature
if (faRecord != (*ppRecord)->faRecord)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_BADRECORDSIGNATURE);
goto exit;
}
// Bad Length
if (faRecord + (*ppRecord)->cbRecord > pFile->cbSize)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
goto exit;
}
// Set pbData
*ppbData = (LPBYTE)((LPBYTE)(*ppRecord) + sizeof(RECORDBLOCKV5B1));
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// GetStreamBlock
// --------------------------------------------------------------------------------
HRESULT GetStreamBlock(LPMEMORYFILE pFile, DWORD faBlock, LPSTREAMBLOCK *ppBlock,
LPBYTE *ppbData, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
// Trace
TraceCall("GetStreamBlock");
// Bad Length
if (faBlock + sizeof(STREAMBLOCK) > pFile->cbSize)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
goto exit;
}
// Cast the Record
(*ppBlock) = (LPSTREAMBLOCK)((LPBYTE)pFile->pView + faBlock);
// Invalid Record Signature
if (faBlock != (*ppBlock)->faThis)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_BADSTREAMBLOCKSIGNATURE);
goto exit;
}
// Bad Length
if (faBlock + (*ppBlock)->cbBlock > pFile->cbSize)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
goto exit;
}
// Set pbData
*ppbData = (LPBYTE)((LPBYTE)(*ppBlock) + sizeof(STREAMBLOCK));
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// UpgradePropTreeMessageFileV5
// --------------------------------------------------------------------------------
HRESULT UpgradePropTreeMessageFileV5(LPFILEINFO pInfo, LPMEMORYFILE pFile,
IDatabase *pDB, LPPROGRESSINFO pProgress, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
LPBYTE pbStart;
LPBYTE pbData;
DWORD faRecord;
DWORD faStreamBlock;
FILEADDRESS faDstStream;
MESSAGEINFO MsgInfo;
IStream *pStream=NULL;
FILEADDRESS faStream;
LPFOLDERUSERDATAV4 pUserDataV4;
FOLDERUSERDATA UserDataV5;
LPSTREAMBLOCK pStmBlock;
LPRECORDBLOCKV5B1 pRecord;
LPTABLEHEADERV5B1 pHeader=(LPTABLEHEADERV5B1)pFile->pView;
// Trace
TraceCall("UpgradePropTreeMessageFileV5");
// Validate
Assert(sizeof(FOLDERUSERDATAV4) == sizeof(FOLDERUSERDATA));
// Get CacheInfo
if (sizeof(FOLDERUSERDATA) != pHeader->cbUserData)
{
*pfContinue = TRUE;
hr = TraceResult(MIGRATE_E_USERDATASIZEDIFF);
goto exit;
}
// Get V4 UserData
pUserDataV4 = (LPFOLDERUSERDATAV4)((LPBYTE)pFile->pView + sizeof(TABLEHEADERV5B1));
// If there is a Server Name and acctid is empty...
if ('\0' != *pUserDataV4->szServer && '\0' == *pInfo->szAcctId)
{
// Loop through the Accounts
for (DWORD i=0; i<g_AcctTable.cAccounts; i++)
{
// Is this the Account
if (lstrcmpi(g_AcctTable.prgAccount[i].szServer, pUserDataV4->szServer) == 0)
{
StrCpyN(pInfo->szAcctId, g_AcctTable.prgAccount[i].szAcctId, ARRAYSIZE(pInfo->szAcctId));
break;
}
}
}
// If there is a folder name, copy it
if ('\0' != *pUserDataV4->szGroup)
{
// Copy
StrCpyN(pInfo->szFolder, pUserDataV4->szGroup, ARRAYSIZE(pInfo->szFolder));
}
// Zero New
ZeroMemory(&UserDataV5, sizeof(FOLDERUSERDATA));
// Copy Over Relavent Stuff
UserDataV5.dwUIDValidity = pUserDataV4->dwUIDValidity;
// Set user data
IF_FAILEXIT(hr = pDB->SetUserData(&UserDataV5, sizeof(FOLDERUSERDATA)));
// Initialize faRecord to start
faRecord = pHeader->faFirstRecord;
// While we have a record
while(faRecord)
{
// Get the Record
IF_FAILEXIT(hr = GetRecordBlock(pFile, faRecord, &pRecord, &pbData, pfContinue));
// Set pbStart
pbStart = pbData;
// Clear MsgInfo
ZeroMemory(&MsgInfo, sizeof(MESSAGEINFO));
// DWORD - idMessage
CopyMemory(&MsgInfo.idMessage, pbData, sizeof(MsgInfo.idMessage));
pbData += sizeof(MsgInfo.idMessage);
// Null Message Id
if (0 == MsgInfo.idMessage)
{
// Generate
pDB->GenerateId((LPDWORD)&MsgInfo.idMessage);
}
// DWORD - dwFlags
CopyMemory(&MsgInfo.dwFlags, pbData, sizeof(MsgInfo.dwFlags));
pbData += sizeof(MsgInfo.dwFlags);
// News ?
if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile)
FLAGSET(MsgInfo.dwFlags, ARF_NEWSMSG);
// Priority
if (ISFLAGSET(MsgInfo.dwFlags, 0x00000200))
{
MsgInfo.wPriority = (WORD)IMSG_PRI_HIGH;
FLAGCLEAR(MsgInfo.dwFlags, 0x00000200);
}
else if (ISFLAGSET(MsgInfo.dwFlags, 0x00000100))
{
MsgInfo.wPriority = (WORD)IMSG_PRI_LOW;
FLAGCLEAR(MsgInfo.dwFlags, 0x00000100);
}
else
MsgInfo.wPriority = (WORD)IMSG_PRI_NORMAL;
// DWORD - ftSent
CopyMemory(&MsgInfo.ftSent, pbData, sizeof(MsgInfo.ftSent));
pbData += sizeof(MsgInfo.ftSent);
MsgInfo.ftReceived = MsgInfo.ftSent;
// DWORD - cLines
CopyMemory(&MsgInfo.cLines, pbData, sizeof(MsgInfo.cLines));
pbData += sizeof(MsgInfo.cLines);
// DWORD - faStream
CopyMemory(&faStream, pbData, sizeof(faStream));
pbData += sizeof(faStream);
// Has a Body
if (faStream)
{
// It has a body
FLAGSET(MsgInfo.dwFlags, ARF_HASBODY);
}
// DWORD - cbArticle / cbMessage (VERSION)
CopyMemory(&MsgInfo.cbMessage, pbData, sizeof(MsgInfo.cbMessage));
pbData += sizeof(MsgInfo.cbMessage);
// DWORD - ftDownloaded
CopyMemory(&MsgInfo.ftDownloaded, pbData, sizeof(MsgInfo.ftDownloaded));
pbData += sizeof(MsgInfo.ftDownloaded);
// LPSTR - pszMessageId
MsgInfo.pszMessageId = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszMessageId) + 1);
// LPSTR - pszSubject
MsgInfo.pszSubject = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszSubject) + 1);
// VERSION
MsgInfo.pszNormalSubj = MsgInfo.pszSubject + HIBYTE(HIWORD(MsgInfo.dwFlags));
// LPSTR - pszFromHeader
MsgInfo.pszFromHeader = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszFromHeader) + 1);
// LPSTR - pszReferences
MsgInfo.pszReferences = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszReferences) + 1);
// LPSTR - pszXref
MsgInfo.pszXref = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszXref) + 1);
// LPSTR - pszServer
MsgInfo.pszServer = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszServer) + 1);
// LPSTR - pszDisplayFrom
MsgInfo.pszDisplayFrom = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszDisplayFrom) + 1);
// No Display From and we have a from header
if ('\0' == *MsgInfo.pszDisplayFrom && '\0' != MsgInfo.pszFromHeader)
MsgInfo.pszDisplayFrom = MsgInfo.pszFromHeader;
// LPSTR - pszEmailFrom
MsgInfo.pszEmailFrom = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszEmailFrom) + 1);
// Going to V4 ?
if (pRecord->cbRecord - (DWORD)(pbData - pbStart) - sizeof(RECORDBLOCKV5B1) > 40)
{
// WORD - wLanguage
CopyMemory(&MsgInfo.wLanguage, pbData, sizeof(MsgInfo.wLanguage));
pbData += sizeof(MsgInfo.wLanguage);
// WORD - wReserved
pbData += sizeof(WORD);
// DWORD - cbMessage
CopyMemory(&MsgInfo.cbMessage, pbData, sizeof(MsgInfo.cbMessage));
pbData += sizeof(MsgInfo.cbMessage);
// FILETIME - ftReceived
CopyMemory(&MsgInfo.ftReceived, pbData, sizeof(MsgInfo.ftReceived));
pbData += sizeof(MsgInfo.ftReceived);
// SBAILEY: Raid-76295: News store corrupted when system dates are changed, Find dialog returns dates of 1900, 00 or blank
if (0 == MsgInfo.ftReceived.dwLowDateTime && 0 == MsgInfo.ftReceived.dwHighDateTime)
CopyMemory(&MsgInfo.ftReceived, &MsgInfo.ftSent, sizeof(FILETIME));
// LPSTR - pszDisplayTo
MsgInfo.pszDisplayTo = (LPSTR)pbData;
pbData += (lstrlen(MsgInfo.pszDisplayTo) + 1);
}
// Otherwise
else
{
// Set ftReceived
CopyMemory(&MsgInfo.ftReceived, &MsgInfo.ftSent, sizeof(FILETIME));
}
// Copy over the stream...
if (0 != faStream)
{
// Allocate a new stream
IF_FAILEXIT(hr = pDB->CreateStream(&faDstStream));
// Open the stream
IF_FAILEXIT(hr = pDB->OpenStream(ACCESS_WRITE, faDstStream, &pStream));
// Start Copying Message
faStreamBlock = faStream;
// While we have a stream block
while(faStreamBlock)
{
// Get a stream block
IF_FAILEXIT(hr = GetStreamBlock(pFile, faStreamBlock, &pStmBlock, &pbData, pfContinue));
// Write into the stream
IF_FAILEXIT(hr = pStream->Write(pbData, pStmBlock->cbData, NULL));
// Goto Next Block
faStreamBlock = pStmBlock->faNext;
}
// Commit
IF_FAILEXIT(hr = pStream->Commit(STGC_DEFAULT));
// Set new stream location
MsgInfo.faStream = faDstStream;
// Release the Stream
SafeRelease(pStream);
}
// If No Account Id and we have a server
if ('\0' == *pInfo->szAcctId && '\0' != *MsgInfo.pszServer)
{
// Loop through the Accounts
for (DWORD i=0; i<g_AcctTable.cAccounts; i++)
{
// Is this the Account
if (lstrcmpi(g_AcctTable.prgAccount[i].szServer, MsgInfo.pszServer) == 0)
{
StrCpyN(pInfo->szAcctId, g_AcctTable.prgAccount[i].szAcctId, ARRAYSIZE(pInfo->szAcctId));
break;
}
}
}
// Default to szAcctId
MsgInfo.pszAcctId = pInfo->szAcctId;
// Lookup Account Id from the Account Name...
if (MsgInfo.pszAcctName)
{
// Loop through the Accounts
for (DWORD i=0; i<g_AcctTable.cAccounts; i++)
{
// Is this the Account
if (lstrcmpi(g_AcctTable.prgAccount[i].szAcctName, MsgInfo.pszAcctName) == 0)
{
MsgInfo.pszAcctId = g_AcctTable.prgAccount[i].szAcctId;
break;
}
}
}
// Otherwise, if we have an account Id, get the account name
else if ('\0' != *pInfo->szAcctId)
{
// Loop through the Accounts
for (DWORD i=0; i<g_AcctTable.cAccounts; i++)
{
// Is this the Account
if (lstrcmpi(g_AcctTable.prgAccount[i].szAcctId, MsgInfo.pszAcctId) == 0)
{
MsgInfo.pszAcctName = g_AcctTable.prgAccount[i].szAcctName;
break;
}
}
}
// Count
pInfo->cMessages++;
if (!ISFLAGSET(MsgInfo.dwFlags, ARF_READ))
pInfo->cUnread++;
// Migrated
FLAGSET(MsgInfo.dwFlags, 0x00000010);
// Insert the Record
IF_FAILEXIT(hr = pDB->InsertRecord(&MsgInfo));
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pInfo);
// Goto the Next Record
faRecord = pRecord->faNext;
}
exit:
// Cleanup
SafeRelease(pStream);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// ParseFolderFileV5
// --------------------------------------------------------------------------------
HRESULT ParseFolderFileV5(LPMEMORYFILE pFile, LPFILEINFO pInfo,
LPPROGRESSINFO pProgress, LPDWORD pcFolders,
LPFLDINFO *pprgFolder)
{
// Locals
HRESULT hr=S_OK;
LPBYTE pbData;
DWORD faRecord;
LPFLDINFO pFolder;
LPFLDINFO prgFolder=NULL;
LPRECORDBLOCKV5B1 pRecord;
LPTABLEHEADERV5B1 pHeader;
BOOL fContinue;
DWORD cFolders=0;
// Trace
TraceCall("ParseFolderFileV5");
// De-ref the header
pHeader = (LPTABLEHEADERV5B1)pFile->pView;
// Get CacheInfo
if (sizeof(STOREUSERDATA) != pHeader->cbUserData)
{
hr = TraceResult(MIGRATE_E_USERDATASIZEDIFF);
goto exit;
}
// Allocate Folder Array
IF_NULLEXIT(prgFolder = (LPFLDINFO)ZeroAllocate(sizeof(FLDINFO) * pHeader->cRecords));
// Initialize faRecord to start
faRecord = pHeader->faFirstRecord;
// While we have a record
while(faRecord)
{
// Readability
pFolder = &prgFolder[cFolders];
// Get the Record
IF_FAILEXIT(hr = GetRecordBlock(pFile, faRecord, &pRecord, &pbData, &fContinue));
// DWORD - hFolder
CopyMemory(&pFolder->idFolder, pbData, sizeof(pFolder->idFolder));
pbData += sizeof(pFolder->idFolder);
// CHAR(MAX_FOLDER_NAME) - szFolder
CopyMemory(pFolder->szFolder, pbData, sizeof(pFolder->szFolder));
pbData += sizeof(pFolder->szFolder);
// CHAR(260) - szFile
CopyMemory(pFolder->szFile, pbData, sizeof(pFolder->szFile));
pbData += sizeof(pFolder->szFile);
// DWORD - idParent
CopyMemory(&pFolder->idParent, pbData, sizeof(pFolder->idParent));
pbData += sizeof(pFolder->idParent);
// DWORD - idChild
CopyMemory(&pFolder->idChild, pbData, sizeof(pFolder->idChild));
pbData += sizeof(pFolder->idChild);
// DWORD - idSibling
CopyMemory(&pFolder->idSibling, pbData, sizeof(pFolder->idSibling));
pbData += sizeof(pFolder->idSibling);
// DWORD - tySpecial
CopyMemory(&pFolder->tySpecial, pbData, sizeof(pFolder->tySpecial));
pbData += sizeof(pFolder->tySpecial);
// DWORD - cChildren
CopyMemory(&pFolder->cChildren, pbData, sizeof(pFolder->cChildren));
pbData += sizeof(pFolder->cChildren);
// DWORD - cMessages
CopyMemory(&pFolder->cMessages, pbData, sizeof(pFolder->cMessages));
pbData += sizeof(pFolder->cMessages);
// DWORD - cUnread
CopyMemory(&pFolder->cUnread, pbData, sizeof(pFolder->cUnread));
pbData += sizeof(pFolder->cUnread);
// DWORD - cbTotal
CopyMemory(&pFolder->cbTotal, pbData, sizeof(pFolder->cbTotal));
pbData += sizeof(pFolder->cbTotal);
// DWORD - cbUsed
CopyMemory(&pFolder->cbUsed, pbData, sizeof(pFolder->cbUsed));
pbData += sizeof(pFolder->cbUsed);
// DWORD - bHierarchy
CopyMemory(&pFolder->bHierarchy, pbData, sizeof(pFolder->bHierarchy));
pbData += sizeof(pFolder->bHierarchy);
// DWORD - dwImapFlags
CopyMemory(&pFolder->dwImapFlags, pbData, sizeof(pFolder->dwImapFlags));
pbData += sizeof(DWORD);
// BLOB - bListStamp
CopyMemory(&pFolder->bListStamp, pbData, sizeof(pFolder->bListStamp));
pbData += sizeof(BYTE);
// DWORD - bReserved[3]
pbData += (3 * sizeof(BYTE));
// DWORD - rgbReserved
pbData += 40;
// Increment Count
cFolders++;
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pInfo);
// Goto the Next Record
faRecord = pRecord->faNext;
}
// Return Folder Count
*pcFolders = cFolders;
// Return the Array
*pprgFolder = prgFolder;
// Don't Free It
prgFolder = NULL;
exit:
// Cleanup
SafeMemFree(prgFolder);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// UpgradePop3UidlFileV5
// --------------------------------------------------------------------------------
HRESULT UpgradePop3UidlFileV5(LPFILEINFO pInfo, LPMEMORYFILE pFile,
IDatabase *pDB, LPPROGRESSINFO pProgress, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
LPBYTE pbData;
DWORD faRecord;
UIDLRECORD UidlInfo;
LPRECORDBLOCKV5B1 pRecord;
LPTABLEHEADERV5B1 pHeader=(LPTABLEHEADERV5B1)pFile->pView;
// Trace
TraceCall("UpgradePop3UidlFileV5");
// Initialize faRecord to start
faRecord = pHeader->faFirstRecord;
// While we have a record
while(faRecord)
{
// Get the Record
IF_FAILEXIT(hr = GetRecordBlock(pFile, faRecord, &pRecord, &pbData, pfContinue));
// Clear UidlInfo
ZeroMemory(&UidlInfo, sizeof(UIDLRECORD));
// FILETIME - ftDownload
CopyMemory(&UidlInfo.ftDownload, pbData, sizeof(UidlInfo.ftDownload));
pbData += sizeof(UidlInfo.ftDownload);
// BYTE - fDownloaded
CopyMemory(&UidlInfo.fDownloaded, pbData, sizeof(UidlInfo.fDownloaded));
pbData += sizeof(UidlInfo.fDownloaded);
// BYTE - fDeleted
CopyMemory(&UidlInfo.fDeleted, pbData, sizeof(UidlInfo.fDeleted));
pbData += sizeof(UidlInfo.fDeleted);
// LPSTR - pszUidl
UidlInfo.pszUidl = (LPSTR)pbData;
pbData += (lstrlen(UidlInfo.pszUidl) + 1);
// LPSTR - pszServer
UidlInfo.pszServer = (LPSTR)pbData;
pbData += (lstrlen(UidlInfo.pszServer) + 1);
// Insert the Record
IF_FAILEXIT(hr = pDB->InsertRecord(&UidlInfo));
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pInfo);
// Goto the Next Record
faRecord = pRecord->faNext;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// UpgradeFileV5
// --------------------------------------------------------------------------------
HRESULT UpgradeFileV5(IDatabaseSession *pSession, MIGRATETOTYPE tyMigrate,
LPFILEINFO pInfo, LPPROGRESSINFO pProgress, BOOL *pfContinue)
{
// Locals
HRESULT hr=S_OK;
MEMORYFILE File={0};
IDatabase *pDB=NULL;
// Trace
TraceCall("UpgradeFileV5");
// Local message file
if (FILE_IS_LOCAL_MESSAGES == pInfo->tyFile)
{
// Create an ObjectDatabase (upgrade only runs when OE5 is installed)
IF_FAILEXIT(hr = pSession->OpenDatabase(pInfo->szDstFile, 0, &g_MessageTableSchema, NULL, &pDB));
// Get the File Header
IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
// UpgradeLocalStoreFileV5
IF_FAILEXIT(hr = UpgradeLocalStoreFileV5(pInfo, &File, pDB, pProgress, pfContinue));
}
// Old News or Imap file
else if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile)
{
// Create an ObjectDatabase (upgrade only runs when OE5 is installed)
IF_FAILEXIT(hr = pSession->OpenDatabase(pInfo->szDstFile, 0, &g_MessageTableSchema, NULL, &pDB));
// Get the File Header
IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
// UpgradePropTreeMessageFileV5
IF_FAILEXIT(hr = UpgradePropTreeMessageFileV5(pInfo, &File, pDB, pProgress, pfContinue));
}
// pop3uidl file
else if (FILE_IS_POP3UIDL == pInfo->tyFile)
{
// Create an ObjectDatabase (upgrade only runs when OE5 is installed)
IF_FAILEXIT(hr = pSession->OpenDatabase(pInfo->szDstFile, 0, &g_UidlTableSchema, NULL, &pDB));
// Get the File Header
IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
// UpgradePop3UidlFileV5
IF_FAILEXIT(hr = UpgradePop3UidlFileV5(pInfo, &File, pDB, pProgress, pfContinue));
}
exit:
// Cleanup
SafeRelease(pDB);
CloseMemoryFile(&File);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// UpgradeProcessFileListV5
// --------------------------------------------------------------------------------
HRESULT UpgradeProcessFileListV5(LPCSTR pszStoreSrc, LPCSTR pszStoreDst,
LPFILEINFO pHead, LPDWORD pcMax, LPDWORD pcbNeeded)
{
// Locals
HRESULT hr=S_OK;
MEMORYFILE File={0};
LPFILEINFO pCurrent;
LPTABLEHEADERV5B1 pHeader;
// Trace
TraceCall("UpgradeProcessFileListV5");
// 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) || 0 == File.cbSize)
{
// Don't Migrate
pCurrent->fMigrate = FALSE;
// Set hrMigrate
pCurrent->hrMigrate = (0 == File.cbSize ? S_OK : hr);
// Reset hr
hr = S_OK;
// Get the LastError
pCurrent->dwLastError = GetLastError();
// Goto Next
goto NextFile;
}
// Local message file
if (FILE_IS_LOCAL_MESSAGES == pCurrent->tyFile)
{
// Cast the Header
LPMBXFILEHEADER pMbxHeader=(LPMBXFILEHEADER)File.pView;
// Bad Version
if (File.cbSize < sizeof(MBXFILEHEADER) || pMbxHeader->dwMagic != MSGFILE_MAGIC || pMbxHeader->ver != MSGFILE_VER)
{
// 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 = pMbxHeader->cMsg;
}
// Otherwise, if its a news group list
else if (FILE_IS_NEWS_SUBLIST == pCurrent->tyFile)
{
// De-Ref the header
LPSUBLISTHEADER pSubList = (LPSUBLISTHEADER)File.pView;
// Check the Signature...
if (File.cbSize < sizeof(SUBLISTHEADER) ||
(SUBFILE_VERSION5 != pSubList->dwVersion &&
SUBFILE_VERSION4 != pSubList->dwVersion &&
SUBFILE_VERSION3 != pSubList->dwVersion &&
SUBFILE_VERSION2 != pSubList->dwVersion))
{
// 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 = pSubList->cSubscribed;
}
// Otherwise, if its a news sub list
else if (FILE_IS_NEWS_GRPLIST == pCurrent->tyFile)
{
// De-Ref the header
LPGRPLISTHEADER pGrpList = (LPGRPLISTHEADER)File.pView;
// Check the Signature...
if (File.cbSize < sizeof(GRPLISTHEADER) || GROUPLISTVERSION != pGrpList->dwVersion)
{
// 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 = pGrpList->cGroups;
}
// Otherwise, objectdb file
else
{
// De-Ref the header
pHeader = (LPTABLEHEADERV5B1)File.pView;
// Check the Signature...
if (File.cbSize < sizeof(TABLEHEADERV5B1) || OBJECTDB_SIGNATURE != pHeader->dwSignature || OBJECTDB_VERSION_PRE_V5 != pHeader->wMajorVersion)
{
// 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;
}
// Special Case pop3uidl.dat
if (FILE_IS_POP3UIDL == pCurrent->tyFile)
{
// Compute Real Destination File
wnsprintf(pCurrent->szDstFile, ARRAYSIZE(pCurrent->szDstFile),"%s\\pop3uidl.dbx", pszStoreDst);
}
// Otherwise, generate a unqiue message file name
else
{
// Save the Folder Id
pCurrent->idFolder = g_idFolderNext;
// Build New Path
wnsprintf(pCurrent->szDstFile, ARRAYSIZE(pCurrent->szDstFile), "%s\\%08d.dbx", pszStoreDst, g_idFolderNext);
// Increment id
g_idFolderNext++;
}
// Initialize counters
InitializeCounters(&File, pCurrent, pcMax, pcbNeeded, TRUE);
// Yes, Migrate
pCurrent->fMigrate = TRUE;
NextFile:
// Close the File
CloseMemoryFile(&File);
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// UpgradeDeleteFilesV5
// --------------------------------------------------------------------------------
void UpgradeDeleteFilesV5(LPCSTR pszStoreDst)
{
// Locals
CHAR szSearch[MAX_PATH + MAX_PATH];
CHAR szFilePath[MAX_PATH + MAX_PATH];
HANDLE hFind=INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fd;
// Trace
TraceCall("UpgradeDeleteFilesV5");
// Do we have a sub dir
wnsprintf(szSearch, ARRAYSIZE(szSearch),"%s\\*.dbx", pszStoreDst);
// Find first file
hFind = FindFirstFile(szSearch, &fd);
// Did we find something
if (INVALID_HANDLE_VALUE == hFind)
goto exit;
// Loop for ever
while(1)
{
// Make File Path
MakeFilePath(pszStoreDst, fd.cFileName, "", szFilePath, ARRAYSIZE(szFilePath));
// Delete
DeleteFile(szFilePath);
// Find the Next File
if (!FindNextFile(hFind, &fd))
break;
}
exit:
// Cleanup
if (hFind)
FindClose(hFind);
}
// --------------------------------------------------------------------------------
// UpgradeDeleteIdxMbxNchDatFilesV5
// --------------------------------------------------------------------------------
void UpgradeDeleteIdxMbxNchDatFilesV5(LPFILEINFO pHeadFile)
{
// Locals
CHAR szDstFile[MAX_PATH + MAX_PATH];
LPFILEINFO pCurrent;
// Trace
TraceCall("UpgradeDeleteOdbFilesV5");
// Delete all old files
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Succeeded
Assert(SUCCEEDED(pCurrent->hrMigrate));
// Delete the file
// DeleteFile(pCurrent->szFilePath);
// 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);
}
}
// Done
return;
}
// --------------------------------------------------------------------------------
// GetSpecialFolderInfo
// --------------------------------------------------------------------------------
HRESULT GetSpecialFolderInfo(LPCSTR pszFilePath, LPSTR pszFolder,
DWORD cchFolder, DWORD *ptySpecial)
{
// Locals
CHAR szPath[_MAX_PATH];
CHAR szDrive[_MAX_DRIVE];
CHAR szDir[_MAX_DIR];
CHAR szFile[_MAX_FNAME];
CHAR szExt[_MAX_EXT];
CHAR szRes[255];
DWORD i;
// Trace
TraceCall("GetSpecialFolderInfo");
// Initialize
*ptySpecial = 0xffffffff;
// Split the Path
_splitpath(pszFilePath, szDrive, szDir, szFile, szExt);
// Set Folder Name
StrCpyN(pszFolder, szFile, cchFolder);
// Loop through special folder
for (i=FOLDER_INBOX; i<FOLDER_MAX; i++)
{
// Load the Special Folder Name
LoadString(g_hInst, IDS_INBOX + (i - 1), szRes, ARRAYSIZE(szRes));
// Compare with szFile
if (lstrcmpi(szFile, szRes) == 0)
{
// Copy the Folder Name
StrCpyN(pszFolder, szRes, cchFolder);
// Return special folder type
*ptySpecial = (i - 1);
// Success
return(S_OK);
}
}
// Done
return(E_FAIL);
}
// --------------------------------------------------------------------------------
// FixupFolderUserData
// --------------------------------------------------------------------------------
HRESULT FixupFolderUserData(IDatabaseSession *pSession, FOLDERID idFolder,
LPCSTR pszName, SPECIALFOLDER tySpecial, LPFILEINFO pCurrent)
{
// Locals
HRESULT hr=S_OK;
FOLDERUSERDATA UserData;
IDatabase *pDB=NULL;
// Trace
TraceCall("FixupFolderUserData");
// Better not be in the store yet
Assert(FALSE == pCurrent->fInStore);
// Its in the store
pCurrent->fInStore = TRUE;
// Create an Ojbect Database
IF_FAILEXIT(hr = pSession->OpenDatabase(pCurrent->szDstFile, 0, &g_MessageTableSchema, NULL, &pDB));
// Store the User Data
IF_FAILEXIT(hr = pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA)));
// Its Initialized
UserData.fInitialized = TRUE;
// UserData.clsidType
if (ISFLAGSET(pCurrent->dwServer, SRV_POP3))
UserData.tyFolder = FOLDER_LOCAL;
else if (ISFLAGSET(pCurrent->dwServer, SRV_NNTP))
UserData.tyFolder = FOLDER_NEWS;
else if (ISFLAGSET(pCurrent->dwServer, SRV_IMAP))
UserData.tyFolder = FOLDER_IMAP;
// Copy the Account Id
StrCpyN(UserData.szAcctId, pCurrent->szAcctId, ARRAYSIZE(UserData.szAcctId));
// Save Folder Id
UserData.idFolder = idFolder;
// Save Special Folder Type
UserData.tySpecial = tySpecial;
// Copy the Folder name
StrCpyN(UserData.szFolder, pszName, ARRAYSIZE(UserData.szFolder));
// Must be Subscribed
UserData.fSubscribed = TRUE;
// Set the Sort Index Information
UserData.idSort = COLUMN_RECEIVED;
// Not Ascending
UserData.fAscending = FALSE;
// Not threaded
UserData.fThreaded = FALSE;
// Basic Filter
UserData.ridFilter = RULEID_VIEW_ALL;
// Add Welcome Message Again
UserData.fWelcomeAdded = FALSE;
// Show Deleted
UserData.fShowDeleted = TRUE;
// New thread model
UserData.fNewThreadModel = TRUE;
UserData.fTotalWatched = TRUE;
UserData.fWatchedCounts = TRUE;
// Store the User Data
IF_FAILEXIT(hr = pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
exit:
// Cleanup
SafeRelease(pDB);
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// SetIMAPSpecialFldrType
// --------------------------------------------------------------------------------
HRESULT SetIMAPSpecialFldrType(LPSTR pszAcctID, LPSTR pszFldrName, SPECIALFOLDER *psfType)
{
char szPath[MAX_PATH + 1];
SPECIALFOLDER sfResult = FOLDER_NOTSPECIAL;
TraceCall("SetIMAPSpecialFldrType");
Assert(NULL != psfType);
Assert(FOLDER_NOTSPECIAL == *psfType);
LoadString(g_hInst, IDS_SENTITEMS, szPath, sizeof(szPath));
if (0 == lstrcmp(szPath, pszFldrName))
{
sfResult = FOLDER_SENT;
goto exit;
}
LoadString(g_hInst, IDS_DRAFT, szPath, sizeof(szPath));
if (0 == lstrcmp(szPath, pszFldrName))
{
sfResult = FOLDER_DRAFT;
goto exit;
}
exit:
*psfType = sfResult;
return S_OK;
}
// --------------------------------------------------------------------------------
// InsertFolderIntoStore
// --------------------------------------------------------------------------------
HRESULT InsertFolderIntoStore(IDatabaseSession *pSession, IMessageStore *pStore,
LPFLDINFO pThis, DWORD cFolders, LPFLDINFO prgFolder, FOLDERID idParentNew,
LPFILEINFO pInfo, LPFILEINFO pFileHead, LPFOLDERID pidNew)
{
// Locals
HRESULT hr=S_OK;
DWORD i;
CHAR szPath[_MAX_PATH];
CHAR szDrive[_MAX_DRIVE];
CHAR szDir[_MAX_DIR];
CHAR szFile[_MAX_FNAME];
CHAR szExt[_MAX_EXT];
CHAR szFilePath[MAX_PATH];
CHAR szInbox[MAX_PATH];
BOOL fFound=FALSE;
LPFILEINFO pCurrent=NULL;
FOLDERINFO Folder={0};
// Trace
TraceCall("InsertFolderIntoStore");
// Invalid Arg
//Assert(FILE_IS_LOCAL_FOLDERS == pInfo->tyFile || FILE_IS_IMAP_FOLDERS == pInfo->tyFile);
// Copy Stuff Over to Folder
Folder.pszName = pThis->szFolder;
Folder.idParent = idParentNew;
Folder.bHierarchy = pThis->bHierarchy;
Folder.dwFlags = FOLDER_SUBSCRIBED; // $$TODO$$ May need to adjust and map to new flags
Folder.tySpecial = (0xffffffff == pThis->tySpecial) ? FOLDER_NOTSPECIAL : (BYTE)(pThis->tySpecial + 1);
Folder.cMessages = pThis->cMessages;
Folder.cUnread = pThis->cUnread;
Folder.pszFile = pThis->szFile;
Folder.dwListStamp = pThis->bListStamp;
// For IMAP folders, we have to set tySpecial based on registry folder paths
if (pInfo && FILE_IS_IMAP_FOLDERS == pInfo->tyFile && NULL != pThis &&
FOLDERID_ROOT == (FOLDERID)IntToPtr(pThis->idParent))
{
HRESULT hrTemp;
if (FOLDER_NOTSPECIAL == Folder.tySpecial)
{
hrTemp = SetIMAPSpecialFldrType(pInfo->szAcctId, Folder.pszName, &Folder.tySpecial);
TraceError(hrTemp);
Assert(SUCCEEDED(hrTemp) || FOLDER_NOTSPECIAL == Folder.tySpecial);
}
else if (FOLDER_INBOX == Folder.tySpecial)
{
LoadString(g_hInst, IDS_INBOX, szInbox, ARRAYSIZE(szInbox));
Folder.pszName = szInbox;
}
}
// Look for Current
if (pInfo && pFileHead)
{
// Locate the file...
for (pCurrent=pFileHead; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Migrate
if (pCurrent->fMigrate)
{
// Local Folder ?
if (FILE_IS_LOCAL_FOLDERS == pInfo->tyFile && FILE_IS_LOCAL_MESSAGES == pCurrent->tyFile)
{
// Get the File Name
_splitpath(pCurrent->szFilePath, szDrive, szDir, szFile, szExt);
// Test For File Name
if (lstrcmpi(szFile, pThis->szFile) == 0)
{
// This is It
fFound = TRUE;
// Adjust the Flags
FLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED);
}
}
// IMAP Folders ?
else if (FILE_IS_IMAP_FOLDERS == pInfo->tyFile && FILE_IS_IMAP_MESSAGES == pCurrent->tyFile)
{
// Same Account
if (lstrcmpi(pCurrent->szAcctId, pInfo->szAcctId) == 0)
{
// Get the File Name
_splitpath(pCurrent->szFilePath, szDrive, szDir, szFile, szExt);
// Build File
wnsprintf(szFilePath, ARRAYSIZE(szFilePath), "%s.nch", szFile);
// Test For File Name
if (lstrcmpi(szFilePath, pThis->szFile) == 0)
{
// This is It
fFound = TRUE;
}
}
}
// Found
if (fFound)
{
// Get the File Name
_splitpath(pCurrent->szDstFile, szDrive, szDir, szFile, szExt);
// Build File
wnsprintf(szFilePath, ARRAYSIZE(szFilePath), "%s.dbx", szFile);
// Local the File for this folder and set
Folder.pszFile = szFilePath;
// Set Folder Counts
Folder.cMessages = pCurrent->cMessages;
Folder.cUnread = pCurrent->cUnread;
// Done
break;
}
}
}
}
// If this is a special folder, then lets try to see if it already exists...
if (FOLDER_NOTSPECIAL != Folder.tySpecial)
{
// Locals
FOLDERINFO Special;
// pThis Parent should be invalid
Assert(FOLDERID_ROOT == (FOLDERID)IntToPtr(pThis->idParent));
// Try to get the special folder info
if (FAILED(pStore->GetSpecialFolderInfo(idParentNew, Folder.tySpecial, &Special)))
{
// Create the Folder
IF_FAILEXIT(hr = pStore->CreateFolder(NOFLAGS, &Folder, NOSTORECALLBACK));
// Update pThis->dwServerHigh with new folderid
pThis->idNewFolderId = (DWORD_PTR)Folder.idFolder;
}
// Otherwise...
else
{
// Update pThis->dwServerHigh with new folderid
pThis->idNewFolderId = (DWORD_PTR)Special.idFolder;
// Update the Special folder
Folder.idFolder = Special.idFolder;
// Update Special
Special.bHierarchy = Folder.bHierarchy;
Special.dwFlags = Folder.dwFlags; // $$TODO$$ May need to adjust and map to new flags
Special.cMessages = Folder.cMessages;
Special.cUnread = Folder.cUnread;
Special.pszFile = Folder.pszFile;
Special.dwListStamp = Folder.dwListStamp;
// Update the Record
IF_FAILEXIT(hr = pStore->UpdateRecord(&Special));
// Free Special
pStore->FreeRecord(&Special);
}
}
// Otherwise, just try to create the folder
else
{
// Create the Folder
IF_FAILEXIT(hr = pStore->CreateFolder(NOFLAGS, &Folder, NOSTORECALLBACK));
// Update pThis->dwServerHigh with new folderid
pThis->idNewFolderId = (DWORD_PTR)Folder.idFolder;
}
// If We Found a folder...
if (pCurrent)
{
// Update the Folder's UserData
IF_FAILEXIT(hr = FixupFolderUserData(pSession, Folder.idFolder, pThis->szFolder, Folder.tySpecial, pCurrent));
}
// Walk Insert the children of pThis
for (i=0; i<cFolders; i++)
{
// If Parent is equal to idParent, then lets insert this node under the new parent
if (prgFolder[i].idParent == pThis->idFolder)
{
// Can't be null
Assert(prgFolder[i].idFolder);
// InsertFolderIntoStore
IF_FAILEXIT(hr = InsertFolderIntoStore(pSession, pStore, &prgFolder[i], cFolders, prgFolder, Folder.idFolder, pInfo, pFileHead, NULL));
}
}
// Return the New Folder
if (pidNew)
*pidNew = Folder.idFolder;
exit:
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// MergeFolderCacheIntoStore
// --------------------------------------------------------------------------------
HRESULT MergeFolderCacheIntoStore(IDatabaseSession *pSession, IMessageStore *pStore,
LPFILEINFO pInfo, LPFILEINFO pHeadFile, LPPROGRESSINFO pProgress)
{
// Locals
HRESULT hr=S_OK;
MEMORYFILE File={0};
FOLDERID idServer;
DWORD cFolders;
DWORD i;
LPFLDINFO prgFolder=NULL;
LPFLDINFO pFolder;
HKEY hKey=NULL;
DWORD cbLength;
LPBYTE pbChange=NULL;
LPFOLDERIDCHANGE prgidChange;
IUserIdentityManager *pManager = NULL;
IUserIdentity *pIdentity = NULL;
HKEY hkeyID = NULL;
// Trace
TraceCall("MergeFolderCacheIntoStore");
// Find the Server Id
if (FAILED(pStore->FindServerId(pInfo->szAcctId, &idServer)))
goto exit;
// Open the File
IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
// Parse the file
IF_FAILEXIT(hr = ParseFolderFileV5(&File, pInfo, pProgress, &cFolders, &prgFolder));
// Loop through the folders
for (i=0; i<cFolders; i++)
{
// If this is the root folder node (OE4), remember to migrate the root hierarchy char
if ((FOLDERID)IntToPtr(prgFolder[i].idFolder) == FOLDERID_ROOT)
{
FOLDERINFO fiFolderInfo;
IF_FAILEXIT(hr = pStore->GetFolderInfo(idServer, &fiFolderInfo));
fiFolderInfo.bHierarchy = prgFolder[i].bHierarchy;
hr = pStore->UpdateRecord(&fiFolderInfo);
pStore->FreeRecord(&fiFolderInfo);
IF_FAILEXIT(hr);
}
// If Parent is equal to idParent, then lets insert this node under the new parent
else if ((FOLDERID)IntToPtr(prgFolder[i].idParent) == FOLDERID_ROOT)
{
// InsertFolderIntoStore
IF_FAILEXIT(hr = InsertFolderIntoStore(pSession, pStore, &prgFolder[i], cFolders, prgFolder, idServer, pInfo, pHeadFile, NULL));
}
}
// Local Folders
if (FILE_IS_LOCAL_FOLDERS == pInfo->tyFile)
{
// cbLength
cbLength = (sizeof(DWORD) + (sizeof(FOLDERIDCHANGE) * cFolders));
// Allocate a folderidchange array
IF_NULLEXIT(pbChange = (LPBYTE)g_pMalloc->Alloc(cbLength));
// Store cLocalFolders
CopyMemory(pbChange, &cFolders, sizeof(DWORD));
// Set prgidChange
prgidChange = (LPFOLDERIDCHANGE)(pbChange + sizeof(DWORD));
// Walk through the list of files and merge the folders, sublist, group lists into pFolder
for (i=0; i<cFolders; i++)
{
prgidChange[i].idOld = (FOLDERID)IntToPtr(prgFolder[i].idFolder);
prgidChange[i].idNew = (FOLDERID)prgFolder[i].idNewFolderId;
}
// Get a user manager
if (FAILED(CoCreateInstance(CLSID_UserIdentityManager, NULL, CLSCTX_INPROC_SERVER,
IID_IUserIdentityManager, (void **)&pManager)))
goto exit;
Assert(pManager);
// Get Default Identity
if (FAILED(pManager->GetIdentityByCookie((GUID*)&UID_GIBC_DEFAULT_USER, &pIdentity)))
goto exit;
Assert(pIdentity);
// Ensure that we have an identity and can get to its registry
if (FAILED(pIdentity->OpenIdentityRegKey(KEY_WRITE, &hkeyID)))
goto exit;
Assert(hkeyID);
// Open the HKCU
if (ERROR_SUCCESS != RegOpenKeyEx(hkeyID, "Software\\Microsoft\\Outlook Express\\5.0", 0, KEY_ALL_ACCESS, &hKey))
{
hr = TraceResult(MIGRATE_E_REGOPENKEY);
goto exit;
}
// Write it to the registry
if (ERROR_SUCCESS != RegSetValueEx(hKey, "FolderIdChange", 0, REG_BINARY, pbChange, cbLength))
{
hr = TraceResult(MIGRATE_E_REGSETVALUE);
goto exit;
}
}
exit:
// Cleanup
if (hKey)
RegCloseKey(hKey);
if (hkeyID)
RegCloseKey(hkeyID);
SafeMemFree(pbChange);
SafeMemFree(prgFolder);
SafeRelease(pIdentity);
SafeRelease(pManager);
CloseMemoryFile(&File);
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// MergeNewsGroupList
// --------------------------------------------------------------------------------
HRESULT MergeNewsGroupList(IDatabaseSession *pSession, IMessageStore *pStore,
LPFILEINFO pInfo, LPFILEINFO pHeadFile, LPPROGRESSINFO pProgress)
{
// Locals
HRESULT hr=S_OK;
DWORD i;
FOLDERINFO Folder={0};
MEMORYFILE File={0};
FOLDERID idServer;
DWORD cbRead;
LPSTR pszT;
LPSTR pszGroup;
LPSTR pszDescription;
FOLDERID idFolder;
LPFILEINFO pSubList=NULL;
LPFILEINFO pCurrent;
LPSUBLISTHEADER pSubListHeader;
IDatabase *pDB=NULL;
CHAR szPath[_MAX_PATH];
CHAR szDrive[_MAX_DRIVE];
CHAR szDir[_MAX_DIR];
CHAR szFile[_MAX_FNAME];
CHAR szExt[_MAX_EXT];
LPGRPLISTHEADER pHeader;
// Trace
TraceCall("MergeNewsGroupList");
// Find the Server Id
if (FAILED(pStore->FindServerId(pInfo->szAcctId, &idServer)))
goto exit;
// Set Progress File
SetProgressFile(pProgress, pInfo);
// Open the Group List File
IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
// Get the Header
pHeader = (LPGRPLISTHEADER)File.pView;
// Initialize cb
cbRead = sizeof(GRPLISTHEADER);
// Loop
for (i=0; i<pHeader->cGroups; i++)
{
// Set pszGroup
pszT = pszGroup = (LPSTR)((LPBYTE)File.pView + cbRead);
// Increment to end of pszGroup or end of file
while (*pszT && cbRead < File.cbSize)
{
// Increment cb
cbRead++;
// End of String
pszT = (LPSTR)((LPBYTE)File.pView + cbRead);
}
// Done
if (cbRead >= File.cbSize)
break;
// Step Over the Null
cbRead++;
// Set pszDescription
pszT = pszDescription = (LPSTR)((LPBYTE)File.pView + cbRead);
// Increment to end of pszGroup or end of file
while (*pszT && cbRead < File.cbSize)
{
// Increment cb
cbRead++;
// End of String
pszT = (LPSTR)((LPBYTE)File.pView + cbRead);
}
// Done
if (cbRead >= File.cbSize)
break;
// Increment over the null
cbRead++;
// Step over group type
cbRead += sizeof(DWORD);
// Not Empyt
if ('\0' == *pszGroup)
break;
// Set the Folder Info
Folder.pszName = pszGroup;
Folder.pszDescription = pszDescription;
Folder.idParent = idServer;
Folder.tySpecial = FOLDER_NOTSPECIAL;
// Create the Folder
pStore->CreateFolder(0, &Folder, NOSTORECALLBACK);
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pInfo);
}
// Walk through news message files and create folders for them.
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Find the Sublist for this group
if (FILE_IS_NEWS_SUBLIST == pCurrent->tyFile && lstrcmpi(pCurrent->szAcctId, pInfo->szAcctId) == 0)
{
// Set pSubList
pSubList = pCurrent;
// Done
break;
}
}
// No Sub List
if (NULL == pSubList)
goto exit;
// Close the File
CloseMemoryFile(&File);
// Set Progress File
SetProgressFile(pProgress, pSubList);
// Open the Group List File
IF_FAILEXIT(hr = OpenMemoryFile(pSubList->szFilePath, &File));
// De-Ref the header
pSubListHeader = (LPSUBLISTHEADER)File.pView;
// SUBFILE_VERSION5
if (SUBFILE_VERSION5 == pSubListHeader->dwVersion)
{
// Locals
PGROUPSTATUS5 pStatus;
DWORD cbRead;
// Initialize cbRead
cbRead = sizeof(SUBLISTHEADER) + sizeof(DWORD);
// PGROUPSTATUS5
for (i=0; i<pSubListHeader->cSubscribed; i++)
{
// De-Ref the Group Status
pStatus = (PGROUPSTATUS5)((LPBYTE)File.pView + cbRead);
// Increment cbRead
cbRead += sizeof(GROUPSTATUS5);
// Read the Name
pszGroup = (LPSTR)((LPBYTE)File.pView + cbRead);
// Increment cbRead
cbRead += pStatus->cbName + pStatus->cbReadRange + pStatus->cbKnownRange + pStatus->cbMarkedRange + pStatus->cbRequestedRange;
// Find The Folder...
Folder.idParent = idServer;
Folder.pszName = pszGroup;
// Try to find this folder
if (DB_S_FOUND == pStore->FindRecord(IINDEX_ALL, COLUMNS_ALL, &Folder, NULL))
{
// Locals
CHAR szSrcFile[MAX_PATH];
// Subscribe to It
if (ISFLAGSET(pStatus->dwFlags, GSF_SUBSCRIBED))
{
// Its SubScribed
FLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED);
}
// Format the original file name
wnsprintf(szSrcFile, ARRAYSIZE(szSrcFile), "%08x", pStatus->dwCacheFileIndex);
// Try to find the folder in the list of files
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Find the Sublist for this group
if (pCurrent->fMigrate && FILE_IS_NEWS_MESSAGES == pCurrent->tyFile && lstrcmpi(pCurrent->szAcctId, pInfo->szAcctId) == 0)
{
// Get the File Name
_splitpath(pCurrent->szFilePath, szDrive, szDir, szFile, szExt);
// Correct file name
if (lstrcmpi(szFile, szSrcFile) == 0)
{
// Get the File Name
_splitpath(pCurrent->szDstFile, szDrive, szDir, szFile, szExt);
// Format the original file name
wnsprintf(szSrcFile, ARRAYSIZE(szSrcFile), "%s%s", szFile, szExt);
// Set the File Path
Folder.pszFile = szSrcFile;
// Set Folder Counts
Folder.cMessages = pCurrent->cMessages;
Folder.cUnread = pCurrent->cUnread;
// FixupFolderUserData(
FixupFolderUserData(pSession, Folder.idFolder, pszGroup, FOLDER_NOTSPECIAL, pCurrent);
// Done
break;
}
}
}
// Update the Record
pStore->UpdateRecord(&Folder);
// Free This
pStore->FreeRecord(&Folder);
}
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pSubList);
}
}
// SUBFILE_VERSION4
else if (SUBFILE_VERSION4 == pSubListHeader->dwVersion)
{
// Locals
PGROUPSTATUS4 pStatus;
DWORD cbRead;
// Initialize cbRead
cbRead = sizeof(SUBLISTHEADER);
// PGROUPSTATUS5
for (i=0; i<pSubListHeader->cSubscribed; i++)
{
// De-Ref the Group Status
pStatus = (PGROUPSTATUS4)((LPBYTE)File.pView + cbRead);
// Increment cbRead
cbRead += sizeof(GROUPSTATUS4);
// Read the Name
pszGroup = (LPSTR)((LPBYTE)File.pView + cbRead);
// Increment cbRead
cbRead += pStatus->cbName + pStatus->cbReadRange + pStatus->cbKnownRange + pStatus->cbMarkedRange + pStatus->cbRequestedRange;
// Find The Folder...
Folder.idParent = idServer;
Folder.pszName = pszGroup;
// Try to find this folder
if (DB_S_FOUND == pStore->FindRecord(IINDEX_ALL, COLUMNS_ALL, &Folder, NULL))
{
// Locals
CHAR szSrcFile[MAX_PATH];
// Subscribe to It
if (ISFLAGSET(pStatus->dwFlags, GSF_SUBSCRIBED))
{
// Its SubScribed
FLAGSET(Folder.dwFlags, FOLDER_SUBSCRIBED);
}
// Try to find the folder in the list of files
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Find the Sublist for this group
if (pCurrent->fMigrate && FILE_IS_NEWS_MESSAGES == pCurrent->tyFile && lstrcmpi(pCurrent->szAcctId, pInfo->szAcctId) == 0)
{
// Correct file name
if (lstrcmpi(pszGroup, pCurrent->szFolder) == 0)
{
// Get the File Name
_splitpath(pCurrent->szDstFile, szDrive, szDir, szFile, szExt);
// Format the original file name
wnsprintf(szSrcFile, ARRAYSIZE(szSrcFile), "%s%s", szFile, szExt);
// Set the File Path
Folder.pszFile = szSrcFile;
// Set Folder Counts
Folder.cMessages = pCurrent->cMessages;
Folder.cUnread = pCurrent->cUnread;
// FixupFolderUserData(
FixupFolderUserData(pSession, Folder.idFolder, pszGroup, FOLDER_NOTSPECIAL, pCurrent);
// Done
break;
}
}
}
// Update the Record
pStore->UpdateRecord(&Folder);
// Free This
pStore->FreeRecord(&Folder);
}
// Bump Progress
if(!g_fQuiet)
IncrementProgress(pProgress, pSubList);
}
}
// SUBFILE_VERSION3
else if (SUBFILE_VERSION3 == pSubListHeader->dwVersion)
{
Assert(FALSE);
}
// SUBFILE_VERSION2
else if (SUBFILE_VERSION2 == pSubListHeader->dwVersion)
{
Assert(FALSE);
}
exit:
// Close the File
CloseMemoryFile(&File);
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// BuildUnifiedFolderManager
// --------------------------------------------------------------------------------
HRESULT BuildUnifiedFolderManager(IDatabaseSession *pSession, IMessageStore *pStore,
LPFILEINFO pHeadFile, LPPROGRESSINFO pProgress)
{
// Locals
HRESULT hr=S_OK;
LPFILEINFO pCurrent;
// Trace
TraceCall("BuildUnifiedFolderManager");
// Walk through the list of files and merge the folders, sublist, group lists into pFolder
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Handle Folders Type
if (FILE_IS_LOCAL_FOLDERS == pCurrent->tyFile)
{
// Merge Local Folder Cache into new Folder Manager
IF_FAILEXIT(hr = MergeFolderCacheIntoStore(pSession, pStore, pCurrent, pHeadFile, pProgress));
}
// IMAP Folder
else if (FILE_IS_IMAP_FOLDERS == pCurrent->tyFile)
{
// Merge IMAP Folder Cache into new Folder Manager
IF_FAILEXIT(hr = MergeFolderCacheIntoStore(pSession, pStore, pCurrent, pHeadFile, pProgress));
}
// News Group List
else if (FILE_IS_NEWS_GRPLIST == pCurrent->tyFile)
{
// Merge IMAP Folder Cache into new Folder Manager
IF_FAILEXIT(hr = MergeNewsGroupList(pSession, pStore, pCurrent, pHeadFile, pProgress));
}
}
// Walk through any files that were not merged into the store
for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Find the Sublist for this group
if (TRUE == pCurrent->fMigrate && FALSE == pCurrent->fInStore)
{
// Local Message File...
if (FILE_IS_LOCAL_MESSAGES == pCurrent->tyFile)
{
// Locals
FLDINFO Folder={0};
SPECIALFOLDER tySpecial;
CHAR szFolder[255];
CHAR szPath[_MAX_PATH];
CHAR szDrive[_MAX_DRIVE];
CHAR szDir[_MAX_DIR];
CHAR szFile[_MAX_FNAME];
CHAR szExt[_MAX_EXT];
// Get Special Folder Info
GetSpecialFolderInfo(pCurrent->szFilePath, szFolder, ARRAYSIZE(szFolder), &Folder.tySpecial);
// Special Case for News Special Folders from v1
if (0xffffffff == Folder.tySpecial && strstr(szFolder, "special folders") != NULL)
{
// Locals
CHAR szRes[255];
// News Outbox
LoadString(g_hInst, IDS_POSTEDITEMS, szRes, ARRAYSIZE(szRes));
// Contains "Posted Items"
if (strstr(szFolder, szRes) != NULL)
LoadString(g_hInst, IDS_NEWSPOSTED, szFolder, ARRAYSIZE(szFolder));
// Contains "Saved Items"
else
{
// News Saved Items
LoadString(g_hInst, IDS_SAVEDITEMS, szRes, ARRAYSIZE(szRes));
// Contains "Saved Items"
if (strstr(szFolder, szRes) != NULL)
LoadString(g_hInst, IDS_NEWSSAVED, szFolder, ARRAYSIZE(szFolder));
// Otherwise
else
{
// News Outbox
LoadString(g_hInst, IDS_OUTBOX, szRes, ARRAYSIZE(szRes));
// Contains Outbox
if (strstr(szFolder, szRes) != NULL)
LoadString(g_hInst, IDS_NEWSOUTBOX, szFolder, ARRAYSIZE(szFolder));
}
}
}
// Compute the File Name
_splitpath(pCurrent->szDstFile, szDrive, szDir, szFile, szExt);
wnsprintf(Folder.szFile, ARRAYSIZE(Folder.szFile), "%s.dbx", szFile);
// Set the Name
if ('\0' != *pCurrent->szFolder)
StrCpyN(Folder.szFolder, pCurrent->szFolder, ARRAYSIZE(Folder.szFolder));
else if ('\0' != *szFolder)
StrCpyN(Folder.szFolder, szFolder, ARRAYSIZE(Folder.szFolder));
else
StrCpyN(Folder.szFolder, szFile, ARRAYSIZE(Folder.szFolder));
// Set Message and Unread Count
Folder.cMessages = pCurrent->cMessages;
Folder.cUnread = pCurrent->cUnread;
// Insert into Local Store
InsertFolderIntoStore(pSession, pStore, &Folder, 0, NULL, FOLDERID_LOCAL_STORE, NULL, NULL, (LPFOLDERID)&Folder.idFolder);
// Fixup special
tySpecial = (Folder.tySpecial == 0xffffffff) ? FOLDER_NOTSPECIAL : (BYTE)(Folder.tySpecial + 1);
// Update the Folder's UserData
FixupFolderUserData(pSession, (FOLDERID)IntToPtr(Folder.idFolder), Folder.szFolder, tySpecial, pCurrent);
}
// Otherwise, just delete the file
else if (FILE_IS_POP3UIDL != pCurrent->tyFile)
DeleteFile(pCurrent->szDstFile);
}
}
exit:
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CleanupMessageStore
// --------------------------------------------------------------------------------
HRESULT CleanupMessageStore(LPCSTR pszStoreRoot, IMessageStore *pStore)
{
// Locals
HRESULT hr=S_OK;
FOLDERINFO Folder={0};
HROWSET hRowset=NULL;
CHAR szFilePath[MAX_PATH + MAX_PATH];
// Trace
TraceCall("CleanupMessageStore");
// Create a Rowset
IF_FAILEXIT(hr = pStore->CreateRowset(IINDEX_PRIMARY, 0, &hRowset));
// Walk the Rowset
while(S_OK == pStore->QueryRowset(hRowset, 1, (LPVOID *)&Folder, NULL))
{
// If it has a file and no messags.
if (Folder.pszFile && 0 == Folder.cMessages)
{
// Delete the file...
IF_FAILEXIT(hr = MakeFilePath(pszStoreRoot, Folder.pszFile, "", szFilePath, ARRAYSIZE(szFilePath)));
// Delete the File
DeleteFile(szFilePath);
// Reset the filename
Folder.pszFile = NULL;
// Update the Record
IF_FAILEXIT(hr = pStore->UpdateRecord(&Folder));
}
// Otherwise, if there is a file, force a folder rename
else if (Folder.pszFile)
{
// Rename the folder
pStore->RenameFolder(Folder.idFolder, Folder.pszName, 0, NULL);
}
// Cleanup
pStore->FreeRecord(&Folder);
}
exit:
// Cleanup
pStore->FreeRecord(&Folder);
pStore->CloseRowset(&hRowset);
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// UpgradeV5
// --------------------------------------------------------------------------------
HRESULT UpgradeV5(MIGRATETOTYPE tyMigrate, LPCSTR pszStoreSrc, LPCSTR pszStoreDst,
LPPROGRESSINFO pProgress, LPFILEINFO *ppHeadFile)
{
// Locals
HRESULT hr=S_OK;
ENUMFILEINFO EnumInfo;
LPFILEINFO pCurrent;
DWORD cbNeeded;
DWORDLONG dwlFree;
BOOL fContinue;
CHAR szFolders[MAX_PATH + MAX_PATH];
CHAR szMsg[512];
IMessageStore *pStore=NULL;
IDatabaseSession *pSession=NULL;
// Trace
TraceCall("UpgradeV5");
// Initialize
*ppHeadFile = NULL;
// Setup the EnumFile Info
ZeroMemory(&EnumInfo, sizeof(ENUMFILEINFO));
EnumInfo.pszExt = ".nch";
EnumInfo.pszFoldFile = "folders.nch";
EnumInfo.pszUidlFile = "pop3uidl.dat";
EnumInfo.pszSubList = "sublist.dat";
EnumInfo.pszGrpList = "grplist.dat";
EnumInfo.fFindV1News = TRUE;
// Enumerate All ODB files in szStoreRoot...
IF_FAILEXIT(hr = EnumerateStoreFiles(pszStoreSrc, DIR_IS_ROOT, NULL, &EnumInfo, ppHeadFile));
// Setup the EnumFile Info
ZeroMemory(&EnumInfo, sizeof(ENUMFILEINFO));
EnumInfo.pszExt = ".mbx";
EnumInfo.pszFoldFile = NULL;
EnumInfo.pszUidlFile = NULL;
// Enumerate All ODB files in szStoreRoot...
IF_FAILEXIT(hr = EnumerateStoreFiles(pszStoreSrc, DIR_IS_ROOT, NULL, &EnumInfo, ppHeadFile));
// Nothing to upgrade
if (NULL == *ppHeadFile)
goto exit;
// Compute some Counts, and validate that the files are valid to migrate...
IF_FAILEXIT(hr = UpgradeProcessFileListV5(pszStoreSrc, pszStoreDst, *ppHeadFile, &pProgress->cMax, &cbNeeded));
// Message
LoadString(g_hInst, IDS_UPGRADEMESSAGE, szMsg, ARRAYSIZE(szMsg));
// Message
if(!g_fQuiet)
MigrateMessageBox(szMsg, MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND);
// Delete fles
UpgradeDeleteFilesV5(pszStoreDst);
// Create an Ojbect Database
IF_FAILEXIT(hr = CoCreateInstance(CLSID_DatabaseSession, NULL, CLSCTX_INPROC_SERVER, IID_IDatabaseSession, (LPVOID *)&pSession));
// Create an Ojbect Database
IF_FAILEXIT(hr = CoCreateInstance(CLSID_MigrateMessageStore, NULL, CLSCTX_INPROC_SERVER, IID_IMessageStore, (LPVOID *)&pStore));
// Build the Folders.odb File Path
wnsprintf(szFolders, ARRAYSIZE(szFolders), "%s\\folders.dbx", pszStoreDst);
// Delete It First
DeleteFile(szFolders);
// Initialize the Store
IF_FAILEXIT(hr = pStore->Initialize(pszStoreDst));
// Initialize the Store
IF_FAILEXIT(hr = pStore->Validate(0));
// Enought DiskSpace ?
IF_FAILEXIT(hr = GetAvailableDiskSpace(pszStoreDst, &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);
// Assume we will continue
fContinue = FALSE;
// Downgrade the file
hr = pCurrent->hrMigrate = UpgradeFileV5(pSession, tyMigrate, pCurrent, pProgress, &fContinue);
// Failure ?
if (FAILED(pCurrent->hrMigrate))
{
// Set Last Error
pCurrent->dwLastError = GetLastError();
// Stop
if (FALSE == fContinue)
break;
if(!g_fQuiet) {
// Fixup the progress
while (pCurrent->cProgCur < pCurrent->cProgMax)
{
IncrementProgress(pProgress, pCurrent);
}
}
// We are ok
hr = S_OK;
}
}
}
// Process Folder Lists
hr = BuildUnifiedFolderManager(pSession, pStore, *ppHeadFile, pProgress);
// Failure, delete all destination files
if (FAILED(hr))
{
// Delete fles
UpgradeDeleteFilesV5(pszStoreDst);
}
// Otherwise, lets force a folder rename to build friendly file names
else
{
// Rename all the folders...
CleanupMessageStore(pszStoreDst, pStore);
}
#if 0
// Otherwise, delete source files
else
{
// Delete all source files
UpgradeDeleteIdxMbxNchDatFilesV5(*ppHeadFile);
}
#endif
exit:
// Cleanup
SafeRelease(pStore);
// Done
return hr;
}