/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    CTeeStream.cpp

Abstract:

    See CTeeStream.h.

Author:

    Jay Krell (a-JayK) May 2000

Revision History:

--*/
#include "stdinc.h"
#include "CTeeStream.h"
#include "Sxsp.h"
#include "SxsExceptionHandling.h"

CTeeStream::~CTeeStream()
{
    FN_TRACE();
    CSxsPreserveLastError ple;

    ASSERT(m_cRef == 0);
    m_streamSource.Release();

    if (!m_fileSink.Win32Close())
    {
        ::FusionpDbgPrintEx(
            FUSION_DBG_LEVEL_ERROR,
            "SXS.DLL: %s(): m_fileSink.Close(%ls) failed: %ld\n",
            __FUNCTION__,
            static_cast<PCWSTR>(m_bufferSinkPath),
            ::FusionpGetLastWin32Error());
    }

    if (FAILED(m_hresult))
    {
        ::FusionpDbgPrintEx(
            FUSION_DBG_LEVEL_INFO,
            "SXS.DLL: %s():deleting %ls\n",
            __FUNCTION__,
            static_cast<PCWSTR>(m_bufferSinkPath));

        if (!::DeleteFileW(m_bufferSinkPath))
        {
            FusionpDbgPrintEx(
                FUSION_DBG_LEVEL_ERROR,
                "SXS.DLL: %s():DeleteFileW(%ls) failed:%ld\n",
                __FUNCTION__,
                static_cast<PCWSTR>(m_bufferSinkPath),
                ::FusionpGetLastWin32Error());
        }
    }

    ple.Restore();
}

VOID
CTeeStream::SetSource(IStream *streamSource)
{
    FN_TRACE();

    m_streamSource = streamSource;
}

BOOL
CTeeStream::SetSink(
    const CImpersonationData &ImpersonationData,
    const CBaseStringBuffer &rbuff,
    DWORD openOrCreate
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    DWORD dwBytesWritten = 0;
    DWORD dwBufferSize = 0;
    BOOL fFailForCreateFile = FALSE;

    IFCOMFAILED_EXIT(m_hresult);

    IFW32FALSE_EXIT(m_bufferSinkPath.Win32Assign(rbuff));

    m_ImpersonationData = ImpersonationData;

    {
        CImpersonate impersonate(ImpersonationData);
        IFW32FALSE_EXIT(impersonate.Impersonate());
        IFW32FALSE_EXIT_UNLESS(m_fileSink.Win32CreateFile(m_bufferSinkPath, GENERIC_WRITE, 0/*share*/, openOrCreate),
            ::FusionpGetLastWin32Error() == ERROR_FILE_EXISTS,
            fFailForCreateFile);
        if (fFailForCreateFile)  // the file has existed, have to reopen in order do not break
        {
            ::FusionpDbgPrintEx(
                FUSION_DBG_LEVEL_ERROR,
                "SXS.DLL: SOFT_VERIFY FAILURE : An Existing manifest is tried to be opened for write again, file a BUG!\n");

            IFW32FALSE_EXIT(m_fileSink.Win32CreateFile(m_bufferSinkPath, GENERIC_WRITE, 0/*share*/, CREATE_ALWAYS));
        }


        IFW32FALSE_EXIT(impersonate.Unimpersonate());
    }

    dwBufferSize = static_cast<DWORD>(m_buffer.GetCurrentCb());
    fSuccess = TRUE;
    if (dwBufferSize > 0)
    {
        fSuccess = WriteFile(m_fileSink, m_buffer, dwBufferSize, &dwBytesWritten, NULL/*overlapped*/);
        DWORD dwLastError = fSuccess ? ERROR_SUCCESS : ::FusionpGetLastWin32Error();
        // I'm not entirely sure why we mask the lasterror of the write
        // if it "succeeded" in writing the wrong number of bytes, but
        // such as it is, this is a write fault (The system cannot write
        // to the specified device.)
        if (fSuccess && dwBytesWritten != dwBufferSize)
        {
            dwLastError = ERROR_WRITE_FAULT;
            fSuccess = FALSE;
        }

		m_fBuffer = FALSE;

		if (dwLastError != ERROR_SUCCESS)
			ORIGINATE_WIN32_FAILURE_AND_EXIT(WriteFile, dwLastError);
    }
    m_fBuffer = FALSE;
Exit:
    if (!fSuccess)
    {
        DWORD dwLastError = ::FusionpGetLastWin32Error();
        m_hresult = FusionpHresultFromLastError();
        m_buffer.Clear(true);
        ::FusionpSetLastWin32Error(dwLastError);
    }
    else
        m_buffer.Clear(true);
    return fSuccess;
}

BOOL
CTeeStream::Close()
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);

    IFCOMFAILED_EXIT(m_hresult);

    IFW32FALSE_EXIT(m_fileSink.Win32Close());

    // ? m_streamSource.Release();

    fSuccess = TRUE;
Exit:
    if (!fSuccess)
        m_hresult = FusionpHresultFromLastError();

    return fSuccess;
}

ULONG __stdcall
CTeeStream::AddRef()
{
    FN_TRACE();
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall
CTeeStream::Release()
{
    FN_TRACE();

    LONG cRef;
    if ((cRef = InterlockedDecrement(&m_cRef)) == 0)
    {
        /*delete this*/;
    }
    return cRef;
}

HRESULT __stdcall
CTeeStream::QueryInterface(
    REFIID  iid,
    PVOID *ppvObj
    )
{
    IUnknown *punk = NULL;
    IUnknown **ppunk = reinterpret_cast<IUnknown **>(ppvObj);
    *ppunk = NULL;
    if (false) { }
#define QI(i) else if (iid == __uuidof(i)) punk = static_cast<i*>(this);
    QI(IUnknown)
    QI(ISequentialStream)
    QI(IStream)
#undef QI
    else return E_NOINTERFACE;
    AddRef();
    *ppunk = punk;
    return NOERROR;
}

HRESULT __stdcall
CTeeStream::Read(PVOID pv, ULONG cb, ULONG *pcbRead)
{
    HRESULT hr;

    FN_TRACE_HR(hr);

    ULONG cbRead;

    if (pcbRead != NULL)
        *pcbRead = 0;

    IFCOMFAILED_ORIGINATE_AND_EXIT(m_hresult);
    IFCOMFAILED_EXIT(m_streamSource->Read(pv, cb, &cbRead));

    if (m_fBuffer)
    {
        IFCOMFAILED_EXIT(m_buffer.Append(reinterpret_cast<const BYTE*>(pv), cbRead));
    }
    else
    {
        DWORD dwBytesWritten = 0;
        BOOL fSuccess = (cbRead == 0) || ::WriteFile(m_fileSink, pv, cbRead, &dwBytesWritten, NULL/*overlapped*/);

        if (!fSuccess)
        {
			TRACE_WIN32_FAILURE_ORIGINATION(WriteFile);

            hr = ::FusionpHresultFromLastError();
            goto Exit;
        }
        else if (dwBytesWritten != cbRead)
        {
            hr = E_FAIL;
            goto Exit;
        }
    }

    if (pcbRead != NULL)
        *pcbRead = cbRead;

    hr = NOERROR;

Exit:
    if (FAILED(hr))
        m_hresult = hr;

    return hr;
}

HRESULT __stdcall
CTeeStream::Write(
    const VOID *pv,
    ULONG cb,
    ULONG *pcbWritten
    )
{
    /*
    since this stream is really only for reading..
    */
    if (pcbWritten != NULL)
        *pcbWritten = 0;

    return E_NOTIMPL;
}

// IStream methods:
HRESULT __stdcall
CTeeStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
    /*
    this messes up our ability to easily copy the stream, I think..
    */
    plibNewPosition->QuadPart = 0;
    return E_NOTIMPL;
}

HRESULT __stdcall
CTeeStream::SetSize(ULARGE_INTEGER libNewSize)
{
    /*
    this messes up our ability to easily copy the stream, I think..
    besides that, this is really a read only stream
    */
    return E_NOTIMPL;
}

HRESULT __stdcall
CTeeStream::CopyTo(
    IStream *pstm,
    ULARGE_INTEGER cb,
    ULARGE_INTEGER *pcbRead,
    ULARGE_INTEGER *pcbWritten)
{
    /*
    Implementing this requires getting the current seek pointer,
    call CopyTo
    seek back
    Read/Write
    seek forward
    because there is no buffer
    */
    pcbRead->QuadPart = 0;
    pcbWritten->QuadPart = 0;
    return E_NOTIMPL;
}

HRESULT __stdcall
CTeeStream::Commit(DWORD grfCommitFlags)
{
    /*
    since this stream is really only for reading..
    */
    return S_OK;
}

HRESULT __stdcall
CTeeStream::Revert()
{
    /*
    since this stream is really only for reading..
    */
    return S_OK;
}

HRESULT __stdcall
CTeeStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
    /*
    since this stream is really only for reading..
    */
    return S_OK;
}

HRESULT __stdcall
CTeeStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
    /*
    since this stream is really only for reading..
    */
    return S_OK;
}

HRESULT __stdcall
CTeeStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
    HRESULT hr = m_streamSource->Stat(pstatstg, grfStatFlag);
    return hr;
}

HRESULT __stdcall
CTeeStream::Clone(IStream **ppIStream)
{
    *ppIStream = NULL;
    return E_NOTIMPL;
}