/******************************************************************************

Copyright (c) 2000 Microsoft Corporation

Module Name:
    utils.cpp

Abstract:
    This file contains the implementation of various utility functions.

Revision History:
    Davide Massarenti   (Dmassare)  03/14/2000
        created

******************************************************************************/

#include "stdafx.h"

////////////////////////////////////////////////////////////////////////////////

static const WCHAR c_szDataFiles_Pattern[] = L"pchdt_*.ca?";

////////////////////////////////////////////////////////////////////////////////

HRESULT SVC::OpenStreamForRead( /*[in]*/  LPCWSTR   szFile           ,
                                /*[out]*/ IStream* *pVal             ,
                                /*[in]*/  bool      fDeleteOnRelease )
{
    __HCP_FUNC_ENTRY( "SVC::OpenStreamForRead" );

    HRESULT                  hr;
    CComPtr<MPC::FileStream> stream;
    MPC::wstring             strFileFull;

    __MPC_PARAMCHECK_BEGIN(hr)
        __MPC_PARAMCHECK_STRING_NOT_EMPTY(szFile);
        __MPC_PARAMCHECK_POINTER_AND_SET(pVal,NULL);
    __MPC_PARAMCHECK_END();


    MPC::SubstituteEnvVariables( strFileFull = szFile );


    //
    // Create a stream for a file.
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));

    __MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForRead    ( strFileFull.c_str() ));
    __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease( fDeleteOnRelease    ));


    //
    // Return the stream to the caller.
    //
    *pVal = stream.Detach();
    hr    = S_OK;


    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}

HRESULT SVC::OpenStreamForWrite( /*[in]*/  LPCWSTR   szFile           ,
                                 /*[out]*/ IStream* *pVal             ,
                                 /*[in]*/  bool      fDeleteOnRelease )
{
    __HCP_FUNC_ENTRY( "SVC::OpenStreamForWrite" );

    HRESULT                  hr;
    CComPtr<MPC::FileStream> stream;
    MPC::wstring             strFileFull;

    __MPC_PARAMCHECK_BEGIN(hr)
        __MPC_PARAMCHECK_STRING_NOT_EMPTY(szFile);
        __MPC_PARAMCHECK_POINTER_AND_SET(pVal,NULL);
    __MPC_PARAMCHECK_END();


    MPC::SubstituteEnvVariables( strFileFull = szFile );


    //
    // Create a stream for a file.
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));

    __MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForWrite   ( strFileFull.c_str() ));
    __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease( fDeleteOnRelease    ));


    //
    // Return the stream to the caller.
    //
    *pVal = stream.Detach();
    hr    = S_OK;


    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

HRESULT SVC::CopyFileWhileImpersonating( /*[in]*/ LPCWSTR             szSrc                 ,
                                         /*[in]*/ LPCWSTR             szDst                 ,
                                         /*[in]*/ MPC::Impersonation& imp                   ,
                                         /*[in]*/ bool                fImpersonateForSource )
{
    __HCP_FUNC_ENTRY( "SVC::CopyFileWhileImpersonating" );

    HRESULT          hr;
    CComPtr<IStream> streamSrc;
    CComPtr<IStream> streamDst;


	if(fImpersonateForSource == true) __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Impersonate());

    __MPC_EXIT_IF_METHOD_FAILS(hr, SVC::OpenStreamForRead( szSrc, &streamSrc ));

	if(fImpersonateForSource == true) __MPC_EXIT_IF_METHOD_FAILS(hr, imp.RevertToSelf());

	////////////////////

	if(fImpersonateForSource == false) __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Impersonate());

    __MPC_EXIT_IF_METHOD_FAILS(hr, SVC::OpenStreamForWrite( szDst, &streamDst ));

	if(fImpersonateForSource == false) __MPC_EXIT_IF_METHOD_FAILS(hr, imp.RevertToSelf());

	////////////////////

    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::BaseStream::TransferData( streamSrc, streamDst ));

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    (void)imp.RevertToSelf();

    __HCP_FUNC_EXIT(hr);
}

HRESULT SVC::CopyOrExtractFileWhileImpersonating( /*[in]*/ LPCWSTR             szSrc ,
												  /*[in]*/ LPCWSTR             szDst ,
												  /*[in]*/ MPC::Impersonation& imp   )
{
    __HCP_FUNC_ENTRY( "SVC::CopyOrExtractFileWhileImpersonating" );

    HRESULT      hr;
    MPC::wstring strTempFile;


    //
    // First of all, try to simply copy the file.
    //
    if(FAILED(hr = CopyFileWhileImpersonating( szSrc, szDst, imp )))
    {
        int iLen = wcslen( szSrc );

        if(hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || iLen < 1)
        {
            __MPC_FUNC_LEAVE;
        }
        else
        {
            MPC::wstring strSrc2( szSrc ); strSrc2[iLen-1] = '_';
			LPCWSTR      szSrc3;

            //
            // Simple copy failed, let's try to copy the same file, with the last character changed to an underscore.
            //
            __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetTemporaryFileName( strTempFile ));

			__MPC_EXIT_IF_METHOD_FAILS(hr, CopyFileWhileImpersonating( strSrc2.c_str(), strTempFile.c_str(), imp ));

            //
            // Success, so it should be a cabinet, extract the real file.
            //
			szSrc3 = wcsrchr( szSrc, '\\' );
            __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::DecompressFromCabinet( strTempFile.c_str(), szDst, szSrc3 ? szSrc3+1 : szSrc ));
        }
    }

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    (void)MPC::RemoveTemporaryFile( strTempFile );

    __HCP_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

HRESULT SVC::LocateDataArchive( /*[in ]*/ LPCWSTR           szDir ,
								/*[out]*/ MPC::WStringList& lst   )
{
    __HCP_FUNC_ENTRY( "SVC::LocateDataArchive" );

    HRESULT                          hr;
    MPC::wstring                     strName;
    MPC::wstring                     strInput( szDir           ); MPC::SubstituteEnvVariables( strInput );
    MPC::FileSystemObject            fso     ( strInput.c_str() );
    MPC::FileSystemObject::List      fso_lst;
    MPC::FileSystemObject::IterConst fso_it;


    //
    // Locate the "pchdt_<XX>.ca?" file.
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, fso.Scan( false, true, c_szDataFiles_Pattern ));

    __MPC_EXIT_IF_METHOD_FAILS(hr, fso.EnumerateFiles( fso_lst ));
    for(fso_it=fso_lst.begin(); fso_it != fso_lst.end(); fso_it++)
    {
		MPC::wstring& strDataArchive = *(lst.insert( lst.end() ));
		int           iLen;

        __MPC_EXIT_IF_METHOD_FAILS(hr, (*fso_it)->get_Path( strDataArchive ));

		//
		// If it's a compressed file from the CD, return the real name.
		//
		iLen = strDataArchive.size();
		if(iLen && strDataArchive[iLen-1] == '_')
		{
			strDataArchive[iLen-1] = 'b';
		}
	}

    hr = S_OK;


	__HCP_FUNC_CLEANUP;

	__HCP_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

HRESULT SVC::RemoveAndRecreateDirectory( /*[in]*/ const MPC::wstring& strDir, /*[in]*/ LPCWSTR szExtra, /*[in]*/ bool fRemove, /*[in]*/ bool fRecreate )
{
	return RemoveAndRecreateDirectory( strDir.c_str(), szExtra, fRemove, fRecreate );
}

HRESULT SVC::RemoveAndRecreateDirectory( /*[in]*/ LPCWSTR szDir, /*[in]*/ LPCWSTR szExtra, /*[in]*/ bool fRemove, /*[in]*/ bool fRecreate )
{
	HRESULT      hr;
    MPC::wstring strPath( szDir ); if(szExtra) strPath.append( szExtra );

	if(SUCCEEDED(hr = MPC::SubstituteEnvVariables( strPath )))
	{
		MPC::FileSystemObject fso( strPath.c_str() );

		if(fRemove)
		{
			hr = fso.Delete( true, false );
		}

		if(SUCCEEDED(hr))
		{
			if(fRecreate)
			{
				hr = fso.CreateDir( /*fForce*/true );
			}
		}
	}

	return hr;
}

HRESULT SVC::ChangeSD( /*[in]*/ MPC::SecurityDescriptor& sdd     ,
					   /*[in]*/ MPC::wstring             strPath ,
					   /*[in]*/ LPCWSTR                  szExtra )
{
    __HCP_FUNC_ENTRY( "SVC::ChangeSD" );

    HRESULT hr;

    if(szExtra) strPath.append( szExtra );

    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SubstituteEnvVariables( strPath ));

    {
        MPC::FileSystemObject fso( strPath.c_str() );

        __MPC_EXIT_IF_METHOD_FAILS(hr, fso.CreateDir( true ));

        __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::SetFileSecurityW( strPath.c_str()                     ,
                                                                 GROUP_SECURITY_INFORMATION          |
                                                                 DACL_SECURITY_INFORMATION           |
                                                                 PROTECTED_DACL_SECURITY_INFORMATION ,
                                                                 sdd.GetSD()                         ));
    }

    hr = S_OK;


    __MPC_FUNC_CLEANUP;

    __MPC_FUNC_EXIT(hr);
}


////////////////////////////////////////////////////////////////////////////////

static HRESULT local_ReadWithRetry( /*[in]*/ const MPC::wstring& strFile   ,
									/*[in]*/ MPC::FileStream*    stream    ,
									/*[in]*/ DWORD               dwTimeout ,
									/*[in]*/ DWORD               dwRetries )
{
	HRESULT hr;

	while(1)
	{
		if(SUCCEEDED(hr = stream->InitForRead( strFile.c_str() ))) return S_OK;

		if(hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED    ) ||
		   hr == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) ||
		   hr == HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION   )  )
		{
			(void)stream->Close();

			if(dwRetries)
			{
				::Sleep( dwTimeout );

				dwRetries--;
				continue;
			}
		}

		break;
	}

	return hr;
}

static HRESULT local_WriteWithRetry( /*[in]*/ const MPC::wstring& strFile   ,
									 /*[in]*/ MPC::FileStream*    stream    ,
									 /*[in]*/ DWORD               dwTimeout ,
									 /*[in]*/ DWORD               dwRetries )
{
	HRESULT hr;

	while(1)
	{
		if(SUCCEEDED(hr = stream->InitForWrite( strFile.c_str() ))) return S_OK;

		if(hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED    ) ||
		   hr == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) ||
		   hr == HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION   )  )
		{
			(void)stream->Close();

			if(dwRetries)
			{
				::Sleep( dwTimeout );

				dwRetries--;
				continue;
			}
		}

		break;
	}

	return hr;
}

HRESULT SVC::SafeLoad( /*[in]*/ const MPC::wstring& 	  strFile   ,
					   /*[in]*/ CComPtr<MPC::FileStream>& stream    ,
					   /*[in]*/ DWORD                     dwTimeout ,
					   /*[in]*/ DWORD                     dwRetries )
{
	__HCP_FUNC_ENTRY( "SVC::SafeLoad" );

	HRESULT hr;


	__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));

	//
	// If fails, try to load "<file>.orig"
	//
	if(FAILED(hr = local_ReadWithRetry( strFile, stream, dwTimeout, dwRetries )))
	{
		MPC::wstring strFileOrig( strFile ); strFileOrig += L".orig";

		__MPC_EXIT_IF_METHOD_FAILS(hr, local_ReadWithRetry( strFileOrig, stream, dwTimeout, dwRetries ));
	}

	hr = S_OK;


	__HCP_FUNC_CLEANUP;

	__HCP_FUNC_EXIT(hr);
}

HRESULT SVC::SafeSave_Init( /*[in]*/ const MPC::wstring& 	   strFile   ,
							/*[in]*/ CComPtr<MPC::FileStream>& stream    ,
							/*[in]*/ DWORD                     dwTimeout ,
							/*[in]*/ DWORD                     dwRetries )
{
	__HCP_FUNC_ENTRY( "SVC::SafeSave_Init" );

	HRESULT      hr;
	MPC::wstring strFileNew( strFile ); strFileNew += L".new";

	__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MakeDir        (  strFileNew ));
	__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance ( &stream     ));

	__MPC_EXIT_IF_METHOD_FAILS(hr, local_WriteWithRetry( strFileNew, stream, dwTimeout, dwRetries ));

	hr = S_OK;


	__HCP_FUNC_CLEANUP;

	__HCP_FUNC_EXIT(hr);
}

HRESULT SVC::SafeSave_Finalize( /*[in]*/ const MPC::wstring&  	   strFile   ,
								/*[in]*/ CComPtr<MPC::FileStream>& stream    ,
								/*[in]*/ DWORD                     dwTimeout ,
								/*[in]*/ DWORD                     dwRetries )
{
	__HCP_FUNC_ENTRY( "SVC::SafeSave_Finalize" );

	HRESULT      hr;
	MPC::wstring strFileNew ( strFile ); strFileNew  += L".new";
	MPC::wstring strFileOrig( strFile ); strFileOrig += L".orig";


	stream.Release();

    //
    // Then move "<file>" to "<file>.orig"
    //
	(void)MPC::DeleteFile(          strFileOrig );
	(void)MPC::MoveFile  ( strFile, strFileOrig );

	while(1)
	{
		//
		// Then rename "<file>.new" to "<file>"
		//
		if(SUCCEEDED(hr = MPC::MoveFile( strFileNew, strFile ))) break;

		if(hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED    ) ||
		   hr == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) ||
		   hr == HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION   )  )
		{
			if(dwRetries)
			{
				::Sleep( dwTimeout );

				dwRetries--;
				continue;
			}
		}

		__MPC_FUNC_LEAVE;
	}

    //
    // Finally delete "<file>.orig"
    //
	(void)MPC::DeleteFile( strFileOrig );

	hr = S_OK;


	__HCP_FUNC_CLEANUP;

	__HCP_FUNC_EXIT(hr);
}