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.
1174 lines
31 KiB
1174 lines
31 KiB
/*
|
|
****************************************************************************
|
|
| Copyright (C) 2002 Microsoft Corporation
|
|
|
|
|
| Component / Subcomponent
|
|
| IIS 6.0 / IIS Migration Wizard
|
|
|
|
|
| Based on:
|
|
| http://iis6/Specs/IIS%20Migration6.0_Final.doc
|
|
|
|
|
| Abstract:
|
|
| Utility helpers for migration
|
|
|
|
|
| Author:
|
|
| ivelinj
|
|
|
|
|
| Revision History:
|
|
| V1.00 March 2002
|
|
|
|
|
****************************************************************************
|
|
*/
|
|
#include "StdAfx.h"
|
|
#include "utils.h"
|
|
|
|
|
|
/*
|
|
Concatenates wszPath and wszPathToAdd. Adds '\' between them if needed
|
|
Result is stored into wszBuffer which is supposed to be large enough
|
|
*/
|
|
void CDirTools::PathAppendLocal( LPWSTR wszBuffer, LPCWSTR wszPath, LPCWSTR wszPathToAdd )
|
|
{
|
|
// Buffer must be large enough!
|
|
// Usually it's MAX_PATH + 1
|
|
// Fail gracefully in release. However this is not expected to happen at all
|
|
if ( ( ::wcslen( wszPath ) + ::wcslen( wszPathToAdd ) + 1 ) > MAX_PATH )
|
|
{
|
|
_ASSERT( false );
|
|
throw CBaseException( IDS_E_OUTOFMEM, ERROR_SUCCESS );
|
|
}
|
|
|
|
if ( wszBuffer != wszPath )
|
|
{
|
|
::wcscpy( wszBuffer, wszPath );
|
|
}
|
|
|
|
// If the second part starts with '\' - skip it
|
|
LPCWSTR wszSecond = wszPathToAdd[ 0 ] == L'\\' ? ( wszPathToAdd + 1 ) : wszPathToAdd;
|
|
|
|
size_t LastCh = ::wcslen( wszBuffer ) - 1;
|
|
if ( wszBuffer[ LastCh ] != L'\\' )
|
|
{
|
|
wszBuffer[ LastCh + 1 ] = L'\\';
|
|
wszBuffer[ LastCh + 2 ] = 0;
|
|
}
|
|
|
|
::wcscat( wszBuffer, wszSecond );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
Cleanup all files from a temp ( wszTempDir ) dir as well as any subdirs.
|
|
If bRemoveRoot is true, wszTempDir is removed at the end
|
|
*/
|
|
void CDirTools::CleanupDir( LPCWSTR wszTempDir,
|
|
bool bRemoveRoot /*=true*/,
|
|
bool bReportErrors /*=false*/ )
|
|
{
|
|
WCHAR wszPath[ MAX_PATH + 1 ];
|
|
CFindFile Search;
|
|
|
|
WIN32_FIND_DATAW fd = { 0 };
|
|
|
|
bool bFound = Search.FindFirst( wszTempDir,
|
|
CFindFile::ffGetDirs |
|
|
CFindFile::ffGetFiles |
|
|
CFindFile::ffAbsolutePaths |
|
|
CFindFile::ffAddFilename,
|
|
wszPath,
|
|
&fd );
|
|
|
|
while( bFound )
|
|
{
|
|
// If it is a subdir - delete recursively
|
|
if ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
|
|
{
|
|
CleanupDir( wszPath, true, bReportErrors );
|
|
}
|
|
else
|
|
{
|
|
// Clear any possible read-only flags
|
|
::SetFileAttributes( wszPath, FILE_ATTRIBUTE_NORMAL );
|
|
|
|
// Delete the file.
|
|
if ( !::DeleteFileW( wszPath ) && bReportErrors )
|
|
{
|
|
throw CObjectException( IDS_E_DELETEFILE, wszPath );
|
|
}
|
|
}
|
|
|
|
bFound = Search.Next( NULL, wszPath, &fd );
|
|
};
|
|
|
|
// Remove the directory ( should be empty now )
|
|
if ( bRemoveRoot )
|
|
{
|
|
if ( !::RemoveDirectoryW( wszTempDir ) && bReportErrors )
|
|
{
|
|
throw CObjectException( IDS_E_DELETEDIR, wszTempDir );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
COunt the number of files ( or dirs if bDirOnly = true ) in
|
|
wszDir and all subdirs. Used for the export/import events to provide progress info
|
|
*/
|
|
DWORD CDirTools::FileCount( LPCWSTR wszDir, WORD wOptions )
|
|
{
|
|
DWORD dwResult = 0;
|
|
CFindFile Search;
|
|
|
|
bool bFound = Search.FindFirst( wszDir, wOptions, NULL, NULL );
|
|
|
|
while( bFound )
|
|
{
|
|
++dwResult;
|
|
bFound = Search.Next( NULL, NULL, NULL );
|
|
};
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
DWORDLONG CDirTools::FilesSize( LPCWSTR wszDir, WORD wOptions )
|
|
{
|
|
DWORDLONG dwResult = 0;
|
|
WIN32_FIND_DATAW fd = { 0 };
|
|
CFindFile Search;
|
|
|
|
|
|
bool bFound = Search.FindFirst( wszDir, wOptions, NULL, &fd );
|
|
|
|
while( bFound )
|
|
{
|
|
dwResult += ( fd.nFileSizeHigh << 32 ) | fd.nFileSizeLow;
|
|
bFound = Search.Next( NULL, NULL, &fd );
|
|
};
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
/*
|
|
Checks if wszPath1 and wszPath2 are contained in one another
|
|
wszPath1 and wszPath2 are expected to be fully qualified paths
|
|
Return value:
|
|
0 - paths are not nested
|
|
1 - wszPath1 is subdir of wszPath2
|
|
2 - wszPath2 is subdir of wszPath1
|
|
3 - wszPath1 == wszPath2
|
|
*/
|
|
int CDirTools::DoPathsNest( LPCWSTR wszPath1, LPCWSTR wszPath2 )
|
|
{
|
|
_ASSERT( ( wszPath1 != NULL ) && ( wszPath2 != NULL ) );
|
|
|
|
WCHAR wszP1[ MAX_PATH + 1 ];
|
|
WCHAR wszP2[ MAX_PATH + 2 ];
|
|
|
|
VERIFY( ::PathCanonicalizeW( wszP1, wszPath1 ) );
|
|
VERIFY( ::PathCanonicalizeW( wszP2, wszPath2 ) );
|
|
::PathAddBackslashW( wszP1 );
|
|
::PathAddBackslashW( wszP2 );
|
|
|
|
// No both paths end with backslash
|
|
|
|
size_t nLen1 = ::wcslen( wszP1 );
|
|
size_t nLen2 = ::wcslen( wszP2 );
|
|
|
|
// Strings have nothing in common
|
|
if ( ::_wcsnicmp( wszP1, wszP2, min( nLen1, nLen2 ) ) != 0 ) return 0;
|
|
|
|
// If wszPath1 is shorter - wszPath2 is subdir
|
|
if ( nLen1 < nLen2 )
|
|
{
|
|
return 2;
|
|
}
|
|
else if ( nLen1 > nLen2 )
|
|
{
|
|
return 1;
|
|
}
|
|
// nLen1 == nLen 2 - paths match
|
|
else
|
|
{
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// CTempDir implementation
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
CTempDir::CTempDir( LPCWSTR wszTemplate /*= L"Migr"*/ )
|
|
{
|
|
WCHAR wszTempPath[ MAX_PATH + 1 ];
|
|
WCHAR wszBuffer[ MAX_PATH + 1 ];
|
|
|
|
// Get the temp path
|
|
// The temp path always ends with slash
|
|
VERIFY( ::GetTempPathW( MAX_PATH + 1, wszTempPath ) != 0 );
|
|
|
|
// Build the name of the dir ( get the first 4 symbols from wszTemplate )
|
|
::swprintf( wszBuffer, L"~%.4s", wszTemplate );
|
|
|
|
// Build the full template ( the temp path + the first 4 symbols from the wszTemplate
|
|
// The result will look simething like "c:\Temp\~Tmpl\"
|
|
CDirTools::PathAppendLocal( wszTempPath, wszTempPath, wszBuffer );
|
|
|
|
// Try to create the temp subdir
|
|
BYTE nIndex = 0;
|
|
|
|
while( nIndex < UCHAR_MAX )
|
|
{
|
|
// Build the full temp path ( including the dir index )
|
|
::swprintf( wszBuffer, L"%s_%02d\\", wszTempPath, ++nIndex );
|
|
|
|
// Try to create that dir
|
|
if ( !::CreateDirectoryW( wszBuffer, NULL ) )
|
|
{
|
|
// The error is not that the dir exists - nothing more to do here
|
|
if ( ::GetLastError() != ERROR_ALREADY_EXISTS )
|
|
{
|
|
throw CObjectException( IDS_E_CANNOT_CREATE_TEMPDIR, wszBuffer );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Exit the loop - we have temp dir now
|
|
break;
|
|
}
|
|
};
|
|
|
|
m_strDir = wszBuffer;
|
|
}
|
|
|
|
|
|
CTempDir::~CTempDir()
|
|
{
|
|
try
|
|
{
|
|
CleanUp();
|
|
}
|
|
catch(...)
|
|
{
|
|
if ( !std::uncaught_exception() )
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CTempDir::CleanUp( bool bReportErrors /*=false*/ )
|
|
{
|
|
if ( !m_strDir.empty() )
|
|
{
|
|
CDirTools::CleanupDir( m_strDir.c_str(), true, bReportErrors );
|
|
m_strDir.erase();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CTools implementation
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
Return true if the current user is member of the Administrators group.
|
|
Caller is NOT expected to be impersonating anyone and is expected to be able to open their own process and process token.
|
|
*/
|
|
bool CTools::IsAdmin()
|
|
{
|
|
BOOL bIsAdmin = FALSE;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
PSID AdminSid = { 0 };
|
|
|
|
if ( ::AllocateAndInitializeSid( &NtAuthority,
|
|
2, // Number of subauthorities
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
&AdminSid ) )
|
|
{
|
|
if ( !::CheckTokenMembership( NULL, AdminSid, &bIsAdmin ) )
|
|
{
|
|
bIsAdmin = FALSE;
|
|
}
|
|
}
|
|
|
|
::GlobalFree( AdminSid );
|
|
|
|
return ( bIsAdmin != FALSE );
|
|
}
|
|
|
|
|
|
/*
|
|
Returns true if the IISAdmin service is running
|
|
We do not check for W3svc as we need the metadata only
|
|
*/
|
|
bool CTools::IsIISRunning()
|
|
{
|
|
bool bResult = false;
|
|
|
|
LPCWSTR SERVICE_NAME = L"IISADMIN";
|
|
|
|
// Open the SCM on the local machine
|
|
SC_HANDLE schSCManager = ::OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
|
|
_ASSERT( schSCManager != NULL ); // We alredy checked that we are Admins
|
|
|
|
SC_HANDLE schService = ::OpenServiceW( schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS );
|
|
|
|
// The service is not installed
|
|
if ( schService != NULL )
|
|
{
|
|
SERVICE_STATUS ssStatus;
|
|
|
|
VERIFY( ::QueryServiceStatus( schService, &ssStatus ) );
|
|
|
|
bResult = ( ssStatus.dwCurrentState == SERVICE_RUNNING );
|
|
|
|
VERIFY( ::CloseServiceHandle( schService ) );
|
|
}
|
|
|
|
VERIFY( ::CloseServiceHandle( schSCManager ) );
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*
|
|
Returns the OS ver in format ###. First digit is Major version, second - minor version,
|
|
Third - 0 if Workstation, 1 if server
|
|
The function returns 0 if the platform is not supported in terms of the Migration Project
|
|
( supported platforms are WinNT ver4.0 and above )
|
|
|
|
*/
|
|
WORD CTools::GetOSVer()
|
|
{
|
|
OSVERSIONINFOEXW vi = { sizeof( OSVERSIONINFOEXW ) };
|
|
WORD wRes = 0;
|
|
|
|
VERIFY( ::GetVersionExW( reinterpret_cast<OSVERSIONINFOW*>( &vi ) ) );
|
|
|
|
if ( VER_PLATFORM_WIN32_NT == vi.dwPlatformId )
|
|
{
|
|
wRes = static_cast<WORD>( ( vi.dwMajorVersion * 100 ) + ( vi.dwMinorVersion * 10 ) );
|
|
|
|
if ( ( VER_NT_SERVER == vi.wProductType ) || ( VER_NT_DOMAIN_CONTROLLER == vi.wProductType ) )
|
|
{
|
|
wRes += 1;
|
|
}
|
|
}
|
|
|
|
return wRes;
|
|
}
|
|
|
|
|
|
/*
|
|
Returns true if the system drive is NTFS volume
|
|
*/
|
|
bool CTools::IsNTFS()
|
|
{
|
|
const UINT BUFF_LEN = 32; // Should be large enough to hold the volume and the file system type
|
|
|
|
WCHAR wszBuffer[ BUFF_LEN ];
|
|
|
|
// Get the system drive letter
|
|
VERIFY( ::ExpandEnvironmentStringsW( L"%SystemDrive%", wszBuffer, BUFF_LEN ) != 0 );
|
|
|
|
// wszBuffer containts the drive only - add the slash to make the volume string
|
|
::wcscat( wszBuffer, L"\\" );
|
|
|
|
DWORD dwMaxComponentLength = 0;
|
|
DWORD dwSystemFlags = 0;
|
|
|
|
WCHAR wszFileSystem[ BUFF_LEN ];
|
|
|
|
VERIFY( ::GetVolumeInformationW( wszBuffer,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&dwMaxComponentLength,
|
|
&dwSystemFlags,
|
|
wszFileSystem,
|
|
BUFF_LEN ) );
|
|
|
|
return ::StrCmpIW( wszFileSystem, L"NTFS" ) == 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Sets the COM error info in the current thread
|
|
*/
|
|
void CTools::SetErrorInfo( LPCWSTR wszError )
|
|
{
|
|
_ASSERT( wszError != NULL );
|
|
|
|
IErrorInfoPtr spErrorInfo;
|
|
ICreateErrorInfoPtr spNewErrorInfo;
|
|
|
|
VERIFY( SUCCEEDED( ::CreateErrorInfo( &spNewErrorInfo ) ) );
|
|
|
|
VERIFY( SUCCEEDED( spNewErrorInfo->SetDescription( const_cast<LPOLESTR>( wszError ) ) ) );
|
|
|
|
spErrorInfo = spNewErrorInfo;
|
|
|
|
VERIFY( SUCCEEDED( ::SetErrorInfo( 0, spErrorInfo ) ) );
|
|
}
|
|
|
|
|
|
|
|
void CTools::SetErrorInfoFromRes( UINT nResID )
|
|
{
|
|
_ASSERT( nResID != 0 );
|
|
|
|
WCHAR wszBuffer[ 1024 ];
|
|
|
|
VERIFY( ::LoadStringW( _Module.GetModuleInstance(), nResID, wszBuffer, 1024 ) );
|
|
|
|
SetErrorInfo( wszBuffer );
|
|
}
|
|
|
|
|
|
|
|
// Implementation idea borrowed from trustapi.cpp
|
|
bool CTools::IsSelfSignedCert( PCCERT_CONTEXT pCert )
|
|
{
|
|
_ASSERT( pCert != NULL );
|
|
|
|
if ( !::CertCompareCertificateName( pCert->dwCertEncodingType,
|
|
&pCert->pCertInfo->Issuer,
|
|
&pCert->pCertInfo->Subject ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
DWORD dwFlag = CERT_STORE_SIGNATURE_FLAG;
|
|
|
|
if ( !::CertVerifySubjectCertificateContext( pCert, pCert, &dwFlag ) ||
|
|
( dwFlag & CERT_STORE_SIGNATURE_FLAG ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
Checks a certificate against the local base certificate policy
|
|
*/
|
|
bool CTools::IsValidCert( PCCERT_CONTEXT hCert, DWORD& rdwError )
|
|
{
|
|
_ASSERT( hCert != NULL );
|
|
|
|
rdwError = ERROR_SUCCESS;
|
|
|
|
// First - try to create a certificate chain for this cert
|
|
PCCERT_CHAIN_CONTEXT pCertChainContext = NULL;
|
|
|
|
// Use default chain parameters
|
|
CERT_CHAIN_PARA CertChainPara = { sizeof( CERT_CHAIN_PARA ) };
|
|
|
|
if ( !::CertGetCertificateChain( HCCE_LOCAL_MACHINE,
|
|
hCert,
|
|
NULL,
|
|
NULL,
|
|
&CertChainPara,
|
|
0,
|
|
NULL,
|
|
&pCertChainContext ) )
|
|
{
|
|
// Chain cannot be created - the certificate is not valid ( possible reason is that a issuer is missing )
|
|
rdwError = ::GetLastError();
|
|
return false;
|
|
}
|
|
|
|
CERT_CHAIN_POLICY_PARA PolicyParam = { sizeof( CERT_CHAIN_POLICY_PARA ) };
|
|
CERT_CHAIN_POLICY_STATUS PolicyStatus = { sizeof( CERT_CHAIN_POLICY_STATUS ) };
|
|
|
|
if ( !::CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE,
|
|
pCertChainContext,
|
|
&PolicyParam,
|
|
&PolicyStatus ) )
|
|
{
|
|
rdwError = PolicyStatus.dwError;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
Adds a certificate context to one of the system cert stores ( "ROOT", "MY", "CA" )
|
|
Returns the context of the inserted cert
|
|
*/
|
|
const TCertContextHandle CTools::AddCertToSysStore( PCCERT_CONTEXT pContext, LPCWSTR wszStore, bool bReuseCerts )
|
|
{
|
|
_ASSERT( pContext != NULL );
|
|
_ASSERT( wszStore != NULL );
|
|
|
|
TCertContextHandle shResult;
|
|
TCertStoreHandle shStore( ::CertOpenSystemStoreW( NULL, wszStore ) );
|
|
|
|
IF_FAILED_BOOL_THROW( shStore.IsValid(),
|
|
CBaseException( IDS_E_OPEN_CERT_STORE ) );
|
|
|
|
IF_FAILED_BOOL_THROW( ::CertAddCertificateContextToStore( shStore.get(),
|
|
pContext,
|
|
bReuseCerts ?
|
|
CERT_STORE_ADD_USE_EXISTING :
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
&shResult ),
|
|
CBaseException( IDS_E_ADD_CERT_STORE ) );
|
|
return shResult;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Obtains a crypt key derived from the password
|
|
*/
|
|
const TCryptKeyHandle CTools::GetCryptKeyFromPwd( HCRYPTPROV hCryptProv, LPCWSTR wszPassword )
|
|
{
|
|
_ASSERT( hCryptProv != NULL );
|
|
_ASSERT( wszPassword != NULL );
|
|
|
|
TCryptKeyHandle shKey;
|
|
TCryptHashHandle shHash;
|
|
|
|
IF_FAILED_BOOL_THROW( ::CryptCreateHash( hCryptProv,
|
|
CALG_MD5,
|
|
NULL,
|
|
0,
|
|
&shHash ),
|
|
CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
|
|
|
|
// Add the password to the hash
|
|
IF_FAILED_BOOL_THROW( ::CryptHashData( shHash.get(),
|
|
( BYTE* )( wszPassword ),
|
|
static_cast<DWORD>( ::wcslen( wszPassword ) * sizeof( WCHAR ) ),
|
|
0 ),
|
|
CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
|
|
|
|
// Get a key derived from the password
|
|
// Stream cypher is used
|
|
IF_FAILED_BOOL_THROW( ::CryptDeriveKey( hCryptProv,
|
|
CALG_RC4,
|
|
shHash.get(),
|
|
0x00800000 | CRYPT_CREATE_SALT, // 128bit RC4 key
|
|
&shKey ),
|
|
CBaseException( IDS_E_CRYPT_KEY_OR_HASH ) );
|
|
|
|
return shKey;
|
|
}
|
|
|
|
|
|
|
|
std::wstring CTools::GetMachineName()
|
|
{
|
|
DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1;
|
|
WCHAR wszCompName[ MAX_COMPUTERNAME_LENGTH + 1 ];
|
|
VERIFY( ::GetComputerNameW( wszCompName, &dwLen ) );
|
|
|
|
return std::wstring( wszCompName );
|
|
}
|
|
|
|
|
|
|
|
ULONGLONG CTools::GetFilePtrPos( HANDLE hFile )
|
|
{
|
|
_ASSERT( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) );
|
|
|
|
LONG nHigh = 0;
|
|
DWORD dwLow = ::SetFilePointer( hFile, 0, &nHigh, FILE_CURRENT );
|
|
|
|
IF_FAILED_BOOL_THROW( ( dwLow != INVALID_SET_FILE_POINTER ) || ( ::GetLastError() == ERROR_SUCCESS ),
|
|
CBaseException( IDS_E_SEEK_PKG ) );
|
|
|
|
return ( ( nHigh << 32 ) | dwLow );
|
|
}
|
|
|
|
|
|
|
|
void CTools::SetFilePtrPos( HANDLE hFile, DWORDLONG nOffset )
|
|
{
|
|
_ASSERT( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) );
|
|
|
|
LONG nHigh = static_cast<LONG>( nOffset >> 32 );
|
|
DWORD dwLow = ::SetFilePointer( hFile,
|
|
static_cast<LONG>( nOffset & ULONG_MAX ),
|
|
&nHigh,
|
|
FILE_BEGIN );
|
|
|
|
IF_FAILED_BOOL_THROW( ( dwLow != INVALID_SET_FILE_POINTER ) || ( ::GetLastError() == ERROR_SUCCESS ),
|
|
CBaseException( IDS_E_SEEK_PKG ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CFindFile implementation
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
CFindFile::CFindFile()
|
|
{
|
|
m_wOptions = 0;
|
|
}
|
|
|
|
|
|
|
|
CFindFile::~CFindFile()
|
|
{
|
|
try
|
|
{
|
|
Close();
|
|
}
|
|
catch(...)
|
|
{
|
|
if ( !std::uncaught_exception() )
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool CFindFile::FindFirst( LPCWSTR wszDirToScan,
|
|
WORD wOptions,
|
|
LPWSTR wszFileDir,
|
|
WIN32_FIND_DATAW* pData )
|
|
{
|
|
_ASSERT( wszDirToScan != NULL );
|
|
_ASSERT( ::PathIsDirectory( wszDirToScan ) );
|
|
_ASSERT( !m_shSearch.IsValid() ); // Call Close() first
|
|
|
|
// Must search for at least files or dirs
|
|
_ASSERT( ( wOptions & ffGetFiles ) || ( wOptions & ffGetDirs ) );
|
|
|
|
m_wOptions = wOptions;
|
|
bool bFound = false;
|
|
|
|
// Push the root dir into the list
|
|
m_DirsToScan.push_front( std::wstring( L"\\" ) );
|
|
|
|
// Set the search path
|
|
m_strRootDir = wszDirToScan;
|
|
|
|
// Get the first match ( if any )
|
|
bFound = Next( NULL, wszFileDir, pData );
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
/*
|
|
Gets the next file from e previously opened search
|
|
|
|
*pbDirchanged is set to TRUE when the file is found, but not in the last dir scanned
|
|
For example if the search was opened in the Dir "c:\\", the first time a file from "c:\\Temp"
|
|
is returned, *pbDirChagned will be true
|
|
|
|
wszDir will hold the dir where the object was found, relative to the search root dir
|
|
For example if the search was opened in "c:\\", and a matching object was found in c:\\Temp" -
|
|
wszDir will be "Temp"
|
|
If ffAbsolutePaths is specified, the wszDir will be absolute ( includeing the name of the root dir )
|
|
|
|
pData will be filled with the info about the match found
|
|
*/
|
|
bool CFindFile::Next( bool* pbDirChanged,
|
|
LPWSTR wszDir,
|
|
WIN32_FIND_DATAW* pData )
|
|
{
|
|
WCHAR wszBuffer[ MAX_PATH + 1 ];
|
|
bool bDirChanged = true;
|
|
bool bFound = false;
|
|
|
|
WIN32_FIND_DATAW fd = { 0 };
|
|
|
|
// Try to find a match in the current search
|
|
if ( m_shSearch.IsValid() )
|
|
{
|
|
bFound = ContinueCurrent( /*r*/fd );
|
|
bDirChanged = !bFound; // No file was found in the current dir - new dir will be scanned
|
|
}
|
|
|
|
// If nothing found - try to find something in the rest of the subdirs
|
|
while( !bFound && !m_DirsToScan.empty() )
|
|
{
|
|
// Get a dir from the list with pending dirs
|
|
const std::wstring strCurrentDir = m_DirsToScan.front();
|
|
m_DirsToScan.pop_front();
|
|
|
|
// Create the full path to the current dir
|
|
CDirTools::PathAppendLocal( wszBuffer, m_strRootDir.c_str(), strCurrentDir.c_str() );
|
|
|
|
bFound = ScanDir( wszBuffer, strCurrentDir.c_str(), /*r*/fd );
|
|
};
|
|
|
|
if ( bFound )
|
|
{
|
|
// Set the dir where the file was found
|
|
// It will be absolute or relative to the search root
|
|
if ( wszDir != NULL )
|
|
{
|
|
CDirTools::PathAppendLocal( wszDir,
|
|
ffAbsolutePaths & m_wOptions ? m_strRootDir.c_str() : L"",
|
|
m_strCurrentDir.c_str() );
|
|
|
|
// Add the filename if needed
|
|
if ( ffAddFilename & m_wOptions )
|
|
{
|
|
CDirTools::PathAppendLocal( wszDir, wszDir, fd.cFileName );
|
|
}
|
|
}
|
|
|
|
if ( pbDirChanged != NULL )
|
|
{
|
|
*pbDirChanged = ( bFound ? bDirChanged : false );
|
|
}
|
|
|
|
if ( pData != NULL )
|
|
{
|
|
*pData = fd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( wszDir != NULL )
|
|
{
|
|
wszDir[ 0 ] = L'\0';
|
|
}
|
|
|
|
// Close the search if no more matches
|
|
Close();
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
|
|
void CFindFile::Close()
|
|
{
|
|
m_wOptions = 0;
|
|
m_shSearch.Close();
|
|
m_DirsToScan.clear();
|
|
m_strRootDir.erase();
|
|
m_strCurrentDir.erase();
|
|
}
|
|
|
|
|
|
/*
|
|
Scans only the wszDir for appropriate result
|
|
*/
|
|
bool CFindFile::ScanDir( LPCWSTR wszDirToScan, LPCWSTR wszRelativeDir, WIN32_FIND_DATAW& FileData )
|
|
{
|
|
_ASSERT( wszDirToScan != NULL );
|
|
_ASSERT( wszRelativeDir != NULL );
|
|
|
|
WCHAR wszBuffer[ MAX_PATH + 1 ];
|
|
WIN32_FIND_DATAW fd = { 0 };
|
|
|
|
::ZeroMemory( &FileData, sizeof( FileData ) );
|
|
|
|
// Build the search string
|
|
CDirTools::PathAppendLocal( wszBuffer, wszDirToScan, L"*.*" );
|
|
|
|
m_shSearch = ::FindFirstFileW( wszBuffer, &fd );
|
|
|
|
if ( !m_shSearch.IsValid() ) throw CObjectException( IDS_E_ENUM_FILES, wszDirToScan );
|
|
|
|
bool bFileFound = false;
|
|
|
|
// Find the first file/dir to return
|
|
do
|
|
{
|
|
bFileFound = CheckFile( wszRelativeDir, fd );
|
|
|
|
}while( !bFileFound && ::FindNextFileW( m_shSearch.get(), &fd ) );
|
|
|
|
// Close the search if a file was not found - we don't need it anymore
|
|
if ( !bFileFound )
|
|
{
|
|
m_shSearch.Close();
|
|
m_strCurrentDir.erase();
|
|
}
|
|
else
|
|
{
|
|
m_strCurrentDir = wszRelativeDir;
|
|
}
|
|
|
|
FileData = fd;
|
|
|
|
return bFileFound;
|
|
}
|
|
|
|
|
|
/*
|
|
Check if data in 'fd' is a match for us
|
|
If the file is a dir, it will be added to the pending dirs
|
|
Returns True if the file is OK. or False if search must continue
|
|
*/
|
|
bool CFindFile::CheckFile( LPCWSTR wszRelativeDir, const WIN32_FIND_DATAW& fd )
|
|
{
|
|
WCHAR wszBuffer[ MAX_PATH + 1 ];
|
|
bool bFileFound = false;
|
|
|
|
// Current file is dir
|
|
if ( ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
|
|
{
|
|
// Every dir contains at least two dirs - "." and "..". Skip them
|
|
if ( fd.cFileName[ 0 ] != L'.' )
|
|
{
|
|
// If we do a recursive search - add the dir name to the relative path
|
|
// and push it in the list. This dir will be scaned later for files/dirs
|
|
if ( m_wOptions & ffRecursive )
|
|
{
|
|
CDirTools::PathAppendLocal( wszBuffer, wszRelativeDir, fd.cFileName );
|
|
m_DirsToScan.push_back( std::wstring( wszBuffer ) );
|
|
}
|
|
|
|
// If we are searching for dirs - we have the result
|
|
bFileFound = ( m_wOptions & ffGetDirs ) != 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// File is found.
|
|
// If we are searching for files - we have the result
|
|
bFileFound = ( m_wOptions & ffGetFiles ) != 0;
|
|
}
|
|
|
|
return bFileFound;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Try to find a match from the current m_shSearch.
|
|
Return True if there is a match. False otherwise
|
|
*/
|
|
bool CFindFile::ContinueCurrent( WIN32_FIND_DATAW& FileData )
|
|
{
|
|
_ASSERT( m_shSearch.IsValid() );
|
|
|
|
bool bFileFound = false;
|
|
|
|
while( !bFileFound && ::FindNextFileW( m_shSearch.get(), &FileData ) )
|
|
{
|
|
bFileFound = CheckFile( m_strCurrentDir.c_str(), FileData );
|
|
};
|
|
|
|
// Close the current search handle. Not needed anymore
|
|
if ( !bFileFound )
|
|
{
|
|
m_shSearch.Close();
|
|
m_strCurrentDir.erase();
|
|
}
|
|
|
|
return bFileFound;
|
|
}
|
|
|
|
|
|
|
|
|
|
// CXMLTools implementation
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
IXMLDOMElementPtr CXMLTools::AddTextNode( const IXMLDOMDocumentPtr& spDoc,
|
|
const IXMLDOMElementPtr& spEl,
|
|
LPCWSTR wszNodeName,
|
|
LPCWSTR wszValue )
|
|
{
|
|
IXMLDOMElementPtr spNode;
|
|
IXMLDOMTextPtr spText;
|
|
_bstr_t bstrName( wszNodeName );
|
|
_bstr_t bstrValue( wszValue );
|
|
|
|
IF_FAILED_HR_THROW( spDoc->createElement( bstrName, &spNode ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
|
|
IF_FAILED_HR_THROW( spDoc->createTextNode( bstrValue, &spText ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
|
|
IF_FAILED_HR_THROW( spNode->appendChild( spText, NULL ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
|
|
IF_FAILED_HR_THROW( spEl->appendChild( spNode, NULL ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
|
|
return spNode;
|
|
}
|
|
|
|
|
|
|
|
const std::wstring CXMLTools::GetAttrib( const IXMLDOMNodePtr& spElement, LPCWSTR wszName )
|
|
{
|
|
_ASSERT( spElement != NULL );
|
|
_ASSERT( wszName != NULL );
|
|
|
|
IXMLDOMNamedNodeMapPtr spAttribs;
|
|
IXMLDOMNodePtr spNode;
|
|
CComBSTR bstrRes;
|
|
|
|
// Get the attribs collection
|
|
IF_FAILED_HR_THROW( spElement->get_attributes( &spAttribs ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
// Get the attrib of interest
|
|
// This succeeds even if the item is not found
|
|
IF_FAILED_HR_THROW( spAttribs->getNamedItem( _bstr_t( wszName ), &spNode ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
if ( spNode == NULL ) throw CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND );
|
|
|
|
// Get the value
|
|
IF_FAILED_HR_THROW( spNode->get_text( &bstrRes ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
return std::wstring( bstrRes );
|
|
}
|
|
|
|
|
|
void CXMLTools::LoadXMLFile( LPCWSTR wszFile, IXMLDOMDocumentPtr& rspDoc )
|
|
{
|
|
_variant_t vntFile = wszFile;
|
|
VARIANT_BOOL vntRes = VARIANT_FALSE;
|
|
IXMLDOMDocumentPtr spDoc;
|
|
|
|
// Create doc instance
|
|
IF_FAILED_HR_THROW( spDoc.CreateInstance( CLSID_DOMDocument ),
|
|
CBaseException( IDS_E_NO_XML_PARSER ) );
|
|
|
|
// Retursn success always
|
|
VERIFY( SUCCEEDED( spDoc->load( vntFile, &vntRes ) ) );
|
|
|
|
if ( vntRes != VARIANT_TRUE )
|
|
{
|
|
throw CObjectException( IDS_E_READFILE, wszFile );
|
|
}
|
|
|
|
rspDoc.Attach( spDoc.Detach() );
|
|
}
|
|
|
|
|
|
/*
|
|
Removes all nodes that match the specified XPath query
|
|
*/
|
|
void CXMLTools::RemoveNodes( const IXMLDOMElementPtr& spContext, LPCWSTR wszXPath )
|
|
{
|
|
IXMLDOMNodeListPtr spList;
|
|
IXMLDOMNodePtr spNode;
|
|
|
|
IF_FAILED_HR_THROW( spContext->selectNodes( _bstr_t( wszXPath ), &spList ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
while( S_OK == spList->nextNode( &spNode ) )
|
|
{
|
|
IXMLDOMNodePtr spParent;
|
|
|
|
IF_FAILED_HR_THROW( spNode->get_parentNode( &spParent ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
|
|
IF_FAILED_HR_THROW( spParent->removeChild( spNode, NULL ),
|
|
CBaseException( IDS_E_XML_GENERATE ) );
|
|
};
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Get's a data from an XML doc
|
|
If an attrib name is specified - the attrib value is returned. Otherwise - the element's text
|
|
The data is located with an XPath query. It is an error fo this query to return more then 1 node
|
|
If the data is missing - the default value is used. If no default value - it's an error for the data to be missing
|
|
*/
|
|
const std::wstring CXMLTools::GetDataValue( const IXMLDOMNodePtr& spRoot,
|
|
LPCWSTR wszQuery,
|
|
LPCWSTR wszAttrib,
|
|
LPCWSTR wszDefault )
|
|
{
|
|
_ASSERT( wszQuery != NULL );
|
|
_ASSERT( spRoot != NULL );
|
|
|
|
IXMLDOMNodeListPtr spList;
|
|
IXMLDOMNodePtr spDataEl;
|
|
std::wstring strRes( wszDefault != NULL ? wszDefault : L"" );
|
|
|
|
// Get the node
|
|
IF_FAILED_HR_THROW( spRoot->selectNodes( _bstr_t( wszQuery ), &spList ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
long nCount = 0;
|
|
if ( FAILED( spList->get_length( &nCount ) ) || ( nCount > 1 ) )
|
|
{
|
|
throw CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA );
|
|
}
|
|
|
|
if ( 0 == nCount )
|
|
{
|
|
// The data is missing and no default was provided - error
|
|
IF_FAILED_BOOL_THROW( wszDefault != NULL, CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
|
|
}
|
|
else
|
|
{
|
|
VERIFY( S_OK == spList->nextNode( &spDataEl ) );
|
|
|
|
if ( wszAttrib != NULL )
|
|
{
|
|
strRes = CXMLTools::GetAttrib( spDataEl, wszAttrib );
|
|
}
|
|
else
|
|
{
|
|
CComBSTR bstrData;
|
|
IF_FAILED_HR_THROW( spDataEl->get_text( &bstrData ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
strRes = bstrData;
|
|
}
|
|
}
|
|
|
|
return strRes;
|
|
}
|
|
|
|
|
|
|
|
const std::wstring CXMLTools::GetDataValueAbs( const IXMLDOMDocumentPtr& spDoc,
|
|
LPCWSTR wszQuery,
|
|
LPCWSTR wszAttrib,
|
|
LPCWSTR wszDefault )
|
|
{
|
|
IXMLDOMElementPtr spRoot;
|
|
|
|
IF_FAILED_HR_THROW( spDoc->get_documentElement( &spRoot ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
return GetDataValue( spRoot, wszQuery, wszAttrib, wszDefault );
|
|
}
|
|
|
|
|
|
/*
|
|
Changes a element value or element's attrib value
|
|
The element is located with the wszQuery XPath
|
|
wszAttrib is the name of the attrib to change. If NULL - the element value is changed
|
|
The new value is wszNewValue
|
|
If the element cannot be find, a new child element is added to spRoot with tha wszNeElName name
|
|
and the data is set either is the element text or as an attrib ( depending on wszAttrib value )
|
|
*/
|
|
const IXMLDOMNodePtr CXMLTools::SetDataValue( const IXMLDOMNodePtr& spRoot,
|
|
LPCWSTR wszQuery,
|
|
LPCWSTR wszAttrib,
|
|
LPCWSTR wszNewValue,
|
|
LPCWSTR wszNewElName /*=NULL*/ )
|
|
{
|
|
_ASSERT( wszQuery != NULL );
|
|
_ASSERT( spRoot != NULL );
|
|
|
|
IXMLDOMNodeListPtr spList;
|
|
IXMLDOMNodePtr spDataEl;
|
|
|
|
// Get the node
|
|
IF_FAILED_HR_THROW( spRoot->selectNodes( _bstr_t( wszQuery ), &spList ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
long nCount = 0;
|
|
IF_FAILED_BOOL_THROW( SUCCEEDED( spList->get_length( &nCount ) ) && ( 1 == nCount ),
|
|
CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA ) );
|
|
|
|
// If the value is not already here and a name is provided - add it
|
|
if ( S_FALSE == spList->nextNode( &spDataEl ) )
|
|
{
|
|
IF_FAILED_BOOL_THROW( wszNewElName != NULL,
|
|
CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
|
|
IXMLDOMDocumentPtr spDoc;
|
|
IF_FAILED_HR_THROW( spRoot->get_ownerDocument( &spDoc ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
|
|
spDataEl = CreateSubNode( spDoc, spRoot, wszNewElName );
|
|
}
|
|
|
|
if ( wszAttrib != NULL )
|
|
{
|
|
CXMLTools::SetAttrib( spDataEl, wszAttrib, wszNewValue );
|
|
}
|
|
else
|
|
{
|
|
IF_FAILED_HR_THROW( spDataEl->put_text( _bstr_t( wszNewValue ) ),
|
|
CBaseException( IDS_E_XML_PARSE ) );
|
|
}
|
|
|
|
return spDataEl;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Convert class implementation
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
const std::wstring Convert::ToString( const BYTE* pvData, DWORD dwSize )
|
|
{
|
|
// Each byte takes 2 symbols. And we need one symbol for the '\0'
|
|
std::wstring str( ( dwSize * 2 ) + 1, L' ' );
|
|
|
|
for ( UINT x = 0, y = 0 ; x < dwSize ; ++x )
|
|
{
|
|
str[ y++ ] = ByteToWChar( pvData[ x ] >> 4 );
|
|
str[ y++ ] = ByteToWChar( pvData[ x ] & 0x0f );
|
|
}
|
|
str[ y ] = L'\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
|
|
void Convert::ToBLOB( LPCWSTR wszData, TByteAutoPtr& rspData, DWORD& rdwDataSize )
|
|
{
|
|
_ASSERT( wszData != NULL );
|
|
_ASSERT( ::wcscspn( wszData, L"ABCDEF" ) == ::wcslen( wszData ) ); // The string must be lower-case!!!
|
|
_ASSERT( ( ::wcslen( wszData ) % 2 ) == 0 );
|
|
|
|
// Calc the size
|
|
DWORD dwSize = static_cast<DWORD>( ::wcslen( wszData ) / 2 ); // Each byte takes 2 symbols
|
|
// Alloc the buffer
|
|
TByteAutoPtr spData( new BYTE[ dwSize ] );
|
|
|
|
BYTE* pbtDest = spData.get();
|
|
|
|
DWORD dwSymbol = 0;
|
|
DWORD dwByte = 0;
|
|
|
|
while( wszData[ dwSymbol ] != L'\0' )
|
|
{
|
|
pbtDest[ dwByte ] = WCharsToByte( wszData[ dwSymbol + 1 ], wszData[ dwSymbol ] );
|
|
++dwByte;
|
|
dwSymbol += 2;
|
|
};
|
|
|
|
rspData = spData;
|
|
rdwDataSize = dwSize;
|
|
}
|
|
|
|
|
|
DWORD Convert::ToDWORD( LPCWSTR wszData )
|
|
{
|
|
int nRes = 0;
|
|
|
|
IF_FAILED_BOOL_THROW( ::StrToIntExW( wszData, STIF_DEFAULT, &nRes ),
|
|
CBaseException( IDS_E_INVALIDARG, ERROR_INVALID_DATA ) );
|
|
return static_cast<DWORD>( nRes );
|
|
}
|