Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1136 lines
37 KiB

  1. // --------------------------------------------------------------------------------
  2. // DownOE5.cpp
  3. // --------------------------------------------------------------------------------
  4. #include "pch.hxx"
  5. #include "utility.h"
  6. #include "migrate.h"
  7. #include "migerror.h"
  8. #include "structs.h"
  9. #include "resource.h"
  10. #include <oestore.h>
  11. #include <mimeole.h>
  12. const static BYTE rgbZero[4] = {0};
  13. //--------------------------------------------------------------------------
  14. // PFNREADTYPEDATA
  15. //--------------------------------------------------------------------------
  16. typedef void (APIENTRY *PFNREADTYPEDATA)(LPBYTE pbSource, DWORD cbLength,
  17. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs);
  18. //--------------------------------------------------------------------------
  19. // g_rgpfnReadTypeData
  20. //--------------------------------------------------------------------------
  21. extern const PFNREADTYPEDATA g_rgpfnReadTypeData[CDT_LASTTYPE];
  22. //--------------------------------------------------------------------------
  23. // ReadTypeData
  24. //--------------------------------------------------------------------------
  25. #define ReadTypeData(_pbSource, _cbLength, _pColumn, _pRecord, _pcPtrRefs) \
  26. (*(g_rgpfnReadTypeData[(_pColumn)->type]))(_pbSource, _cbLength, (_pColumn), _pRecord, _pcPtrRefs)
  27. //--------------------------------------------------------------------------
  28. inline void ReadTypeDataFILETIME(LPBYTE pbSource, DWORD cbLength,
  29. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  30. {
  31. Assert(cbLength == sizeof(FILETIME));
  32. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(FILETIME));
  33. }
  34. //--------------------------------------------------------------------------
  35. inline void ReadTypeDataFIXSTRA(LPBYTE pbSource, DWORD cbLength,
  36. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  37. {
  38. Assert(cbLength == pColumn->cbSize);
  39. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, pColumn->cbSize);
  40. }
  41. //--------------------------------------------------------------------------
  42. inline void ReadTypeDataVARSTRA(LPBYTE pbSource, DWORD cbLength,
  43. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  44. {
  45. Assert((LPSTR)((LPBYTE)pbSource)[cbLength - 1] == '\0');
  46. *((LPSTR *)((LPBYTE)pRecord + pColumn->ofBinding)) = (LPSTR)((LPBYTE)pbSource);
  47. (*pcPtrRefs)++;
  48. }
  49. //--------------------------------------------------------------------------
  50. inline void ReadTypeDataBYTE(LPBYTE pbSource, DWORD cbLength,
  51. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  52. {
  53. Assert(cbLength == sizeof(BYTE));
  54. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(BYTE));
  55. }
  56. //--------------------------------------------------------------------------
  57. inline void ReadTypeDataDWORD(LPBYTE pbSource, DWORD cbLength,
  58. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  59. {
  60. Assert(cbLength == sizeof(DWORD));
  61. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(DWORD));
  62. }
  63. //--------------------------------------------------------------------------
  64. inline void ReadTypeDataWORD(LPBYTE pbSource, DWORD cbLength,
  65. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  66. {
  67. Assert(cbLength == sizeof(WORD));
  68. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(WORD));
  69. }
  70. //--------------------------------------------------------------------------
  71. inline void ReadTypeDataSTREAM(LPBYTE pbSource, DWORD cbLength,
  72. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  73. {
  74. Assert(cbLength == sizeof(FILEADDRESS));
  75. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, sizeof(FILEADDRESS));
  76. }
  77. //--------------------------------------------------------------------------
  78. inline void ReadTypeDataVARBLOB(LPBYTE pbSource, DWORD cbLength,
  79. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  80. {
  81. LPBLOB pBlob = (LPBLOB)((LPBYTE)pRecord + pColumn->ofBinding);
  82. pBlob->cbSize = cbLength;
  83. if (pBlob->cbSize > 0)
  84. {
  85. pBlob->pBlobData = pbSource;
  86. (*pcPtrRefs)++;
  87. }
  88. else
  89. pBlob->pBlobData = NULL;
  90. }
  91. //--------------------------------------------------------------------------
  92. inline void ReadTypeDataFIXBLOB(LPBYTE pbSource, DWORD cbLength,
  93. LPCTABLECOLUMN pColumn, LPVOID pRecord, LPDWORD pcPtrRefs)
  94. {
  95. Assert(pColumn->cbSize == cbLength);
  96. CopyMemory((LPBYTE)pRecord + pColumn->ofBinding, pbSource, pColumn->cbSize);
  97. }
  98. //--------------------------------------------------------------------------
  99. const PFNREADTYPEDATA g_rgpfnReadTypeData[CDT_LASTTYPE] = {
  100. (PFNREADTYPEDATA)ReadTypeDataFILETIME,
  101. (PFNREADTYPEDATA)ReadTypeDataFIXSTRA,
  102. (PFNREADTYPEDATA)ReadTypeDataVARSTRA,
  103. (PFNREADTYPEDATA)ReadTypeDataBYTE,
  104. (PFNREADTYPEDATA)ReadTypeDataDWORD,
  105. (PFNREADTYPEDATA)ReadTypeDataWORD,
  106. (PFNREADTYPEDATA)ReadTypeDataSTREAM,
  107. (PFNREADTYPEDATA)ReadTypeDataVARBLOB,
  108. (PFNREADTYPEDATA)ReadTypeDataFIXBLOB
  109. };
  110. // --------------------------------------------------------------------------------
  111. // DowngradeReadMsgInfoV5
  112. // --------------------------------------------------------------------------------
  113. HRESULT DowngradeReadMsgInfoV5(LPRECORDBLOCKV5 pRecord, LPMESSAGEINFO pMsgInfo)
  114. {
  115. // Locals
  116. HRESULT hr=S_OK;
  117. DWORD i;
  118. DWORD cColumns;
  119. DWORD cbRead=0;
  120. DWORD cbLength;
  121. DWORD cbData;
  122. DWORD cPtrRefs;
  123. LPBYTE pbData;
  124. LPBYTE pbSource;
  125. LPDWORD prgdwOffset=(LPDWORD)((LPBYTE)pRecord + sizeof(RECORDBLOCKV5));
  126. // Trace
  127. TraceCall("DowngradeReadMsgInfoV5");
  128. // Set cbData
  129. cbData = (pRecord->cbRecord - sizeof(RECORDBLOCKV5) - (pRecord->cColumns * sizeof(DWORD)));
  130. // Allocate
  131. IF_NULLEXIT(pbData = (LPBYTE)g_pMalloc->Alloc(cbData));
  132. // Free This
  133. pMsgInfo->pvMemory = pbData;
  134. // Set pbData
  135. pbSource = (LPBYTE)((LPBYTE)pRecord + sizeof(RECORDBLOCKV5) + (pRecord->cColumns * sizeof(DWORD)));
  136. // Copy the data
  137. CopyMemory(pbData, pbSource, cbData);
  138. // Compute number of columns to read
  139. cColumns = min(pRecord->cColumns, MSGCOL_LASTID);
  140. // Read the Record
  141. for (i=0; i<cColumns; i++)
  142. {
  143. // Compute cbLength
  144. cbLength = (i + 1 == cColumns) ? (cbData - prgdwOffset[i]) : (prgdwOffset[i + 1] - prgdwOffset[i]);
  145. // Bad-Record
  146. if (prgdwOffset[i] != cbRead || cbRead + cbLength > cbData)
  147. {
  148. hr = TraceResult(MIGRATE_E_BADRECORDFORMAT);
  149. goto exit;
  150. }
  151. // ReadTypeData
  152. ReadTypeData(pbData + cbRead, cbLength, &g_MessageTableSchema.prgColumn[i], pMsgInfo, &cPtrRefs);
  153. // Increment cbRead
  154. cbRead += cbLength;
  155. }
  156. exit:
  157. // Done
  158. return hr;
  159. }
  160. // --------------------------------------------------------------------------------
  161. // DowngradeLocalStoreFileV5
  162. // --------------------------------------------------------------------------------
  163. HRESULT DowngradeLocalStoreFileV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo,
  164. LPMEMORYFILE pFile, LPPROGRESSINFO pProgress)
  165. {
  166. // Locals
  167. HRESULT hr=S_OK;
  168. DWORD cRecords=0;
  169. CHAR szIdxPath[MAX_PATH + MAX_PATH];
  170. CHAR szMbxPath[MAX_PATH + MAX_PATH];
  171. HANDLE hIdxFile=NULL;
  172. HANDLE hMbxFile=NULL;
  173. MESSAGEINFO MsgInfo={0};
  174. IDXFILEHEADER IdxHeader;
  175. MBXFILEHEADER MbxHeader;
  176. MBXMESSAGEHEADER MbxMessage;
  177. IDXMESSAGEHEADER IdxMessage;
  178. LPRECORDBLOCKV5 pRecord;
  179. LPSTREAMBLOCK pStmBlock;
  180. LPBYTE pbData;
  181. DWORD faRecord;
  182. DWORD faIdxWrite;
  183. DWORD faMbxWrite;
  184. DWORD faStreamBlock;
  185. DWORD cbAligned;
  186. DWORD faMbxCurrent;
  187. LPTABLEHEADERV5 pHeader=(LPTABLEHEADERV5)pFile->pView;
  188. // Trace
  189. TraceCall("DowngradeLocalStoreFileV5");
  190. // Set idx path
  191. ReplaceExtension(pInfo->szFilePath, ".idx", szIdxPath, ARRAYSIZE(szIdxPath));
  192. // Set mbx path
  193. ReplaceExtension(pInfo->szFilePath, ".mbx", szMbxPath, ARRAYSIZE(szMbxPath));
  194. // Delete Both Files
  195. DeleteFile(szIdxPath);
  196. DeleteFile(szMbxPath);
  197. // Open the idx file
  198. hIdxFile = CreateFile(szIdxPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL);
  199. if (INVALID_HANDLE_VALUE == hIdxFile)
  200. {
  201. hIdxFile = NULL;
  202. hr = TraceResult(MIGRATE_E_CANTOPENFILE);
  203. goto exit;
  204. }
  205. // Initialize Idx Header
  206. ZeroMemory(&IdxHeader, sizeof(IDXFILEHEADER));
  207. IdxHeader.dwMagic = CACHEFILE_MAGIC;
  208. IdxHeader.ver = CACHEFILE_VER;
  209. IdxHeader.verBlob = 1; // this will force the .idx blobs to be rebuilt when imn 1.0 or oe v4.0 is run again
  210. // Write the header
  211. IF_FAILEXIT(hr = MyWriteFile(hIdxFile, 0, &IdxHeader, sizeof(IDXFILEHEADER)));
  212. // Open the mbx file
  213. hMbxFile = CreateFile(szMbxPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL);
  214. if (INVALID_HANDLE_VALUE == hMbxFile)
  215. {
  216. hMbxFile = NULL;
  217. hr = TraceResult(MIGRATE_E_CANTOPENFILE);
  218. goto exit;
  219. }
  220. // Initialize MBX Header
  221. ZeroMemory(&MbxHeader, sizeof(MBXFILEHEADER));
  222. MbxHeader.dwMagic = MSGFILE_MAGIC;
  223. MbxHeader.ver = MSGFILE_VER;
  224. // Write the header
  225. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, 0, &MbxHeader, sizeof(MBXFILEHEADER)));
  226. // Set First Record
  227. faRecord = pHeader->faFirstRecord;
  228. // Set faIdxWrite
  229. faIdxWrite = sizeof(IDXFILEHEADER);
  230. // Set faMbxWrite
  231. faMbxWrite = sizeof(MBXFILEHEADER);
  232. // While we have a record
  233. while(faRecord)
  234. {
  235. // Bad Length
  236. if (faRecord + sizeof(RECORDBLOCKV5) > pFile->cbSize)
  237. {
  238. hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  239. goto exit;
  240. }
  241. // Cast the Record
  242. pRecord = (LPRECORDBLOCKV5)((LPBYTE)pFile->pView + faRecord);
  243. // Invalid Record Signature
  244. if (faRecord != pRecord->faRecord)
  245. {
  246. hr = TraceResult(MIGRATE_E_BADRECORDSIGNATURE);
  247. goto exit;
  248. }
  249. // Bad Length
  250. if (faRecord + pRecord->cbRecord > pFile->cbSize)
  251. {
  252. hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  253. goto exit;
  254. }
  255. // Load MsgInfo
  256. IF_FAILEXIT(hr = DowngradeReadMsgInfoV5(pRecord, &MsgInfo));
  257. // No Stream ?
  258. if (0 == MsgInfo.faStream)
  259. goto NextRecord;
  260. // Set msgidLast
  261. if ((DWORD)MsgInfo.idMessage > MbxHeader.msgidLast)
  262. MbxHeader.msgidLast = (DWORD)MsgInfo.idMessage;
  263. // Zero Out the Message Structures
  264. ZeroMemory(&MbxMessage, sizeof(MBXMESSAGEHEADER));
  265. ZeroMemory(&IdxMessage, sizeof(IDXMESSAGEHEADER));
  266. // Fill MbxMessage
  267. MbxMessage.dwMagic = MSGHDR_MAGIC;
  268. MbxMessage.msgid = (DWORD)MsgInfo.idMessage;
  269. // Fixup the Flags
  270. if (FALSE == ISFLAGSET(MsgInfo.dwFlags, ARF_READ))
  271. FLAGSET(IdxMessage.dwState, MSG_UNREAD);
  272. if (ISFLAGSET(MsgInfo.dwFlags, ARF_VOICEMAIL))
  273. FLAGSET(IdxMessage.dwState, MSG_VOICEMAIL);
  274. if (ISFLAGSET(MsgInfo.dwFlags, ARF_REPLIED))
  275. FLAGSET(IdxMessage.dwState, MSG_REPLIED);
  276. if (ISFLAGSET(MsgInfo.dwFlags, ARF_FORWARDED))
  277. FLAGSET(IdxMessage.dwState, MSG_FORWARDED);
  278. if (ISFLAGSET(MsgInfo.dwFlags, ARF_FLAGGED))
  279. FLAGSET(IdxMessage.dwState, MSG_FLAGGED);
  280. if (ISFLAGSET(MsgInfo.dwFlags, ARF_RCPTSENT))
  281. FLAGSET(IdxMessage.dwState, MSG_RCPTSENT);
  282. if (ISFLAGSET(MsgInfo.dwFlags, ARF_NOSECUI))
  283. FLAGSET(IdxMessage.dwState, MSG_NOSECUI);
  284. if (ISFLAGSET(MsgInfo.dwFlags, ARF_NEWSMSG))
  285. FLAGSET(IdxMessage.dwState, MSG_NEWSMSG);
  286. if (ISFLAGSET(MsgInfo.dwFlags, ARF_UNSENT))
  287. FLAGSET(IdxMessage.dwState, MSG_UNSENT);
  288. if (ISFLAGSET(MsgInfo.dwFlags, ARF_SUBMITTED))
  289. FLAGSET(IdxMessage.dwState, MSG_SUBMITTED);
  290. if (ISFLAGSET(MsgInfo.dwFlags, ARF_RECEIVED))
  291. FLAGSET(IdxMessage.dwState, MSG_RECEIVED);
  292. // Save faMbxCurrent
  293. faMbxCurrent = faMbxWrite;
  294. // Validate alignment
  295. Assert((faMbxCurrent % 4) == 0);
  296. // Write the mbx header
  297. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxCurrent, &MbxMessage, sizeof(MBXMESSAGEHEADER)));
  298. // Increment faMbxWrite
  299. faMbxWrite += sizeof(MBXMESSAGEHEADER);
  300. // Initialize dwMsgSize
  301. MbxMessage.dwMsgSize = sizeof(MBXMESSAGEHEADER);
  302. // Set faStreamBlock
  303. faStreamBlock = MsgInfo.faStream;
  304. // While we have stream block
  305. while(faStreamBlock)
  306. {
  307. // Bad Length
  308. if (faStreamBlock + sizeof(STREAMBLOCK) > pFile->cbSize)
  309. {
  310. hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  311. goto exit;
  312. }
  313. // Cast the Record
  314. pStmBlock = (LPSTREAMBLOCK)((LPBYTE)pFile->pView + faStreamBlock);
  315. // Invalid Record Signature
  316. if (faStreamBlock != pStmBlock->faThis)
  317. {
  318. hr = TraceResult(MIGRATE_E_BADSTREAMBLOCKSIGNATURE);
  319. goto exit;
  320. }
  321. // Bad Length
  322. if (faStreamBlock + pStmBlock->cbBlock > pFile->cbSize)
  323. {
  324. hr = TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  325. goto exit;
  326. }
  327. // Set pbData
  328. pbData = (LPBYTE)((LPBYTE)(pStmBlock) + sizeof(STREAMBLOCK));
  329. // Write into the stream
  330. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxWrite, pbData, pStmBlock->cbData));
  331. // Increment dwBodySize
  332. MbxMessage.dwBodySize += pStmBlock->cbData;
  333. // Increment dwMsgSize
  334. MbxMessage.dwMsgSize += pStmBlock->cbData;
  335. // Increment faMbxWrite
  336. faMbxWrite += pStmBlock->cbData;
  337. // Goto Next Block
  338. faStreamBlock = pStmBlock->faNext;
  339. }
  340. // Pad the Message on a dword boundary
  341. cbAligned = (faMbxWrite % 4);
  342. // cbAligned ?
  343. if (cbAligned)
  344. {
  345. // Reset cbAligned
  346. cbAligned = 4 - cbAligned;
  347. // Write the mbx header
  348. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxWrite, (LPVOID)rgbZero, cbAligned));
  349. // Increment faMbxWrite
  350. faMbxWrite += cbAligned;
  351. // Increment
  352. MbxMessage.dwMsgSize += cbAligned;
  353. }
  354. // Validate alignment
  355. Assert((faMbxWrite % 4) == 0);
  356. // Write the mbx header again
  357. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, faMbxCurrent, &MbxMessage, sizeof(MBXMESSAGEHEADER)));
  358. // Fill IdxMessage
  359. IdxMessage.dwLanguage = (DWORD)MAKELONG(MsgInfo.wLanguage, MsgInfo.wHighlight);
  360. IdxMessage.msgid = (DWORD)MsgInfo.idMessage;
  361. IdxMessage.dwOffset = faMbxCurrent;
  362. IdxMessage.dwMsgSize = MbxMessage.dwMsgSize;
  363. IdxMessage.dwHdrOffset = 0;
  364. IdxMessage.dwSize = sizeof(IDXMESSAGEHEADER);
  365. IdxMessage.dwHdrSize = 0;
  366. IdxMessage.rgbHdr[4] = 0;
  367. // Write the mbx header
  368. IF_FAILEXIT(hr = MyWriteFile(hIdxFile, faIdxWrite, &IdxMessage, sizeof(IDXMESSAGEHEADER)));
  369. // Increment faIdxWrite
  370. faIdxWrite += IdxMessage.dwSize;
  371. // Increment cRecords
  372. cRecords++;
  373. NextRecord:
  374. // Progress
  375. IncrementProgress(pProgress, pInfo);
  376. // Cleanup
  377. SafeMemFree(MsgInfo.pvMemory);
  378. // Goto Next
  379. faRecord = pRecord->faNext;
  380. }
  381. // Set the Record Counts
  382. MbxHeader.cMsg = cRecords;
  383. IdxHeader.cMsg = cRecords;
  384. // Set the Flags
  385. IdxHeader.dwFlags = 1; // STOREINIT_MAIL
  386. MbxHeader.dwFlags = 1; // STOREINIT_MAIL
  387. // Get the Size of the idx file
  388. IdxHeader.cbValid = ::GetFileSize(hIdxFile, NULL);
  389. if (0xFFFFFFFF == IdxHeader.cbValid)
  390. {
  391. hr = TraceResult(MIGRATE_E_CANTGETFILESIZE);
  392. goto exit;
  393. }
  394. // Get the Size of the mbx file
  395. MbxHeader.cbValid = ::GetFileSize(hMbxFile, NULL);
  396. if (0xFFFFFFFF == MbxHeader.cbValid)
  397. {
  398. hr = TraceResult(MIGRATE_E_CANTGETFILESIZE);
  399. goto exit;
  400. }
  401. // Write the header
  402. IF_FAILEXIT(hr = MyWriteFile(hIdxFile, 0, &IdxHeader, sizeof(IDXFILEHEADER)));
  403. // Write the header
  404. IF_FAILEXIT(hr = MyWriteFile(hMbxFile, 0, &MbxHeader, sizeof(MBXFILEHEADER)));
  405. exit:
  406. // Cleanup
  407. SafeCloseHandle(hIdxFile);
  408. SafeCloseHandle(hMbxFile);
  409. SafeMemFree(MsgInfo.pvMemory);
  410. // Done
  411. return hr;
  412. }
  413. // --------------------------------------------------------------------------------
  414. // DowngradeRecordV5
  415. // --------------------------------------------------------------------------------
  416. HRESULT DowngradeRecordV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo,
  417. LPMEMORYFILE pFile, LPCHAINNODEV5 pNode, LPDWORD pcbRecord)
  418. {
  419. // Locals
  420. HRESULT hr=S_OK;
  421. DWORD cbRecord=0;
  422. DWORD cbOffsets;
  423. DWORD cbData;
  424. DWORD cb;
  425. LPBYTE pbData;
  426. LPBYTE pbStart;
  427. MESSAGEINFO MsgInfo={0};
  428. RECORDBLOCKV5B1 RecordOld;
  429. LPRECORDBLOCKV5 pRecord;
  430. // Trace
  431. TraceCall("DowngradeRecordV5");
  432. // Invalid
  433. if (pNode->faRecord + sizeof(RECORDBLOCKV5) > pFile->cbSize || 0 == pNode->faRecord)
  434. return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  435. // Access the Record
  436. pRecord = (LPRECORDBLOCKV5((LPBYTE)pFile->pView + pNode->faRecord));
  437. // Bad Record
  438. if (pRecord->faRecord != pNode->faRecord)
  439. return TraceResult(MIGRATE_E_BADRECORDSIGNATURE);
  440. // Invalid
  441. if (pNode->faRecord + sizeof(RECORDBLOCKV5) + pRecord->cbRecord > pFile->cbSize)
  442. return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  443. // Fill an old record header
  444. RecordOld.faRecord = pRecord->faRecord;
  445. RecordOld.faNext = pRecord->faNext;
  446. RecordOld.faPrevious = pRecord->faPrevious;
  447. // Reformat the record
  448. if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile)
  449. {
  450. // Read the v5 record into a msginfo structure
  451. IF_FAILEXIT(hr = DowngradeReadMsgInfoV5(pRecord, &MsgInfo));
  452. }
  453. // Compute offset table length
  454. cbOffsets = (pRecord->cColumns * sizeof(DWORD));
  455. // Cast the datablock
  456. pbData = ((LPBYTE)pRecord + sizeof(RECORDBLOCKV5B1));
  457. // Set Size
  458. cbData = (pRecord->cbRecord - cbOffsets - sizeof(RECORDBLOCKV5));
  459. // Remove the Offset Table
  460. MoveMemory(pbData, ((LPBYTE)pRecord + sizeof(RECORDBLOCKV5) + cbOffsets), cbData);
  461. // Reformat the record
  462. if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile)
  463. {
  464. // Set pbStart
  465. pbStart = pbData;
  466. // DWORD - idMessage
  467. CopyMemory(pbData, &MsgInfo.idMessage, sizeof(MsgInfo.idMessage));
  468. pbData += sizeof(MsgInfo.idMessage);
  469. // VERSION - dwFlags
  470. if (IMSG_PRI_HIGH == MsgInfo.wPriority)
  471. FLAGSET(MsgInfo.dwFlags, 0x00000200);
  472. else if (IMSG_PRI_LOW == MsgInfo.wPriority)
  473. FLAGSET(MsgInfo.dwFlags, 0x00000100);
  474. // VERSION - Normalized Subject -
  475. if (lstrcmpi(MsgInfo.pszSubject, MsgInfo.pszNormalSubj) != 0)
  476. MsgInfo.dwFlags = (DWORD)MAKELONG(MsgInfo.dwFlags, MAKEWORD(0, 4));
  477. // DWORD - dwFlags
  478. CopyMemory(pbData, &MsgInfo.dwFlags, sizeof(MsgInfo.dwFlags));
  479. pbData += sizeof(MsgInfo.dwFlags);
  480. // FILETIME - ftSent
  481. CopyMemory(pbData, &MsgInfo.ftSent, sizeof(MsgInfo.ftSent));
  482. pbData += sizeof(MsgInfo.ftSent);
  483. // DWORD - cLines
  484. CopyMemory(pbData, &MsgInfo.cLines, sizeof(MsgInfo.cLines));
  485. pbData += sizeof(MsgInfo.cLines);
  486. // DWORD - faStream
  487. CopyMemory(pbData, &MsgInfo.faStream, sizeof(MsgInfo.faStream));
  488. pbData += sizeof(MsgInfo.faStream);
  489. // VERSION - DWORD - cbArticle
  490. CopyMemory(pbData, &MsgInfo.cbMessage, sizeof(MsgInfo.cbMessage));
  491. pbData += sizeof(MsgInfo.cbMessage);
  492. // FILETIME - ftDownloaded
  493. CopyMemory(pbData, &MsgInfo.ftDownloaded, sizeof(MsgInfo.ftDownloaded));
  494. pbData += sizeof(MsgInfo.ftDownloaded);
  495. // LPSTR - pszMessageId
  496. cb = lstrlen(MsgInfo.pszMessageId) + 1;
  497. CopyMemory(pbData, MsgInfo.pszMessageId, cb);
  498. pbData += cb;
  499. // LPSTR - pszSubject
  500. cb = lstrlen(MsgInfo.pszSubject) + 1;
  501. CopyMemory(pbData, MsgInfo.pszSubject, cb);
  502. pbData += cb;
  503. // LPSTR - pszFromHeader
  504. cb = lstrlen(MsgInfo.pszFromHeader) + 1;
  505. CopyMemory(pbData, MsgInfo.pszFromHeader, cb);
  506. pbData += cb;
  507. // LPSTR - pszReferences
  508. cb = lstrlen(MsgInfo.pszReferences) + 1;
  509. CopyMemory(pbData, MsgInfo.pszReferences, cb);
  510. pbData += cb;
  511. // LPSTR - pszXref
  512. cb = lstrlen(MsgInfo.pszXref) + 1;
  513. CopyMemory(pbData, MsgInfo.pszXref, cb);
  514. pbData += cb;
  515. // LPSTR - pszServer
  516. cb = lstrlen(MsgInfo.pszServer) + 1;
  517. CopyMemory(pbData, MsgInfo.pszServer, cb);
  518. pbData += cb;
  519. // LPSTR - pszDisplayFrom
  520. cb = lstrlen(MsgInfo.pszDisplayFrom) + 1;
  521. CopyMemory(pbData, MsgInfo.pszDisplayFrom, cb);
  522. pbData += cb;
  523. // LPSTR - pszEmailFrom
  524. cb = lstrlen(MsgInfo.pszEmailFrom) + 1;
  525. CopyMemory(pbData, MsgInfo.pszEmailFrom, cb);
  526. pbData += cb;
  527. // Going to V4 ?
  528. if (DOWNGRADE_V5_TO_V4 == tyMigrate)
  529. {
  530. // WORD - wLanguage
  531. CopyMemory(pbData, &MsgInfo.wLanguage, sizeof(MsgInfo.wLanguage));
  532. pbData += sizeof(MsgInfo.wLanguage);
  533. // WORD - wReserved
  534. MsgInfo.wHighlight = 0;
  535. CopyMemory(pbData, &MsgInfo.wHighlight, sizeof(MsgInfo.wHighlight));
  536. pbData += sizeof(MsgInfo.wHighlight);
  537. // DWORD - cbMessage
  538. CopyMemory(pbData, &MsgInfo.cbMessage, sizeof(MsgInfo.cbMessage));
  539. pbData += sizeof(MsgInfo.cbMessage);
  540. // DWORD - ftReceived
  541. CopyMemory(pbData, &MsgInfo.ftReceived, sizeof(MsgInfo.ftReceived));
  542. pbData += sizeof(MsgInfo.ftReceived);
  543. // LPSTR - pszDisplayTo
  544. cb = lstrlen(MsgInfo.pszDisplayTo) + 1;
  545. CopyMemory(pbData, MsgInfo.pszDisplayTo, cb);
  546. pbData += cb;
  547. }
  548. // Add on Reserved
  549. cbRecord = (40 + sizeof(RECORDBLOCKV5B1) + (pbData - pbStart));
  550. // Better be smaller
  551. Assert(cbRecord <= pRecord->cbRecord);
  552. }
  553. // Otherwise, much easier
  554. else
  555. {
  556. // Set Size
  557. cbRecord = (pRecord->cbRecord - cbOffsets - sizeof(RECORDBLOCKV5)) + sizeof(RECORDBLOCKV5B1);
  558. }
  559. // Set the Record Size
  560. RecordOld.cbRecord = cbRecord;
  561. // Write the new record header
  562. CopyMemory((LPBYTE)pRecord, &RecordOld, sizeof(RECORDBLOCKV5B1));
  563. // Return size
  564. *pcbRecord = cbRecord;
  565. exit:
  566. // Cleanup
  567. SafeMemFree(MsgInfo.pvMemory);
  568. // Done
  569. return hr;
  570. }
  571. // --------------------------------------------------------------------------------
  572. // DowngradeIndexV5
  573. // --------------------------------------------------------------------------------
  574. HRESULT DowngradeIndexV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo,
  575. LPMEMORYFILE pFile, LPPROGRESSINFO pProgress, DWORD faRootChain, DWORD faChain)
  576. {
  577. // Locals
  578. HRESULT hr=S_OK;
  579. LONG i;
  580. LPCHAINBLOCKV5 pChain;
  581. CHAINBLOCKV5B1 ChainOld;
  582. DWORD cbRecord;
  583. // Trace
  584. TraceCall("DowngradeIndexV5");
  585. // Nothing to validate
  586. if (0 == faChain)
  587. return S_OK;
  588. // Out-of-bounds
  589. if (faChain + CB_CHAIN_BLOCKV5 > pFile->cbSize)
  590. return TraceResult(MIGRATE_E_OUTOFRANGEADDRESS);
  591. // De-ref the block
  592. pChain = (LPCHAINBLOCKV5)((LPBYTE)pFile->pView + faChain);
  593. // Out-of-Bounds
  594. if (pChain->faStart != faChain)
  595. return TraceResult(MIGRATE_E_BADCHAINSIGNATURE);
  596. // Too many nodes
  597. if (pChain->cNodes > BTREE_ORDER)
  598. return TraceResult(MIGRATE_E_TOOMANYCHAINNODES);
  599. // Validate Minimum Filled Constraint
  600. if (pChain->cNodes < BTREE_MIN_CAP && pChain->faStart != faRootChain)
  601. return TraceResult(MIGRATE_E_BADMINCAPACITY);
  602. // Go to the left
  603. IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, pFile, pProgress, faRootChain, pChain->faLeftChain));
  604. // Convert pChain to ChainOld
  605. ChainOld.faStart = pChain->faStart;
  606. ChainOld.cNodes = pChain->cNodes;
  607. ChainOld.faLeftChain = pChain->faLeftChain;
  608. // Loop throug right chains
  609. for (i=0; i<pChain->cNodes; i++)
  610. {
  611. // Bump Progress
  612. IncrementProgress(pProgress, pInfo);
  613. /// Downgrad this record
  614. IF_FAILEXIT(hr = DowngradeRecordV5(tyMigrate, pInfo, pFile, &pChain->rgNode[i], &cbRecord));
  615. // Update Old Node
  616. ChainOld.rgNode[i].faRecord = pChain->rgNode[i].faRecord;
  617. ChainOld.rgNode[i].cbRecord = cbRecord;
  618. ChainOld.rgNode[i].faRightChain = pChain->rgNode[i].faRightChain;
  619. // Validate the Right Chain
  620. IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, pFile, pProgress, faRootChain, pChain->rgNode[i].faRightChain));
  621. }
  622. // Write this new chain
  623. CopyMemory((LPBYTE)pChain, &ChainOld, CB_CHAIN_BLOCKV5B1);
  624. exit:
  625. // Done
  626. return hr;
  627. }
  628. // --------------------------------------------------------------------------------
  629. // DowngradeFileV5
  630. // --------------------------------------------------------------------------------
  631. HRESULT DowngradeFileV5(MIGRATETOTYPE tyMigrate, LPFILEINFO pInfo,
  632. LPPROGRESSINFO pProgress)
  633. {
  634. // Locals
  635. HRESULT hr=S_OK;
  636. MEMORYFILE File={0};
  637. TABLEHEADERV5 HeaderV5;
  638. LPTABLEHEADERV5B1 pHeaderV5B1;
  639. CHAR szDstFile[MAX_PATH + MAX_PATH];
  640. // Trace
  641. TraceCall("DowngradeFileV5");
  642. // Local message file
  643. if (FILE_IS_LOCAL_MESSAGES == pInfo->tyFile)
  644. {
  645. // Get the File Header
  646. IF_FAILEXIT(hr = OpenMemoryFile(pInfo->szFilePath, &File));
  647. // UpgradeLocalStoreFileV5
  648. IF_FAILEXIT(hr = DowngradeLocalStoreFileV5(tyMigrate, pInfo, &File, pProgress));
  649. }
  650. // Old News or Imap file
  651. else
  652. {
  653. // Create xxx.nch file
  654. if (FILE_IS_POP3UIDL == pInfo->tyFile)
  655. ReplaceExtension(pInfo->szFilePath, ".dat", szDstFile, ARRAYSIZE(szDstFile));
  656. else
  657. ReplaceExtension(pInfo->szFilePath, ".nch", szDstFile, ARRAYSIZE(szDstFile));
  658. // Copy the file
  659. if (0 == CopyFile(pInfo->szFilePath, szDstFile, FALSE))
  660. {
  661. hr = TraceResult(MIGRATE_E_CANTCOPYFILE);
  662. goto exit;
  663. }
  664. // Get the File Header
  665. IF_FAILEXIT(hr = OpenMemoryFile(szDstFile, &File));
  666. // Copy Table Header
  667. CopyMemory(&HeaderV5, (LPBYTE)File.pView, sizeof(TABLEHEADERV5));
  668. // De-Ref the header
  669. pHeaderV5B1 = (LPTABLEHEADERV5B1)File.pView;
  670. // Fixup the Header
  671. ZeroMemory(pHeaderV5B1, sizeof(TABLEHEADERV5B1));
  672. pHeaderV5B1->dwSignature = HeaderV5.dwSignature;
  673. pHeaderV5B1->wMajorVersion = (WORD)HeaderV5.dwMajorVersion;
  674. pHeaderV5B1->faRootChain = HeaderV5.rgfaIndex[0];
  675. pHeaderV5B1->faFreeRecordBlock = HeaderV5.faFreeRecordBlock;
  676. pHeaderV5B1->faFirstRecord = HeaderV5.faFirstRecord;
  677. pHeaderV5B1->faLastRecord = HeaderV5.faLastRecord;
  678. pHeaderV5B1->cRecords = HeaderV5.cRecords;
  679. pHeaderV5B1->cbAllocated = HeaderV5.cbAllocated;
  680. pHeaderV5B1->cbFreed = HeaderV5.cbFreed;
  681. pHeaderV5B1->dwReserved1 = 0;
  682. pHeaderV5B1->dwReserved2 = 0;
  683. pHeaderV5B1->cbUserData = HeaderV5.cbUserData;
  684. pHeaderV5B1->cDeletes = 0;
  685. pHeaderV5B1->cInserts = 0;
  686. pHeaderV5B1->cActiveThreads = 0;
  687. pHeaderV5B1->dwReserved3 = 0;
  688. pHeaderV5B1->cbStreams = HeaderV5.cbStreams;
  689. pHeaderV5B1->faFreeStreamBlock = HeaderV5.faFreeStreamBlock;
  690. pHeaderV5B1->faFreeChainBlock = HeaderV5.faFreeChainBlock;
  691. pHeaderV5B1->faNextAllocate = HeaderV5.faNextAllocate;
  692. pHeaderV5B1->dwNextId = HeaderV5.dwNextId;
  693. pHeaderV5B1->AllocateRecord = HeaderV5.AllocateRecord;
  694. pHeaderV5B1->AllocateChain = HeaderV5.AllocateChain;
  695. pHeaderV5B1->AllocateStream = HeaderV5.AllocateStream;
  696. pHeaderV5B1->fCorrupt = FALSE;
  697. pHeaderV5B1->fCorruptCheck = TRUE;
  698. // DowngradeIndexV5
  699. IF_FAILEXIT(hr = DowngradeIndexV5(tyMigrate, pInfo, &File, pProgress, pHeaderV5B1->faRootChain, pHeaderV5B1->faRootChain));
  700. // Reset the version
  701. pHeaderV5B1->wMajorVersion = OBJECTDB_VERSION_PRE_V5;
  702. // Set the Minor Version
  703. if (FILE_IS_NEWS_MESSAGES == pInfo->tyFile || FILE_IS_IMAP_MESSAGES == pInfo->tyFile)
  704. pHeaderV5B1->wMinorVersion = ACACHE_VERSION_PRE_V5;
  705. // Folder cache version
  706. else if (FILE_IS_LOCAL_FOLDERS == pInfo->tyFile || FILE_IS_IMAP_FOLDERS == pInfo->tyFile)
  707. pHeaderV5B1->wMinorVersion = FLDCACHE_VERSION_PRE_V5;
  708. // UIDL Cache Version
  709. else if (FILE_IS_POP3UIDL == pInfo->tyFile)
  710. pHeaderV5B1->wMinorVersion = UIDCACHE_VERSION_PRE_V5;
  711. // Bad mojo
  712. else
  713. Assert(FALSE);
  714. }
  715. exit:
  716. // Cleanup
  717. CloseMemoryFile(&File);
  718. // Done
  719. return hr;
  720. }
  721. // --------------------------------------------------------------------------------
  722. // DowngradeProcessFileListV5
  723. // --------------------------------------------------------------------------------
  724. HRESULT DowngradeProcessFileListV5(LPFILEINFO pHead, LPDWORD pcMax, LPDWORD pcbNeeded)
  725. {
  726. // Locals
  727. HRESULT hr=S_OK;
  728. MEMORYFILE File={0};
  729. LPFILEINFO pCurrent;
  730. LPTABLEHEADERV5 pHeader;
  731. // Trace
  732. TraceCall("DowngradeProcessFileListV5");
  733. // Invalid Arg
  734. Assert(pHead);
  735. // Init
  736. *pcMax = 0;
  737. *pcbNeeded = 0;
  738. // Loop
  739. for (pCurrent=pHead; pCurrent!=NULL; pCurrent=pCurrent->pNext)
  740. {
  741. // Get the File Header
  742. hr = OpenMemoryFile(pCurrent->szFilePath, &File);
  743. // Failure ?
  744. if (FAILED(hr))
  745. {
  746. // Don't Migrate
  747. pCurrent->fMigrate = FALSE;
  748. // Set hrMigrate
  749. pCurrent->hrMigrate = hr;
  750. // Reset hr
  751. hr = S_OK;
  752. // Get the LastError
  753. pCurrent->dwLastError = GetLastError();
  754. // Goto Next
  755. goto NextFile;
  756. }
  757. // De-Ref the header
  758. pHeader = (LPTABLEHEADERV5)File.pView;
  759. // Check the Signature...
  760. if (File.cbSize < sizeof(TABLEHEADERV5) || OBJECTDB_SIGNATURE != pHeader->dwSignature || OBJECTDB_VERSION_V5 != pHeader->dwMajorVersion)
  761. {
  762. // Not a file that should be migrate
  763. pCurrent->fMigrate = FALSE;
  764. // Set hrMigrate
  765. pCurrent->hrMigrate = MIGRATE_E_BADVERSION;
  766. // Goto Next
  767. goto NextFile;
  768. }
  769. // Save the Number of record
  770. pCurrent->cRecords = pHeader->cRecords;
  771. // Initialize counters
  772. InitializeCounters(&File, pCurrent, pcMax, pcbNeeded, FALSE);
  773. // Yes, Migrate
  774. pCurrent->fMigrate = TRUE;
  775. NextFile:
  776. // Close the File
  777. CloseMemoryFile(&File);
  778. }
  779. // Done
  780. return hr;
  781. }
  782. // --------------------------------------------------------------------------------
  783. // DowngradeDeleteFilesV5
  784. // --------------------------------------------------------------------------------
  785. void DowngradeDeleteFilesV5(LPFILEINFO pHeadFile)
  786. {
  787. // Locals
  788. CHAR szDstFile[MAX_PATH + MAX_PATH];
  789. LPFILEINFO pCurrent;
  790. // Trace
  791. TraceCall("DowngradeDeleteFilesV5");
  792. // Delete all files
  793. for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
  794. {
  795. // Succeeded
  796. Assert(SUCCEEDED(pCurrent->hrMigrate));
  797. // Delete the file
  798. DeleteFile(pCurrent->szFilePath);
  799. }
  800. // Done
  801. return;
  802. }
  803. // --------------------------------------------------------------------------------
  804. // DowngradeDeleteIdxMbxNchDatFilesV5
  805. // --------------------------------------------------------------------------------
  806. void DowngradeDeleteIdxMbxNchDatFilesV5(LPFILEINFO pHeadFile)
  807. {
  808. // Locals
  809. CHAR szDstFile[MAX_PATH + MAX_PATH];
  810. LPFILEINFO pCurrent;
  811. // Trace
  812. TraceCall("DowngradeDeleteIdxMbxNchDatFilesV5");
  813. // Delete all old files
  814. for (pCurrent=pHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
  815. {
  816. // If local message file, need to delete the idx file
  817. if (FILE_IS_LOCAL_MESSAGES == pCurrent->tyFile)
  818. {
  819. // Replace file extension
  820. ReplaceExtension(pCurrent->szFilePath, ".idx", szDstFile, ARRAYSIZE(szDstFile));
  821. // Delete the file
  822. DeleteFile(szDstFile);
  823. // Replace file extension
  824. ReplaceExtension(pCurrent->szFilePath, ".mbx", szDstFile, ARRAYSIZE(szDstFile));
  825. // Delete the file
  826. DeleteFile(szDstFile);
  827. }
  828. // Otherwise, pop3uidl.dat
  829. else if (FILE_IS_POP3UIDL == pCurrent->tyFile)
  830. {
  831. // Replace file extension
  832. ReplaceExtension(pCurrent->szFilePath, ".dat", szDstFile, ARRAYSIZE(szDstFile));
  833. // Delete the file
  834. DeleteFile(szDstFile);
  835. }
  836. // Otherwise, it has a .nch extension
  837. else
  838. {
  839. // Replace file extension
  840. ReplaceExtension(pCurrent->szFilePath, ".nch", szDstFile, ARRAYSIZE(szDstFile));
  841. // Delete the file
  842. DeleteFile(szDstFile);
  843. }
  844. }
  845. // Done
  846. return;
  847. }
  848. // --------------------------------------------------------------------------------
  849. // DowngradeV5
  850. // --------------------------------------------------------------------------------
  851. HRESULT DowngradeV5(MIGRATETOTYPE tyMigrate, LPCSTR pszStoreRoot,
  852. LPPROGRESSINFO pProgress, LPFILEINFO *ppHeadFile)
  853. {
  854. // Locals
  855. HRESULT hr=S_OK;
  856. ENUMFILEINFO EnumInfo={0};
  857. LPFILEINFO pCurrent;
  858. DWORD cbNeeded;
  859. DWORDLONG dwlFree;
  860. // Trace
  861. TraceCall("DowngradeV5");
  862. // Initialize
  863. *ppHeadFile = NULL;
  864. // Setup the EnumFile Info
  865. EnumInfo.pszExt = ".dbx";
  866. EnumInfo.pszFoldFile = "folders.dbx";
  867. EnumInfo.pszUidlFile = "pop3uidl.dbx";
  868. // Enumerate All ODB files in szStoreRoot...
  869. IF_FAILEXIT(hr = EnumerateStoreFiles(pszStoreRoot, DIR_IS_ROOT, NULL, &EnumInfo, ppHeadFile));
  870. // Compute some Counts, and validate that the files are valid to migrate...
  871. IF_FAILEXIT(hr = DowngradeProcessFileListV5(*ppHeadFile, &pProgress->cMax, &cbNeeded));
  872. // Delete all source files
  873. DowngradeDeleteIdxMbxNchDatFilesV5(*ppHeadFile);
  874. // Enought DiskSpace ?
  875. IF_FAILEXIT(hr = GetAvailableDiskSpace(pszStoreRoot, &dwlFree));
  876. // Not Enought Diskspace
  877. if (((DWORDLONG) cbNeeded) > dwlFree)
  878. {
  879. // cbNeeded is DWORD and in this case we can downgrade dwlFree to DWORD
  880. g_cbDiskNeeded = cbNeeded; g_cbDiskFree = ((DWORD) dwlFree);
  881. hr = TraceResult(MIGRATE_E_NOTENOUGHDISKSPACE);
  882. goto exit;
  883. }
  884. // Loop through the files and migrate each one
  885. for (pCurrent=*ppHeadFile; pCurrent!=NULL; pCurrent=pCurrent->pNext)
  886. {
  887. // Migrate this file ?
  888. if (pCurrent->fMigrate)
  889. {
  890. // Set Progress File
  891. SetProgressFile(pProgress, pCurrent);
  892. // Downgrade the file
  893. hr = pCurrent->hrMigrate = DowngradeFileV5(tyMigrate, pCurrent, pProgress);
  894. // Failure ?
  895. if (FAILED(pCurrent->hrMigrate))
  896. {
  897. // Set Last Error
  898. pCurrent->dwLastError = GetLastError();
  899. // Done
  900. break;
  901. }
  902. }
  903. }
  904. // Failure, delete all destination files
  905. if (FAILED(hr))
  906. {
  907. // Delete.idx, .mbx and .nch fles
  908. DowngradeDeleteIdxMbxNchDatFilesV5(*ppHeadFile);
  909. }
  910. // Otherwise, delete source files
  911. else
  912. {
  913. // Delete all source files
  914. DowngradeDeleteFilesV5(*ppHeadFile);
  915. }
  916. exit:
  917. // Done
  918. return hr;
  919. }