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.

1173 lines
31 KiB

  1. /*
  2. ****************************************************************************
  3. | Copyright (C) 2002 Microsoft Corporation
  4. |
  5. | Component / Subcomponent
  6. | IIS 6.0 / IIS Migration Wizard
  7. |
  8. | Based on:
  9. | http://iis6/Specs/IIS%20Migration6.0_Final.doc
  10. |
  11. | Abstract:
  12. | Utility helpers for migration
  13. |
  14. | Author:
  15. | ivelinj
  16. |
  17. | Revision History:
  18. | V1.00 March 2002
  19. |
  20. ****************************************************************************
  21. */
  22. #include "StdAfx.h"
  23. #include "utils.h"
  24. /*
  25. Concatenates wszPath and wszPathToAdd. Adds '\' between them if needed
  26. Result is stored into wszBuffer which is supposed to be large enough
  27. */
  28. void CDirTools::PathAppendLocal( LPWSTR wszBuffer, LPCWSTR wszPath, LPCWSTR wszPathToAdd )
  29. {
  30. // Buffer must be large enough!
  31. // Usually it's MAX_PATH + 1
  32. // Fail gracefully in release. However this is not expected to happen at all
  33. if ( ( ::wcslen( wszPath ) + ::wcslen( wszPathToAdd ) + 1 ) > MAX_PATH )
  34. {
  35. _ASSERT( false );
  36. throw CBaseException( IDS_E_OUTOFMEM, ERROR_SUCCESS );
  37. }
  38. if ( wszBuffer != wszPath )
  39. {
  40. ::wcscpy( wszBuffer, wszPath );
  41. }
  42. // If the second part starts with '\' - skip it
  43. LPCWSTR wszSecond = wszPathToAdd[ 0 ] == L'\\' ? ( wszPathToAdd + 1 ) : wszPathToAdd;
  44. size_t LastCh = ::wcslen( wszBuffer ) - 1;
  45. if ( wszBuffer[ LastCh ] != L'\\' )
  46. {
  47. wszBuffer[ LastCh + 1 ] = L'\\';
  48. wszBuffer[ LastCh + 2 ] = 0;
  49. }
  50. ::wcscat( wszBuffer, wszSecond );
  51. }
  52. /*
  53. Cleanup all files from a temp ( wszTempDir ) dir as well as any subdirs.
  54. If bRemoveRoot is true, wszTempDir is removed at the end
  55. */
  56. void CDirTools::CleanupDir( LPCWSTR wszTempDir,
  57. bool bRemoveRoot /*=true*/,
  58. bool bReportErrors /*=false*/ )
  59. {
  60. WCHAR wszPath[ MAX_PATH + 1 ];
  61. CFindFile Search;
  62. WIN32_FIND_DATAW fd = { 0 };
  63. bool bFound = Search.FindFirst( wszTempDir,
  64. CFindFile::ffGetDirs |
  65. CFindFile::ffGetFiles |
  66. CFindFile::ffAbsolutePaths |
  67. CFindFile::ffAddFilename,
  68. wszPath,
  69. &fd );
  70. while( bFound )
  71. {
  72. // If it is a subdir - delete recursively
  73. if ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  74. {
  75. CleanupDir( wszPath, true, bReportErrors );
  76. }
  77. else
  78. {
  79. // Clear any possible read-only flags
  80. ::SetFileAttributes( wszPath, FILE_ATTRIBUTE_NORMAL );
  81. // Delete the file.
  82. if ( !::DeleteFileW( wszPath ) && bReportErrors )
  83. {
  84. throw CObjectException( IDS_E_DELETEFILE, wszPath );
  85. }
  86. }
  87. bFound = Search.Next( NULL, wszPath, &fd );
  88. };
  89. // Remove the directory ( should be empty now )
  90. if ( bRemoveRoot )
  91. {
  92. if ( !::RemoveDirectoryW( wszTempDir ) && bReportErrors )
  93. {
  94. throw CObjectException( IDS_E_DELETEDIR, wszTempDir );
  95. }
  96. }
  97. }
  98. /*
  99. COunt the number of files ( or dirs if bDirOnly = true ) in
  100. wszDir and all subdirs. Used for the export/import events to provide progress info
  101. */
  102. DWORD CDirTools::FileCount( LPCWSTR wszDir, WORD wOptions )
  103. {
  104. DWORD dwResult = 0;
  105. CFindFile Search;
  106. bool bFound = Search.FindFirst( wszDir, wOptions, NULL, NULL );
  107. while( bFound )
  108. {
  109. ++dwResult;
  110. bFound = Search.Next( NULL, NULL, NULL );
  111. };
  112. return dwResult;
  113. }
  114. DWORDLONG CDirTools::FilesSize( LPCWSTR wszDir, WORD wOptions )
  115. {
  116. DWORDLONG dwResult = 0;
  117. WIN32_FIND_DATAW fd = { 0 };
  118. CFindFile Search;
  119. bool bFound = Search.FindFirst( wszDir, wOptions, NULL, &fd );
  120. while( bFound )
  121. {
  122. dwResult += ( fd.nFileSizeHigh << 32 ) | fd.nFileSizeLow;
  123. bFound = Search.Next( NULL, NULL, &fd );
  124. };
  125. return dwResult;
  126. }
  127. /*
  128. Checks if wszPath1 and wszPath2 are contained in one another
  129. wszPath1 and wszPath2 are expected to be fully qualified paths
  130. Return value:
  131. 0 - paths are not nested
  132. 1 - wszPath1 is subdir of wszPath2
  133. 2 - wszPath2 is subdir of wszPath1
  134. 3 - wszPath1 == wszPath2
  135. */
  136. int CDirTools::DoPathsNest( LPCWSTR wszPath1, LPCWSTR wszPath2 )
  137. {
  138. _ASSERT( ( wszPath1 != NULL ) && ( wszPath2 != NULL ) );
  139. WCHAR wszP1[ MAX_PATH + 1 ];
  140. WCHAR wszP2[ MAX_PATH + 2 ];
  141. VERIFY( ::PathCanonicalizeW( wszP1, wszPath1 ) );
  142. VERIFY( ::PathCanonicalizeW( wszP2, wszPath2 ) );
  143. ::PathAddBackslashW( wszP1 );
  144. ::PathAddBackslashW( wszP2 );
  145. // No both paths end with backslash
  146. size_t nLen1 = ::wcslen( wszP1 );
  147. size_t nLen2 = ::wcslen( wszP2 );
  148. // Strings have nothing in common
  149. if ( ::_wcsnicmp( wszP1, wszP2, min( nLen1, nLen2 ) ) != 0 ) return 0;
  150. // If wszPath1 is shorter - wszPath2 is subdir
  151. if ( nLen1 < nLen2 )
  152. {
  153. return 2;
  154. }
  155. else if ( nLen1 > nLen2 )
  156. {
  157. return 1;
  158. }
  159. // nLen1 == nLen 2 - paths match
  160. else
  161. {
  162. return 3;
  163. }
  164. }
  165. // CTempDir implementation
  166. /////////////////////////////////////////////////////////////////////////////////////////
  167. CTempDir::CTempDir( LPCWSTR wszTemplate /*= L"Migr"*/ )
  168. {
  169. WCHAR wszTempPath[ MAX_PATH + 1 ];
  170. WCHAR wszBuffer[ MAX_PATH + 1 ];
  171. // Get the temp path
  172. // The temp path always ends with slash
  173. VERIFY( ::GetTempPathW( MAX_PATH + 1, wszTempPath ) != 0 );
  174. // Build the name of the dir ( get the first 4 symbols from wszTemplate )
  175. ::swprintf( wszBuffer, L"~%.4s", wszTemplate );
  176. // Build the full template ( the temp path + the first 4 symbols from the wszTemplate
  177. // The result will look simething like "c:\Temp\~Tmpl\"
  178. CDirTools::PathAppendLocal( wszTempPath, wszTempPath, wszBuffer );
  179. // Try to create the temp subdir
  180. BYTE nIndex = 0;
  181. while( nIndex < UCHAR_MAX )
  182. {
  183. // Build the full temp path ( including the dir index )
  184. ::swprintf( wszBuffer, L"%s_%02d\\", wszTempPath, ++nIndex );
  185. // Try to create that dir
  186. if ( !::CreateDirectoryW( wszBuffer, NULL ) )
  187. {
  188. // The error is not that the dir exists - nothing more to do here
  189. if ( ::GetLastError() != ERROR_ALREADY_EXISTS )
  190. {
  191. throw CObjectException( IDS_E_CANNOT_CREATE_TEMPDIR, wszBuffer );
  192. }
  193. }
  194. else
  195. {
  196. // Exit the loop - we have temp dir now
  197. break;
  198. }
  199. };
  200. m_strDir = wszBuffer;
  201. }
  202. CTempDir::~CTempDir()
  203. {
  204. try
  205. {
  206. CleanUp();
  207. }
  208. catch(...)
  209. {
  210. if ( !std::uncaught_exception() )
  211. {
  212. throw;
  213. }
  214. }
  215. }
  216. void CTempDir::CleanUp( bool bReportErrors /*=false*/ )
  217. {
  218. if ( !m_strDir.empty() )
  219. {
  220. CDirTools::CleanupDir( m_strDir.c_str(), true, bReportErrors );
  221. m_strDir.erase();
  222. }
  223. }
  224. // CTools implementation
  225. /////////////////////////////////////////////////////////////////////////////////////////
  226. /*
  227. Return true if the current user is member of the Administrators group.
  228. Caller is NOT expected to be impersonating anyone and is expected to be able to open their own process and process token.
  229. */
  230. bool CTools::IsAdmin()
  231. {
  232. BOOL bIsAdmin = FALSE;
  233. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  234. PSID AdminSid = { 0 };
  235. if ( ::AllocateAndInitializeSid( &NtAuthority,
  236. 2, // Number of subauthorities
  237. SECURITY_BUILTIN_DOMAIN_RID,
  238. DOMAIN_ALIAS_RID_ADMINS,
  239. 0,
  240. 0,
  241. 0,
  242. 0,
  243. 0,
  244. 0,
  245. &AdminSid ) )
  246. {
  247. if ( !::CheckTokenMembership( NULL, AdminSid, &bIsAdmin ) )
  248. {
  249. bIsAdmin = FALSE;
  250. }
  251. }
  252. ::GlobalFree( AdminSid );
  253. return ( bIsAdmin != FALSE );
  254. }
  255. /*
  256. Returns true if the IISAdmin service is running
  257. We do not check for W3svc as we need the metadata only
  258. */
  259. bool CTools::IsIISRunning()
  260. {
  261. bool bResult = false;
  262. LPCWSTR SERVICE_NAME = L"IISADMIN";
  263. // Open the SCM on the local machine
  264. SC_HANDLE schSCManager = ::OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  265. _ASSERT( schSCManager != NULL ); // We alredy checked that we are Admins
  266. SC_HANDLE schService = ::OpenServiceW( schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS );
  267. // The service is not installed
  268. if ( schService != NULL )
  269. {
  270. SERVICE_STATUS ssStatus;
  271. VERIFY( ::QueryServiceStatus( schService, &ssStatus ) );
  272. bResult = ( ssStatus.dwCurrentState == SERVICE_RUNNING );
  273. VERIFY( ::CloseServiceHandle( schService ) );
  274. }
  275. VERIFY( ::CloseServiceHandle( schSCManager ) );
  276. return bResult;
  277. }
  278. /*
  279. Returns the OS ver in format ###. First digit is Major version, second - minor version,
  280. Third - 0 if Workstation, 1 if server
  281. The function returns 0 if the platform is not supported in terms of the Migration Project
  282. ( supported platforms are WinNT ver4.0 and above )
  283. */
  284. WORD CTools::GetOSVer()
  285. {
  286. OSVERSIONINFOEXW vi = { sizeof( OSVERSIONINFOEXW ) };
  287. WORD wRes = 0;
  288. VERIFY( ::GetVersionExW( reinterpret_cast<OSVERSIONINFOW*>( &vi ) ) );
  289. if ( VER_PLATFORM_WIN32_NT == vi.dwPlatformId )
  290. {
  291. wRes = static_cast<WORD>( ( vi.dwMajorVersion * 100 ) + ( vi.dwMinorVersion * 10 ) );
  292. if ( ( VER_NT_SERVER == vi.wProductType ) || ( VER_NT_DOMAIN_CONTROLLER == vi.wProductType ) )
  293. {
  294. wRes += 1;
  295. }
  296. }
  297. return wRes;
  298. }
  299. /*
  300. Returns true if the system drive is NTFS volume
  301. */
  302. bool CTools::IsNTFS()
  303. {
  304. const UINT BUFF_LEN = 32; // Should be large enough to hold the volume and the file system type
  305. WCHAR wszBuffer[ BUFF_LEN ];
  306. // Get the system drive letter
  307. VERIFY( ::ExpandEnvironmentStringsW( L"%SystemDrive%", wszBuffer, BUFF_LEN ) != 0 );
  308. // wszBuffer containts the drive only - add the slash to make the volume string
  309. ::wcscat( wszBuffer, L"\\" );
  310. DWORD dwMaxComponentLength = 0;
  311. DWORD dwSystemFlags = 0;
  312. WCHAR wszFileSystem[ BUFF_LEN ];
  313. VERIFY( ::GetVolumeInformationW( wszBuffer,
  314. NULL,
  315. 0,
  316. NULL,
  317. &dwMaxComponentLength,
  318. &dwSystemFlags,
  319. wszFileSystem,
  320. BUFF_LEN ) );
  321. return ::StrCmpIW( wszFileSystem, L"NTFS" ) == 0;
  322. }
  323. /*
  324. Sets the COM error info in the current thread
  325. */
  326. void CTools::SetErrorInfo( LPCWSTR wszError )
  327. {
  328. _ASSERT( wszError != NULL );
  329. IErrorInfoPtr spErrorInfo;
  330. ICreateErrorInfoPtr spNewErrorInfo;
  331. VERIFY( SUCCEEDED( ::CreateErrorInfo( &spNewErrorInfo ) ) );
  332. VERIFY( SUCCEEDED( spNewErrorInfo->SetDescription( const_cast<LPOLESTR>( wszError ) ) ) );
  333. spErrorInfo = spNewErrorInfo;
  334. VERIFY( SUCCEEDED( ::SetErrorInfo( 0, spErrorInfo ) ) );
  335. }
  336. void CTools::SetErrorInfoFromRes( UINT nResID )
  337. {
  338. _ASSERT( nResID != 0 );
  339. WCHAR wszBuffer[ 1024 ];
  340. VERIFY( ::LoadStringW( _Module.GetModuleInstance(), nResID, wszBuffer, 1024 ) );
  341. SetErrorInfo( wszBuffer );
  342. }
  343. // Implementation idea borrowed from trustapi.cpp
  344. bool CTools::IsSelfSignedCert( PCCERT_CONTEXT pCert )
  345. {
  346. _ASSERT( pCert != NULL );
  347. if ( !::CertCompareCertificateName( pCert->dwCertEncodingType,
  348. &pCert->pCertInfo->Issuer,
  349. &pCert->pCertInfo->Subject ) )
  350. {
  351. return false;
  352. }
  353. DWORD dwFlag = CERT_STORE_SIGNATURE_FLAG;
  354. if ( !::CertVerifySubjectCertificateContext( pCert, pCert, &dwFlag ) ||
  355. ( dwFlag & CERT_STORE_SIGNATURE_FLAG ) )
  356. {
  357. return false;
  358. }
  359. return true;
  360. }
  361. /*
  362. Checks a certificate against the local base certificate policy
  363. */
  364. bool CTools::IsValidCert( PCCERT_CONTEXT hCert, DWORD& rdwError )
  365. {
  366. _ASSERT( hCert != NULL );
  367. rdwError = ERROR_SUCCESS;
  368. // First - try to create a certificate chain for this cert
  369. PCCERT_CHAIN_CONTEXT pCertChainContext = NULL;
  370. // Use default chain parameters
  371. CERT_CHAIN_PARA CertChainPara = { sizeof( CERT_CHAIN_PARA ) };
  372. if ( !::CertGetCertificateChain( HCCE_LOCAL_MACHINE,
  373. hCert,
  374. NULL,
  375. NULL,
  376. &CertChainPara,
  377. 0,
  378. NULL,
  379. &pCertChainContext ) )
  380. {
  381. // Chain cannot be created - the certificate is not valid ( possible reason is that a issuer is missing )
  382. rdwError = ::GetLastError();
  383. return false;
  384. }
  385. CERT_CHAIN_POLICY_PARA PolicyParam = { sizeof( CERT_CHAIN_POLICY_PARA ) };
  386. CERT_CHAIN_POLICY_STATUS PolicyStatus = { sizeof( CERT_CHAIN_POLICY_STATUS ) };
  387. if ( !::CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE,
  388. pCertChainContext,
  389. &PolicyParam,
  390. &PolicyStatus ) )
  391. {
  392. rdwError = PolicyStatus.dwError;
  393. return false;
  394. }
  395. return true;
  396. }
  397. /*
  398. Adds a certificate context to one of the system cert stores ( "ROOT", "MY", "CA" )
  399. Returns the context of the inserted cert
  400. */
  401. const TCertContextHandle CTools::AddCertToSysStore( PCCERT_CONTEXT pContext, LPCWSTR wszStore, bool bReuseCerts )
  402. {
  403. _ASSERT( pContext != NULL );
  404. _ASSERT( wszStore != NULL );
  405. TCertContextHandle shResult;
  406. TCertStoreHandle shStore( ::CertOpenSystemStoreW( NULL, wszStore ) );
  407. IF_FAILED_BOOL_THROW( shStore.IsValid(),
  408. CBaseException( IDS_E_OPEN_CERT_STORE ) );
  409. IF_FAILED_BOOL_THROW( ::CertAddCertificateContextToStore( shStore.get(),
  410. pContext,
  411. bReuseCerts ?
  412. CERT_STORE_ADD_USE_EXISTING :
  413. CERT_STORE_ADD_REPLACE_EXISTING,
  414. &shResult ),
  415. CBaseException( IDS_E_ADD_CERT_STORE ) );
  416. return shResult;
  417. }
  418. /*
  419. Obtains a crypt key derived from the password
  420. */
  421. const TCryptKeyHandle CTools::GetCryptKeyFromPwd( HCRYPTPROV hCryptProv, LPCWSTR wszPassword )
  422. {
  423. _ASSERT( hCryptProv != NULL );
  424. _ASSERT( wszPassword != NULL );
  425. TCryptKeyHandle shKey;
  426. TCryptHashHandle shHash;
  427. IF_FAILED_BOOL_THROW( ::CryptCreateHash( hCryptProv,
  428. CALG_MD5,
  429. NULL,
  430. 0,
  431. &shHash ),
  432. CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
  433. // Add the password to the hash
  434. IF_FAILED_BOOL_THROW( ::CryptHashData( shHash.get(),
  435. ( BYTE* )( wszPassword ),
  436. static_cast<DWORD>( ::wcslen( wszPassword ) * sizeof( WCHAR ) ),
  437. 0 ),
  438. CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
  439. // Get a key derived from the password
  440. // Stream cypher is used
  441. IF_FAILED_BOOL_THROW( ::CryptDeriveKey( hCryptProv,
  442. CALG_RC4,
  443. shHash.get(),
  444. 0x00800000 | CRYPT_CREATE_SALT, // 128bit RC4 key
  445. &shKey ),
  446. CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
  447. return shKey;
  448. }
  449. std::wstring CTools::GetMachineName()
  450. {
  451. DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1;
  452. WCHAR wszCompName[ MAX_COMPUTERNAME_LENGTH + 1 ];
  453. VERIFY( ::GetComputerNameW( wszCompName, &dwLen ) );
  454. return std::wstring( wszCompName );
  455. }
  456. ULONGLONG CTools::GetFilePtrPos( HANDLE hFile )
  457. {
  458. _ASSERT( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) );
  459. LONG nHigh = 0;
  460. DWORD dwLow = ::SetFilePointer( hFile, 0, &nHigh, FILE_CURRENT );
  461. IF_FAILED_BOOL_THROW( ( dwLow != INVALID_SET_FILE_POINTER ) || ( ::GetLastError() == ERROR_SUCCESS ),
  462. CBaseException( IDS_E_SEEK_PKG ) );
  463. return ( ( nHigh << 32 ) | dwLow );
  464. }
  465. void CTools::SetFilePtrPos( HANDLE hFile, DWORDLONG nOffset )
  466. {
  467. _ASSERT( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) );
  468. LONG nHigh = static_cast<LONG>( nOffset >> 32 );
  469. DWORD dwLow = ::SetFilePointer( hFile,
  470. static_cast<LONG>( nOffset & ULONG_MAX ),
  471. &nHigh,
  472. FILE_BEGIN );
  473. IF_FAILED_BOOL_THROW( ( dwLow != INVALID_SET_FILE_POINTER ) || ( ::GetLastError() == ERROR_SUCCESS ),
  474. CBaseException( IDS_E_SEEK_PKG ) );
  475. }
  476. // CFindFile implementation
  477. /////////////////////////////////////////////////////////////////////////////////////////
  478. CFindFile::CFindFile()
  479. {
  480. m_wOptions = 0;
  481. }
  482. CFindFile::~CFindFile()
  483. {
  484. try
  485. {
  486. Close();
  487. }
  488. catch(...)
  489. {
  490. if ( !std::uncaught_exception() )
  491. {
  492. throw;
  493. }
  494. }
  495. }
  496. bool CFindFile::FindFirst( LPCWSTR wszDirToScan,
  497. WORD wOptions,
  498. LPWSTR wszFileDir,
  499. WIN32_FIND_DATAW* pData )
  500. {
  501. _ASSERT( wszDirToScan != NULL );
  502. _ASSERT( ::PathIsDirectory( wszDirToScan ) );
  503. _ASSERT( !m_shSearch.IsValid() ); // Call Close() first
  504. // Must search for at least files or dirs
  505. _ASSERT( ( wOptions & ffGetFiles ) || ( wOptions & ffGetDirs ) );
  506. m_wOptions = wOptions;
  507. bool bFound = false;
  508. // Push the root dir into the list
  509. m_DirsToScan.push_front( std::wstring( L"\\" ) );
  510. // Set the search path
  511. m_strRootDir = wszDirToScan;
  512. // Get the first match ( if any )
  513. bFound = Next( NULL, wszFileDir, pData );
  514. return bFound;
  515. }
  516. /*
  517. Gets the next file from e previously opened search
  518. *pbDirchanged is set to TRUE when the file is found, but not in the last dir scanned
  519. For example if the search was opened in the Dir "c:\\", the first time a file from "c:\\Temp"
  520. is returned, *pbDirChagned will be true
  521. wszDir will hold the dir where the object was found, relative to the search root dir
  522. For example if the search was opened in "c:\\", and a matching object was found in c:\\Temp" -
  523. wszDir will be "Temp"
  524. If ffAbsolutePaths is specified, the wszDir will be absolute ( includeing the name of the root dir )
  525. pData will be filled with the info about the match found
  526. */
  527. bool CFindFile::Next( bool* pbDirChanged,
  528. LPWSTR wszDir,
  529. WIN32_FIND_DATAW* pData )
  530. {
  531. WCHAR wszBuffer[ MAX_PATH + 1 ];
  532. bool bDirChanged = true;
  533. bool bFound = false;
  534. WIN32_FIND_DATAW fd = { 0 };
  535. // Try to find a match in the current search
  536. if ( m_shSearch.IsValid() )
  537. {
  538. bFound = ContinueCurrent( /*r*/fd );
  539. bDirChanged = !bFound; // No file was found in the current dir - new dir will be scanned
  540. }
  541. // If nothing found - try to find something in the rest of the subdirs
  542. while( !bFound && !m_DirsToScan.empty() )
  543. {
  544. // Get a dir from the list with pending dirs
  545. const std::wstring strCurrentDir = m_DirsToScan.front();
  546. m_DirsToScan.pop_front();
  547. // Create the full path to the current dir
  548. CDirTools::PathAppendLocal( wszBuffer, m_strRootDir.c_str(), strCurrentDir.c_str() );
  549. bFound = ScanDir( wszBuffer, strCurrentDir.c_str(), /*r*/fd );
  550. };
  551. if ( bFound )
  552. {
  553. // Set the dir where the file was found
  554. // It will be absolute or relative to the search root
  555. if ( wszDir != NULL )
  556. {
  557. CDirTools::PathAppendLocal( wszDir,
  558. ffAbsolutePaths & m_wOptions ? m_strRootDir.c_str() : L"",
  559. m_strCurrentDir.c_str() );
  560. // Add the filename if needed
  561. if ( ffAddFilename & m_wOptions )
  562. {
  563. CDirTools::PathAppendLocal( wszDir, wszDir, fd.cFileName );
  564. }
  565. }
  566. if ( pbDirChanged != NULL )
  567. {
  568. *pbDirChanged = ( bFound ? bDirChanged : false );
  569. }
  570. if ( pData != NULL )
  571. {
  572. *pData = fd;
  573. }
  574. }
  575. else
  576. {
  577. if ( wszDir != NULL )
  578. {
  579. wszDir[ 0 ] = L'\0';
  580. }
  581. // Close the search if no more matches
  582. Close();
  583. }
  584. return bFound;
  585. }
  586. void CFindFile::Close()
  587. {
  588. m_wOptions = 0;
  589. m_shSearch.Close();
  590. m_DirsToScan.clear();
  591. m_strRootDir.erase();
  592. m_strCurrentDir.erase();
  593. }
  594. /*
  595. Scans only the wszDir for appropriate result
  596. */
  597. bool CFindFile::ScanDir( LPCWSTR wszDirToScan, LPCWSTR wszRelativeDir, WIN32_FIND_DATAW& FileData )
  598. {
  599. _ASSERT( wszDirToScan != NULL );
  600. _ASSERT( wszRelativeDir != NULL );
  601. WCHAR wszBuffer[ MAX_PATH + 1 ];
  602. WIN32_FIND_DATAW fd = { 0 };
  603. ::ZeroMemory( &FileData, sizeof( FileData ) );
  604. // Build the search string
  605. CDirTools::PathAppendLocal( wszBuffer, wszDirToScan, L"*.*" );
  606. m_shSearch = ::FindFirstFileW( wszBuffer, &fd );
  607. if ( !m_shSearch.IsValid() ) throw CObjectException( IDS_E_ENUM_FILES, wszDirToScan );
  608. bool bFileFound = false;
  609. // Find the first file/dir to return
  610. do
  611. {
  612. bFileFound = CheckFile( wszRelativeDir, fd );
  613. }while( !bFileFound && ::FindNextFileW( m_shSearch.get(), &fd ) );
  614. // Close the search if a file was not found - we don't need it anymore
  615. if ( !bFileFound )
  616. {
  617. m_shSearch.Close();
  618. m_strCurrentDir.erase();
  619. }
  620. else
  621. {
  622. m_strCurrentDir = wszRelativeDir;
  623. }
  624. FileData = fd;
  625. return bFileFound;
  626. }
  627. /*
  628. Check if data in 'fd' is a match for us
  629. If the file is a dir, it will be added to the pending dirs
  630. Returns True if the file is OK. or False if search must continue
  631. */
  632. bool CFindFile::CheckFile( LPCWSTR wszRelativeDir, const WIN32_FIND_DATAW& fd )
  633. {
  634. WCHAR wszBuffer[ MAX_PATH + 1 ];
  635. bool bFileFound = false;
  636. // Current file is dir
  637. if ( ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
  638. {
  639. // Every dir contains at least two dirs - "." and "..". Skip them
  640. if ( fd.cFileName[ 0 ] != L'.' )
  641. {
  642. // If we do a recursive search - add the dir name to the relative path
  643. // and push it in the list. This dir will be scaned later for files/dirs
  644. if ( m_wOptions & ffRecursive )
  645. {
  646. CDirTools::PathAppendLocal( wszBuffer, wszRelativeDir, fd.cFileName );
  647. m_DirsToScan.push_back( std::wstring( wszBuffer ) );
  648. }
  649. // If we are searching for dirs - we have the result
  650. bFileFound = ( m_wOptions & ffGetDirs ) != 0;
  651. }
  652. }
  653. else
  654. {
  655. // File is found.
  656. // If we are searching for files - we have the result
  657. bFileFound = ( m_wOptions & ffGetFiles ) != 0;
  658. }
  659. return bFileFound;
  660. }
  661. /*
  662. Try to find a match from the current m_shSearch.
  663. Return True if there is a match. False otherwise
  664. */
  665. bool CFindFile::ContinueCurrent( WIN32_FIND_DATAW& FileData )
  666. {
  667. _ASSERT( m_shSearch.IsValid() );
  668. bool bFileFound = false;
  669. while( !bFileFound && ::FindNextFileW( m_shSearch.get(), &FileData ) )
  670. {
  671. bFileFound = CheckFile( m_strCurrentDir.c_str(), FileData );
  672. };
  673. // Close the current search handle. Not needed anymore
  674. if ( !bFileFound )
  675. {
  676. m_shSearch.Close();
  677. m_strCurrentDir.erase();
  678. }
  679. return bFileFound;
  680. }
  681. // CXMLTools implementation
  682. /////////////////////////////////////////////////////////////////////////////////////////
  683. IXMLDOMElementPtr CXMLTools::AddTextNode( const IXMLDOMDocumentPtr& spDoc,
  684. const IXMLDOMElementPtr& spEl,
  685. LPCWSTR wszNodeName,
  686. LPCWSTR wszValue )
  687. {
  688. IXMLDOMElementPtr spNode;
  689. IXMLDOMTextPtr spText;
  690. _bstr_t bstrName( wszNodeName );
  691. _bstr_t bstrValue( wszValue );
  692. IF_FAILED_HR_THROW( spDoc->createElement( bstrName, &spNode ),
  693. CBaseException( IDS_E_XML_GENERATE ) );
  694. IF_FAILED_HR_THROW( spDoc->createTextNode( bstrValue, &spText ),
  695. CBaseException( IDS_E_XML_GENERATE ) );
  696. IF_FAILED_HR_THROW( spNode->appendChild( spText, NULL ),
  697. CBaseException( IDS_E_XML_GENERATE ) );
  698. IF_FAILED_HR_THROW( spEl->appendChild( spNode, NULL ),
  699. CBaseException( IDS_E_XML_GENERATE ) );
  700. return spNode;
  701. }
  702. const std::wstring CXMLTools::GetAttrib( const IXMLDOMNodePtr& spElement, LPCWSTR wszName )
  703. {
  704. _ASSERT( spElement != NULL );
  705. _ASSERT( wszName != NULL );
  706. IXMLDOMNamedNodeMapPtr spAttribs;
  707. IXMLDOMNodePtr spNode;
  708. CComBSTR bstrRes;
  709. // Get the attribs collection
  710. IF_FAILED_HR_THROW( spElement->get_attributes( &spAttribs ),
  711. CBaseException( IDS_E_XML_PARSE ) );
  712. // Get the attrib of interest
  713. // This succeeds even if the item is not found
  714. IF_FAILED_HR_THROW( spAttribs->getNamedItem( _bstr_t( wszName ), &spNode ),
  715. CBaseException( IDS_E_XML_PARSE ) );
  716. if ( spNode == NULL ) throw CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND );
  717. // Get the value
  718. IF_FAILED_HR_THROW( spNode->get_text( &bstrRes ),
  719. CBaseException( IDS_E_XML_PARSE ) );
  720. return std::wstring( bstrRes );
  721. }
  722. void CXMLTools::LoadXMLFile( LPCWSTR wszFile, IXMLDOMDocumentPtr& rspDoc )
  723. {
  724. _variant_t vntFile = wszFile;
  725. VARIANT_BOOL vntRes = VARIANT_FALSE;
  726. IXMLDOMDocumentPtr spDoc;
  727. // Create doc instance
  728. IF_FAILED_HR_THROW( spDoc.CreateInstance( CLSID_DOMDocument ),
  729. CBaseException( IDS_E_NO_XML_PARSER ) );
  730. // Retursn success always
  731. VERIFY( SUCCEEDED( spDoc->load( vntFile, &vntRes ) ) );
  732. if ( vntRes != VARIANT_TRUE )
  733. {
  734. throw CObjectException( IDS_E_READFILE, wszFile );
  735. }
  736. rspDoc.Attach( spDoc.Detach() );
  737. }
  738. /*
  739. Removes all nodes that match the specified XPath query
  740. */
  741. void CXMLTools::RemoveNodes( const IXMLDOMElementPtr& spContext, LPCWSTR wszXPath )
  742. {
  743. IXMLDOMNodeListPtr spList;
  744. IXMLDOMNodePtr spNode;
  745. IF_FAILED_HR_THROW( spContext->selectNodes( _bstr_t( wszXPath ), &spList ),
  746. CBaseException( IDS_E_XML_PARSE ) );
  747. while( S_OK == spList->nextNode( &spNode ) )
  748. {
  749. IXMLDOMNodePtr spParent;
  750. IF_FAILED_HR_THROW( spNode->get_parentNode( &spParent ),
  751. CBaseException( IDS_E_XML_GENERATE ) );
  752. IF_FAILED_HR_THROW( spParent->removeChild( spNode, NULL ),
  753. CBaseException( IDS_E_XML_GENERATE ) );
  754. };
  755. }
  756. /*
  757. Get's a data from an XML doc
  758. If an attrib name is specified - the attrib value is returned. Otherwise - the element's text
  759. The data is located with an XPath query. It is an error fo this query to return more then 1 node
  760. If the data is missing - the default value is used. If no default value - it's an error for the data to be missing
  761. */
  762. const std::wstring CXMLTools::GetDataValue( const IXMLDOMNodePtr& spRoot,
  763. LPCWSTR wszQuery,
  764. LPCWSTR wszAttrib,
  765. LPCWSTR wszDefault )
  766. {
  767. _ASSERT( wszQuery != NULL );
  768. _ASSERT( spRoot != NULL );
  769. IXMLDOMNodeListPtr spList;
  770. IXMLDOMNodePtr spDataEl;
  771. std::wstring strRes( wszDefault != NULL ? wszDefault : L"" );
  772. // Get the node
  773. IF_FAILED_HR_THROW( spRoot->selectNodes( _bstr_t( wszQuery ), &spList ),
  774. CBaseException( IDS_E_XML_PARSE ) );
  775. long nCount = 0;
  776. if ( FAILED( spList->get_length( &nCount ) ) || ( nCount > 1 ) )
  777. {
  778. throw CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA );
  779. }
  780. if ( 0 == nCount )
  781. {
  782. // The data is missing and no default was provided - error
  783. IF_FAILED_BOOL_THROW( wszDefault != NULL, CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
  784. }
  785. else
  786. {
  787. VERIFY( S_OK == spList->nextNode( &spDataEl ) );
  788. if ( wszAttrib != NULL )
  789. {
  790. strRes = CXMLTools::GetAttrib( spDataEl, wszAttrib );
  791. }
  792. else
  793. {
  794. CComBSTR bstrData;
  795. IF_FAILED_HR_THROW( spDataEl->get_text( &bstrData ),
  796. CBaseException( IDS_E_XML_PARSE ) );
  797. strRes = bstrData;
  798. }
  799. }
  800. return strRes;
  801. }
  802. const std::wstring CXMLTools::GetDataValueAbs( const IXMLDOMDocumentPtr& spDoc,
  803. LPCWSTR wszQuery,
  804. LPCWSTR wszAttrib,
  805. LPCWSTR wszDefault )
  806. {
  807. IXMLDOMElementPtr spRoot;
  808. IF_FAILED_HR_THROW( spDoc->get_documentElement( &spRoot ),
  809. CBaseException( IDS_E_XML_PARSE ) );
  810. return GetDataValue( spRoot, wszQuery, wszAttrib, wszDefault );
  811. }
  812. /*
  813. Changes a element value or element's attrib value
  814. The element is located with the wszQuery XPath
  815. wszAttrib is the name of the attrib to change. If NULL - the element value is changed
  816. The new value is wszNewValue
  817. If the element cannot be find, a new child element is added to spRoot with tha wszNeElName name
  818. and the data is set either is the element text or as an attrib ( depending on wszAttrib value )
  819. */
  820. const IXMLDOMNodePtr CXMLTools::SetDataValue( const IXMLDOMNodePtr& spRoot,
  821. LPCWSTR wszQuery,
  822. LPCWSTR wszAttrib,
  823. LPCWSTR wszNewValue,
  824. LPCWSTR wszNewElName /*=NULL*/ )
  825. {
  826. _ASSERT( wszQuery != NULL );
  827. _ASSERT( spRoot != NULL );
  828. IXMLDOMNodeListPtr spList;
  829. IXMLDOMNodePtr spDataEl;
  830. // Get the node
  831. IF_FAILED_HR_THROW( spRoot->selectNodes( _bstr_t( wszQuery ), &spList ),
  832. CBaseException( IDS_E_XML_PARSE ) );
  833. long nCount = 0;
  834. IF_FAILED_BOOL_THROW( SUCCEEDED( spList->get_length( &nCount ) ) && ( 1 == nCount ),
  835. CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA ) );
  836. // If the value is not already here and a name is provided - add it
  837. if ( S_FALSE == spList->nextNode( &spDataEl ) )
  838. {
  839. IF_FAILED_BOOL_THROW( wszNewElName != NULL,
  840. CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
  841. IXMLDOMDocumentPtr spDoc;
  842. IF_FAILED_HR_THROW( spRoot->get_ownerDocument( &spDoc ),
  843. CBaseException( IDS_E_XML_PARSE ) );
  844. spDataEl = CreateSubNode( spDoc, spRoot, wszNewElName );
  845. }
  846. if ( wszAttrib != NULL )
  847. {
  848. CXMLTools::SetAttrib( spDataEl, wszAttrib, wszNewValue );
  849. }
  850. else
  851. {
  852. IF_FAILED_HR_THROW( spDataEl->put_text( _bstr_t( wszNewValue ) ),
  853. CBaseException( IDS_E_XML_PARSE ) );
  854. }
  855. return spDataEl;
  856. }
  857. // Convert class implementation
  858. /////////////////////////////////////////////////////////////////////////////////////////
  859. const std::wstring Convert::ToString( const BYTE* pvData, DWORD dwSize )
  860. {
  861. // Each byte takes 2 symbols. And we need one symbol for the '\0'
  862. std::wstring str( ( dwSize * 2 ) + 1, L' ' );
  863. for ( UINT x = 0, y = 0 ; x < dwSize ; ++x )
  864. {
  865. str[ y++ ] = ByteToWChar( pvData[ x ] >> 4 );
  866. str[ y++ ] = ByteToWChar( pvData[ x ] & 0x0f );
  867. }
  868. str[ y ] = L'\0';
  869. return str;
  870. }
  871. void Convert::ToBLOB( LPCWSTR wszData, TByteAutoPtr& rspData, DWORD& rdwDataSize )
  872. {
  873. _ASSERT( wszData != NULL );
  874. _ASSERT( ::wcscspn( wszData, L"ABCDEF" ) == ::wcslen( wszData ) ); // The string must be lower-case!!!
  875. _ASSERT( ( ::wcslen( wszData ) % 2 ) == 0 );
  876. // Calc the size
  877. DWORD dwSize = static_cast<DWORD>( ::wcslen( wszData ) / 2 ); // Each byte takes 2 symbols
  878. // Alloc the buffer
  879. TByteAutoPtr spData( new BYTE[ dwSize ] );
  880. BYTE* pbtDest = spData.get();
  881. DWORD dwSymbol = 0;
  882. DWORD dwByte = 0;
  883. while( wszData[ dwSymbol ] != L'\0' )
  884. {
  885. pbtDest[ dwByte ] = WCharsToByte( wszData[ dwSymbol + 1 ], wszData[ dwSymbol ] );
  886. ++dwByte;
  887. dwSymbol += 2;
  888. };
  889. rspData = spData;
  890. rdwDataSize = dwSize;
  891. }
  892. DWORD Convert::ToDWORD( LPCWSTR wszData )
  893. {
  894. int nRes = 0;
  895. IF_FAILED_BOOL_THROW( ::StrToIntExW( wszData, STIF_DEFAULT, &nRes ),
  896. CBaseException( IDS_E_INVALIDARG, ERROR_INVALID_DATA ) );
  897. return static_cast<DWORD>( nRes );
  898. }