//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       transdata.cxx
//
//  Contents:   Contains the module which provide data passed on in OnDataAvailable
//
//  Classes:
//
//  Functions:
//
//  History:    12-07-95   JohannP (Johann Posch)   Created
//
//----------------------------------------------------------------------------
#include <trans.h>
#ifndef unix
#include "..\stg\rostmdir.hxx"
#include "..\stg\rostmfil.hxx"
#else
#include "../stg/rostmdir.hxx"
#include "../stg/rostmfil.hxx"
#endif /* unix */

PerfDbgTag(tagCTransData,    "Urlmon", "Log CTransData",        DEB_DATA);
    DbgTag(tagCTransDataErr, "Urlmon", "Log CTransData Errors", DEB_DATA|DEB_ERROR);

HRESULT FindMediaType(LPCSTR pszType, CLIPFORMAT *cfType);
HRESULT FindMediaTypeW(LPCWSTR pwzType, CLIPFORMAT *cfType);

static LPSTR g_szCF_NULL = "*/*";

char  szContent[]           = "Content Type";
char  szClassID[]           = "CLSID";
char  szFlags[]             = "Flags";
char  szExtension[]         = "Extension";
char  szMimeKey[]           = "MIME\\Database\\Content Type\\";
const ULONG ulMimeKeyLen    = ((sizeof(szMimeKey)/sizeof(char))-1);

// The byte combination that identifies that a file is a storage of
// some kind
const BYTE SIGSTG[] = {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};
const BYTE CBSIGSTG = sizeof(SIGSTG);


#define CBSNIFFDATA_MAX 256

#ifdef DBG
char *szDataSinkName[] =
{
     "Unknown"
    ,"StreamNoCopyData"
    ,"File"
    ,"Storage"
    ,"StreamOnFile"
    ,"StreamBindToObject"
    ,"GenericStream"
};

#define GetDataSinkName(ds) szDataSinkName[ds]

#else

#define GetDataSinkName(ds) ""

#endif


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::CTransData
//
//  Synopsis:
//
//  Arguments:  [pTrans] --
//              [DWORD] --
//              [dwSizeBuffer] --
//              [fBindToObject] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
CTransData::CTransData(CTransaction *pTrans, LPBYTE pByte,DWORD dwSizeBuffer, BOOL fBindToObject) : _CRefs()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                None,
                "CTransData::CTransData",
                "this=%#x, %#x, %#x, %#x, %B",
                this, pTrans, pByte, dwSizeBuffer, fBindToObject
                ));
                
    _TransDataState = TransData_Initialized;
    _wzMime[0] = '\0';
    _wzFileName[0]= 0;
    _pwzUrl = 0;
    _pwzRedirectUrl = 0;
    _pStgMed = 0;
    _lpBuffer = pByte;
    _cbBufferSize = dwSizeBuffer;
    _cbDataSize = 0;
    _cbTotalBytesRead = 0;
    _cbReadReturn = 0;
    _cbBufferFilled = 0;
    _cbDataSniffMin = DATASNIFSIZE_MIN;
    _pEnumFE = 0;
    _pStgMed = NULL;
    _pProt = NULL;

    _fBindToObject = fBindToObject; //Flag changes on attachment.. Use sparingly.
    _fMimeTypeVerified = TRUE;
    _fDocFile = FALSE;
    _fInitialized = FALSE;
    _fRemoteReady = FALSE;
    _fCache = FALSE;
    _fLocked = FALSE;
    _fFileAsStmOnFile = FALSE;
    _fEOFOnSwitchSink = FALSE; 

    _hFile = NULL;
    _cbBytesReported = 0;
    _dwAttached = 0;
    _grfBindF  = 0;
    _grfBSC = 0;

    DEBUG_LEAVE(0);
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::~CTransData
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
CTransData::~CTransData()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                None,
                "CTransData::~CTransData",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::~CTransData");

    if (_pwzUrl)
    {
        delete [] _pwzUrl;
    }

    if (_pwzRedirectUrl)
    {
        delete [] _pwzRedirectUrl;
    }
    
    if (_pProt)
    {
        if (_fLocked)
        {
            _pProt->UnlockRequest();
        }
        _pProt->Release();
        _pProt = NULL;
    }

    if (_pBndCtx)
    {
        _pBndCtx->Release();
    }

    if (_pStgMed)
    {
        TransAssert((   (_pStgMed->pUnkForRelease == NULL)
                     || (_pStgMed->pUnkForRelease == this)));

        if (_pStgMed->tymed == TYMED_ISTREAM)
        {
            _pStgMed->pstm->Release();
        }
        else if (_pStgMed->tymed == TYMED_FILE)
        {
            if (_pStgMed->lpszFileName)
            {
                delete _pStgMed->lpszFileName;
            }
        }
        else if (_pStgMed->tymed == TYMED_ISTORAGE)
        {
            _pStgMed->pstg->Release();
        }

        DbgLog1(tagCTransData, this, "=== CTransData::~CTransData: (pStgMed:%lx)", _pStgMed);
        delete _pStgMed;
        _pStgMed = NULL;
    }

    if (_hFile)
    {
        DbgLog1(tagCTransData, this, "=== CTransData::~CTransData (CloseHandle(%lx)", _hFile);
        CloseHandle(_hFile);
        _hFile = NULL;
    }

    if (_lpBuffer)
    {
        delete _lpBuffer;
    }

    PerfDbgLog(tagCTransData, this, "-CTransData::~CTransData");
    
    DEBUG_LEAVE(0);
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::QueryInterface
//
//  Synopsis:
//
//  Arguments:  [riid] --
//              [ppvObj] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::QueryInterface(REFIID riid, void **ppvObj)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::IUnknown::QueryInterface",
                "this=%#x, %#x, %#x",
                this, &riid, ppvObj
                ));
                
    VDATEPTROUT(ppvObj, void *);
    VDATETHIS(this);
    HRESULT hr = NOERROR;

    PerfDbgLog(tagCTransData, this, "+CTransData::QueryInterface");

    *ppvObj = NULL;
    if ((riid == IID_IUnknown) || (riid == IID_ITransactionData))
    {
        *ppvObj = this;
        AddRef();
    }
    else
    {
        hr = E_NOINTERFACE;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::QueryInterface (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   CTransData::AddRef
//
//  Synopsis:
//
//  Arguments:  [ULONG] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CTransData::AddRef(void)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Dword,
                "CTransData::IUnknown::AddRef",
                "this=%#x",
                this
                ));
                
    LONG lRet = ++_CRefs;
    PerfDbgLog1(tagCTransData, this, "CTransData::AddRef (cRefs:%ld)", lRet);

    DEBUG_LEAVE(lRet);
    return lRet;
}

//+---------------------------------------------------------------------------
//
//  Function:   CTransData::Release
//
//  Synopsis:
//
//  Arguments:  [ULONG] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CTransData::Release(void)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Dword,
                "CTransData::IUnknown::Release",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::Release");

    LONG lRet = --_CRefs;

    if (_CRefs == 0)
    {
        delete this;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::Release (cRefs:%ld)", lRet);

    DEBUG_LEAVE(lRet);
    return lRet;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetTransactionData
//
//  Synopsis:
//
//  Arguments:  [pwzUrl] --
//              [ppwzFilename] --
//              [ppwzMime] --
//              [pdwSizeTotal] --
//              [pdwSizeAvailable] --
//              [dwReserved] --
//
//  Returns:
//
//  History:    9-09-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetTransactionData(LPCWSTR pwzUrl, LPOLESTR *ppwzFilename, LPOLESTR *ppwzMime,
                                            DWORD *pdwSizeTotal, DWORD *pdwSizeAvailable, DWORD  dwReserved)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetTransactionData",
                "this=%#x, %.80wq, %#x, %#x, %#x, %#x, %#x",
                this, pwzUrl, ppwzFilename, ppwzMime, pdwSizeTotal, pdwSizeAvailable, dwReserved
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetTransactionData");
    HRESULT hr = NOERROR;
    TransAssert((pwzUrl && ppwzFilename && ppwzMime && pdwSizeTotal && pdwSizeAvailable ));
    TransAssert((_wzFileName[0] != 0));


    if  (ppwzFilename && ppwzMime && pdwSizeTotal && pdwSizeAvailable)
    {
        LPWSTR pwzUrlLocal = GetUrl();
        TransAssert((pwzUrlLocal));

        DbgLog2(tagCTransData, this, "=== CTransData::GetTransactionData (pwzUrlLocal:%ws, pwzUrl:%ws)", pwzUrlLocal, pwzUrl);

        if (!wcscmp(pwzUrl, _pwzUrl) || (_pwzRedirectUrl && !wcscmp(pwzUrl, _pwzRedirectUrl)))
        {
            *ppwzFilename = OLESTRDuplicate(_wzFileName);

            *ppwzMime = OLESTRDuplicate(_wzMime);

            if (_cbDataSize)
            {
                *pdwSizeTotal  = _cbDataSize;
            }
            else
            {
                *pdwSizeTotal  = _cbTotalBytesRead;
            }
            *pdwSizeAvailable = _cbTotalBytesRead;

        }
        else
        {
            *ppwzFilename = 0;
            *ppwzMime = NULL;
            *pdwSizeTotal = 0;
            *pdwSizeAvailable = 0;
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }

    PerfDbgLog5(tagCTransData, this, "-CTransData::GetTransactionData (hr:%lx, Filename:%ws, Mime:%ws, _cbDataSize:%ld, _cbTotalBytesRead:%ld)",
        hr, XDBG(*ppwzFilename,L""), XDBG(*ppwzMime,L""), _cbDataSize, _cbTotalBytesRead);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::Create
//
//  Synopsis:   Set up the transaction data object.
//
//  Arguments:  [pTrans] -- pointer to transaction
//              [riid] --   riid the users passed in
//              [ppCTD] --  the transdata object passed back
//
//  Returns:
//
//  History:    1-18-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::Create(LPCWSTR pwUrl, DWORD grfBindF, REFIID riid, IBindCtx *pBndCtx,
                                BOOL fBindToObject, CTransData **ppCTD)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::Create",
                "%.80wq, %#x, %#x, %#x, %B, %#x",
                pwUrl, grfBindF, &riid, pBndCtx, fBindToObject, ppCTD
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog1(tagCTransData, NULL, "+CTransData::Create(fBindToObject:%d)", fBindToObject);
    CTransData *pCTData;

    LPBYTE   lpBuffer;
    ULONG    cbBufferSize;
    cbBufferSize = DNLD_BUFFER_SIZE;

    TransAssert((DATASNIFSIZEDOCFILE_MIN <= cbBufferSize));
    lpBuffer = (LPBYTE) new BYTE[cbBufferSize];
    pCTData = new CTransData(NULL,lpBuffer,cbBufferSize, fBindToObject);

    if (lpBuffer && pCTData)
    {
        *ppCTD = pCTData;
        pCTData->Initialize(pwUrl, grfBindF, riid, pBndCtx);

        // Try to get an IEnumFORMATETC pointer from the bind context
        //hr = GetObjectParam(pbc, REG_ENUMFORMATETC, IID_IEnumFORMATETC, (IUnknown**)&_pEnumFE);
    }
    else
    {
        if (pCTData)
        {
            delete pCTData;
        }
        else if (lpBuffer)
        {
            delete lpBuffer;
        }

        hr = E_OUTOFMEMORY;
        *ppCTD = 0;
    }

    PerfDbgLog2(tagCTransData, NULL, "-CTransData::Create (out:%lx,hr:%lx)", *ppCTD, hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::Initialize
//
//  Synopsis:
//
//  Arguments:  [riid] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::Initialize(LPCWSTR pwzUrl, DWORD grfBindF, REFIID riid, IBindCtx *pBndCtx, BOOL fBindToObject)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::Initialize",
                "this=%#x, %.80wq, %#x, %#x, %#x, %B",
                this, pwzUrl, grfBindF, &riid, pBndCtx, fBindToObject
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::Initialize");
    HRESULT hr = NOERROR;

    if (_fInitialized == FALSE)
    {
        _ds = DataSink_Unknown;
        _formatetc.tymed = TYMED_NULL;
        _grfBindF = grfBindF;
        _pBndCtx = pBndCtx;
        _pBndCtx->AddRef();
        
        _pwzUrl = OLESTRDuplicate((LPWSTR)pwzUrl);
        if (!_pwzUrl) 
        {
            hr = E_OUTOFMEMORY;
        }

        if (SUCCEEDED(hr))
        {
            if (_fBindToObject)
            {
                _formatetc.tymed = TYMED_ISTREAM;
                _ds = DataSink_Unknown;
            }
            else if (riid == IID_IUnknown)
            {
                // this is the BindToStorage
                _formatetc.tymed = TYMED_FILE;
                _ds = DataSink_File;
            }
            else if (riid == IID_IStream)
            {
                // We do not know yet which kind of stream
                // SetDataSink will determine this.

                _formatetc.tymed = TYMED_ISTREAM;
                _ds = DataSink_StreamOnFile;
            }
            else if (riid == IID_IStorage)
            {
                BIND_OPTS bindopts;
                bindopts.cbStruct = sizeof(BIND_OPTS);
                hr = pBndCtx->GetBindOptions(&bindopts);
                _grfMode = bindopts.grfMode;

                _formatetc.tymed = TYMED_ISTORAGE;
                _ds = DataSink_Storage;

            }
            else
            {
                // this call should fail
                hr = E_INVALIDARG;
                TransAssert((FALSE && "Unknown data sink for this request!"));
            }
        }
        
        if (SUCCEEDED(hr))
        {
            HRESULT hr1;
            ITransactionData *pCTransData = NULL;
            LPWSTR pwzFilename = NULL;
            LPWSTR pwzMime = NULL;

            hr1 = GetObjectParam(pBndCtx, SZ_TRANSACTIONDATA, IID_ITransactionData, (IUnknown **)&pCTransData);
            DbgLog2(tagCTransData, this, "=== CTransData::Initialize GetObjectParam: pbndctx:%lx, hr:%lx)", pBndCtx, hr1);

            if (SUCCEEDED(hr1))
            {
                TransAssert((pCTransData));
                hr1 = pCTransData->GetTransactionData(_pwzUrl,&pwzFilename, &pwzMime, &_cbDataSize, &_cbTotalBytesRead, 0);
                DbgLog5(tagCTransData, this, "=== CTransData::Initialize GetTransactionData (hr:%lx, Filename:%ws, Mime:%ws, _cbDataSize:%ld, _cbTotalBytesRead:%ld)", hr1, pwzFilename, pwzMime, _cbDataSize, _cbTotalBytesRead);
                pCTransData->Release();
            }


            if (SUCCEEDED(hr1) )
            {
                // set the url filename
                SetFileName(pwzFilename);
                if (pwzMime)
                {
                    SetMimeType(pwzMime);
                    _fMimeTypeVerified = TRUE;
                }
                _fRemoteReady = TRUE;

                if (pwzMime)
                {
                    delete pwzMime;
                }
                if (pwzFilename)
                {
                    delete pwzFilename;
                }
            }
        }
        _fInitialized = TRUE;
    }
    else
    {
        _dwAttached++;
        
        if (_pBndCtx)
        {
            _pBndCtx->Release();
        }

        _pBndCtx = pBndCtx;

        if (_pBndCtx)
        {
            _pBndCtx->AddRef();
        }

        // no set up the righte datasink
        if (fBindToObject)
        {
            _fBindToObject = TRUE;
            SwitchDataSink(DataSink_StreamBindToObject);
        }
        else if (riid == IID_IUnknown)
        {
            SwitchDataSink(DataSink_File);
        }
        else if (   (riid == IID_IStream)
                 && (grfBindF & BINDF_NEEDFILE))
        {
            SwitchDataSink(DataSink_StreamOnFile);
        }

    }

    PerfDbgLog3(tagCTransData, this, "-CTransData::Initialize (_formatetc.tymed:%ld, _ds:%lx, hr:%lx)", _formatetc.tymed,_ds, hr);

    DEBUG_LEAVE(hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::PrepareThreadTransfer
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  History:    10-09-1996   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::PrepareThreadTransfer()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::PrepareThreadTransfer",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::PrepareThreadTransfer");
    HRESULT hr = NOERROR;

    if (_pBndCtx)
    {
        _pBndCtx->Release();
        _pBndCtx = NULL;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::PrepareThreadTransfer (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetDataSink
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    2-22-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DataSink CTransData::GetDataSink()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Pointer,
                "CTransData::GetDataSink",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetDataSink");
    DataSink dsRet = DataSink_Unknown;

    if (_ds == DataSink_Unknown)
    {
        DWORD dwBindF = GetBindFlags();

        if (   (dwBindF & BINDF_ASYNCSTORAGE)
            && (dwBindF & BINDF_PULLDATA)
            && (_formatetc.tymed == TYMED_ISTREAM))
        {
            dsRet = _ds = DataSink_StreamNoCopyData;
        }
    }
    else
    {
        dsRet = _ds;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::GetDataSink (dsRet:%lx)", dsRet);

    DEBUG_LEAVE(dsRet);
    return dsRet;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::SetDataSink
//
//  Synopsis:
//
//  Arguments:  [dwBindF] --
//
//  Returns:
//
//  History:    2-25-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DataSink CTransData::SetDataSink(DWORD dwBindF)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Pointer,
                "CTransData::SetDataSink",
                "this=%#x, %#x",
                this, dwBindF
                ));
                
    PerfDbgLog1(tagCTransData, this, "+CTransData::SetDataSink (_ds:%lx)", _ds);

    TransAssert((_formatetc.tymed != TYMED_NULL));
    _grfBindF = dwBindF;

    switch (_ds)
    {
    case DataSink_Unknown:
    {
        if (_fBindToObject)
        {
            TransAssert((_formatetc.tymed == TYMED_ISTREAM));
            _ds = DataSink_StreamBindToObject;
        }
    }
    break;
    case DataSink_File:
    {
        TransAssert((_formatetc.tymed == TYMED_FILE));

    }
    break;
    case DataSink_StreamOnFile:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        if ((dwBindF & BINDF_ASYNCSTORAGE) && (dwBindF & BINDF_PULLDATA))  
        {
            _ds = DataSink_StreamNoCopyData;
        }
    }
    break;

    case DataSink_StreamBindToObject:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        TransAssert((_fBindToObject == TRUE));

        if (IsObjectReady() == NOERROR)
        {
            // 
            // change it to file - stream on file will be opened
            //
            _ds = DataSink_StreamOnFile;
        }
        else if ((dwBindF & BINDF_ASYNCSTORAGE) && (dwBindF & BINDF_PULLDATA))
        {
            _ds = DataSink_StreamNoCopyData;
        }

    }
    break;

    case DataSink_Storage:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTORAGE));
    }
    break;

    case DataSink_GenericStream:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
    }
    break;

    default:
        TransAssert((FALSE && "CTransData::SetDataSink -- Invalid data location"));
    break;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::SetDataSink (_ds:%lx)", _ds);

    DEBUG_LEAVE(_ds);
    return _ds;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::SwitchDataSink
//
//  Synopsis:
//
//  Arguments:  [dwBindF] --
//
//  Returns:
//
//  History:    2-25-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DataSink CTransData::SwitchDataSink(DataSink dsNew)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Pointer,
                "CTransData::SwitchDataSink",
                "this=%#x, %#x",
                this, dsNew
                ));
                
    PerfDbgLog2(tagCTransData, this, "+CTransData::SwitchDataSink (_ds:%lx, dsNew:%lx)", _ds, dsNew);

    TransAssert((_ds != DataSink_Unknown));
    TransAssert((_formatetc.tymed != TYMED_NULL));
    HRESULT hr = NOERROR;

    switch (_ds)
    {
    case DataSink_File:
    {
        TransAssert((_formatetc.tymed == TYMED_FILE));
    }
    break;
    case DataSink_StreamOnFile:
    {
         TransAssert((_wzFileName[0] != 0));
         _formatetc.tymed = TYMED_ISTREAM;
    }
    break;

    case DataSink_GenericStream:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
    }
    break;

    case DataSink_StreamNoCopyData:
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        if (   dsNew == DataSink_StreamOnFile
            || dsNew == DataSink_GenericStream
            || dsNew == DataSink_StreamBindToObject)
        {
            _ds = dsNew;
            DWORD dwNew = 0;
            hr = OnDataReceived(_grfBSC, 0, 0, &dwNew);
            if( hr == S_FALSE)
            {
                _fEOFOnSwitchSink = TRUE;
            }
        }

    break;

    case DataSink_StreamBindToObject:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        TransAssert((_fBindToObject == TRUE));
        _ds = dsNew;
        if (_ds == DataSink_File)
        {
            _formatetc.tymed = TYMED_FILE;
            DWORD dwNew = 0;
            hr = OnDataReceived(_grfBSC, 0, 0, &dwNew);
            if( hr == S_FALSE)
            {
                _fEOFOnSwitchSink = TRUE;
            }
        }

    }
    break;

    case DataSink_Storage:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTORAGE));
    }
    break;

    default:
        TransAssert((FALSE && "CTransData::SwitchDataSink -- Invalid data location"));
    break;
    }

    PerfDbgLog2(tagCTransData, this, "-CTransData::SwitchDataSink (_ds:%lx, dsNew:%lx)", _ds, dsNew);

    DEBUG_LEAVE(_ds);
    return _ds;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::IsFileRequired()
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    2-22-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL CTransData::IsFileRequired()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Bool,
                "CTransData::IsFileRequired",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::IsFileRequired()");
    BOOL fRet = FALSE;
    TransAssert((_ds != DataSink_Unknown));

    switch (_ds)
    {
    case DataSink_File:
    case DataSink_StreamOnFile:
        fRet = TRUE;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::IsFileRequired() (fRet:%d)", fRet);

    DEBUG_LEAVE(fRet);
    return fRet;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetData
//
//  Synopsis:
//
//  Arguments:  [ppformatetc] --
//              [ppStgMed] --
//              [grfBSCF] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetData(FORMATETC **ppformatetc, STGMEDIUM **ppStgMed, DWORD grfBSCF)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetData",
                "this=%#x, %#x, %#x, %#x",
                this, ppformatetc, ppStgMed, grfBSCF
                ));
                
    PerfDbgLog1(tagCTransData, this, "+CTransData::GetData (_ds:%ld)", _ds);
    HRESULT hr = INET_E_DATA_NOT_AVAILABLE;
    BOOL fNewStgMed = FALSE;
    DataSink ds;

    *ppStgMed = 0;
    *ppformatetc = 0;


    if (_pStgMed == NULL)
    {
        // first find the formatETC based on
        // clipformat and the EnumFormatETC
        FindFormatETC();

        _pStgMed = new STGMEDIUM;
        if (_pStgMed == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto End;
        }

        *ppformatetc = &_formatetc;

        _pStgMed->tymed = TYMED_NULL;
        _pStgMed->hGlobal = NULL;
        _pStgMed->pUnkForRelease = 0;
        _pStgMed->pstm = NULL;
        fNewStgMed = TRUE;

    }

    ds = _ds;
    if( _fFileAsStmOnFile && _ds == DataSink_File )
    {
        ds = DataSink_StreamOnFile;
    } 
    
    switch (ds)
    {
    case DataSink_File:
    {
        //TransAssert((   (grfBSCF & ~BSCF_LASTDATANOTIFICATION)
        //             || ((grfBSCF & BSCF_LASTDATANOTIFICATION) && (_formatetc.tymed == TYMED_FILE)) ));

        if (_wzFileName[0] != 0)
        {
            TransAssert((_wzFileName[0] != 0));
            if (_pStgMed->tymed == TYMED_FILE)
            {
                if (_pStgMed->lpszFileName == NULL)
                {
                    _pStgMed->lpszFileName = (LPWSTR) new WCHAR [wcslen(_wzFileName)+2];
                    if (_pStgMed->lpszFileName)
                    {
                        wcscpy(_pStgMed->lpszFileName, _wzFileName);
                    }
                    else
                    {
                        hr = E_OUTOFMEMORY;
                        goto End;
                    }
                }
            }
            else
            {
                ASSERT(_pStgMed->tymed == TYMED_NULL);
                
                _pStgMed->tymed = TYMED_FILE;
                _pStgMed->lpszFileName = (LPWSTR) new WCHAR [wcslen(_wzFileName)+2];
                if (_pStgMed->lpszFileName)
                {
                    wcscpy(_pStgMed->lpszFileName, _wzFileName);
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                    goto End;
                }
                
            }

            if (_pStgMed->pUnkForRelease == NULL)
            {
                _pStgMed->pUnkForRelease = this;
            }

            hr = NOERROR;
            DbgLog1(tagCTransData, this, "+CTransData::GetData -> TYMED_FILE: %ws", _wzFileName);
        }
        else
        {
            // filename is not available yet
            hr = INET_E_DATA_NOT_AVAILABLE;
        }
    }
    break;

    case DataSink_GenericStream:
    case DataSink_StreamBindToObject:
    case DataSink_StreamNoCopyData:
    {
        DbgLog1(tagCTransData, this, "=== CTransData::GetData (_ds:%lx)", _ds);
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));

        if (_pStgMed->tymed == TYMED_NULL)
        {
            CReadOnlyStreamDirect *pCRoStm = new CReadOnlyStreamDirect( this, _grfBindF);

            if (pCRoStm)
            {
                _pStgMed->tymed = TYMED_ISTREAM;
                _pStgMed->pstm = pCRoStm;
                _pStgMed->pUnkForRelease = this;
                hr = NOERROR;
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }

            DbgLog1(tagCTransData, this, "+CTransData::GetData -> TYMED_ISTREAM: %lx", pCRoStm);
        }
        else
        {
            hr = NOERROR;
        }

        DbgLog2(tagCTransData, this, "=== CTransData::GetData (_ds:%lx,pstm:%lx)", _ds,_pStgMed->pstm);
        DbgLog2(tagCTransData, this, "=== (_cbBufferFilled:%ld, _cbBufferSize:%ld)", _cbBufferFilled,_cbBufferSize);

    }
    break;

    case DataSink_StreamOnFile:
    {
        DbgLog1(tagCTransData, this, "=== CTransData::GetData (_ds:%lx)", _ds);
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));

        if (_wzFileName[0] != 0)
        {
            if (_pStgMed->tymed == TYMED_NULL)
            {
                if( _pStgMed->pstm )
                {
                    //BUG-BUG : relying on undocumented behavior of ReleaseStgMedium..
                    // may or may not zero this out in future.
                    _pStgMed->tymed = TYMED_ISTREAM;
                    _pStgMed->pUnkForRelease = this;

                    // When pStm gets created, the ctor set the ref count
                    // to 1, so no need any additional AddRef.
                    //
                    // Here we are not create a new pStm, we need to do an
                    // AddRef on the pStm because 
                    // CBinding::OnDataNotification (the only caller of this)
                    // will call ReleaseStgMedia() which will call 
                    // _pStgMeg->pstm->Release()

                    _pStgMed->pstm->AddRef();

                    hr = NOERROR;
                }
                else
                {
                    char szTempFile[MAX_PATH];
                    CReadOnlyStreamFile *pCRoStm = NULL;
                    W2A(_wzFileName, szTempFile, MAX_PATH);

                    hr = CReadOnlyStreamFile::Create(szTempFile, &pCRoStm);

                    if (pCRoStm)
                    {
                        _pStgMed->tymed = TYMED_ISTREAM;
                        _pStgMed->pstm = pCRoStm;
                        _pStgMed->pUnkForRelease = this;
                        hr = NOERROR;
                    }
                    else
                    {
                        // filename is not available yet
                        hr = INET_E_DATA_NOT_AVAILABLE;
                    }

                    DbgLog2(tagCTransData, this, "+CTransData::GetData -> TYMED_ISTREAM: %lx (hr:%lx)", pCRoStm,hr);
                }

            }
            else
            {
                TransAssert((_pStgMed->tymed == TYMED_ISTREAM));
                hr = NOERROR;
            }
        }
        else
        {
            DbgLog(tagCTransDataErr, this, "+CTransData::GetData ->StreamOnFile: no filename!");

            // filename is not available yet
            hr = INET_E_DATA_NOT_AVAILABLE;
        }

    }
    break;

    case DataSink_Storage:
    {
        DbgLog2(tagCTransData, this, "=== CTransData::GetData (_ds:%lx, _wzFileName:%ws)", _ds, _wzFileName);
        TransAssert((_formatetc.tymed == TYMED_ISTORAGE));

        TransAssert((_wzFileName != NULL));

        if (_wzFileName[0] != 0)
        {
            if (_pStgMed->tymed == TYMED_NULL)
            {
                IStorage *pStg = NULL;

                hr = StgOpenStorage(_wzFileName, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &pStg );
                DbgLog1(tagCTransData, this, "+CTransData::GetData -> TYMED_ISTORAGE: %ws", _wzFileName);

                if (pStg)
                {
                    _pStgMed->tymed = TYMED_ISTORAGE;
                    _pStgMed->pstg = pStg;
                    _pStgMed->pUnkForRelease = this;
                    hr = NOERROR;
                }

                DbgLog2(tagCTransData, this, "+CTransData::GetData -> TYMED_ISTORAGE: %lx (hr:%lx)", pStg,hr);
            }
            else
            {
                TransAssert((_pStgMed->tymed == TYMED_ISTORAGE));
                hr = NOERROR;
            }
        }
        // else return default error
    }
    break;

    default:
        // this needs to be implemented
        TransAssert((FALSE && "CTransData::GetData -- Invalid data location"));
    break;
    }

    if (SUCCEEDED(hr) && _pStgMed)
    {
        // this object was addref on punkforrelease
        *ppStgMed = _pStgMed;
        *ppformatetc = &_formatetc;

        // Use ourselves as the unknown for release, so that the temp file
        // doesn't get removed when ReleaseStgMedium() is called.  We need
        // to AddRef ourselves to balance the Release that will occur.

        if (_pStgMed->pUnkForRelease)
        {
            AddRef();
            if (   _pProt 
                && fNewStgMed
                && SUCCEEDED(_pProt->LockRequest(0)) )
            {
                    _fLocked = TRUE;
            }
        }

        hr = NOERROR;

        TransAssert((_pStgMed->pUnkForRelease != NULL));
    }

End:
    PerfDbgLog1(tagCTransData, this, "-CTransData::GetData (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::ReadHere
//
//  Synopsis:
//
//  Arguments:  [pBuffer] --
//              [cbBuffer] --
//              [pdwRead] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::ReadHere(LPBYTE pBuffer, DWORD cbBuffer, DWORD *pdwRead)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::ReadHere",
                "this=%#x, %#x, %#x, %#x",
                this, pBuffer, cbBuffer, pdwRead
                ));
                
    PerfDbgLog2(tagCTransData, this, "+CTransData::ReadHere (_ds:%lx,cbBuffer:%ld)", _ds,cbBuffer);
    HRESULT hr = NOERROR;

    switch (_ds)
    {
    case DataSink_StreamNoCopyData:
    case DataSink_StreamBindToObject:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        BOOL fRead = TRUE;
        DWORD dwCopy = 0;
        DWORD dwCopyNew = 0;

        if (_cbBufferFilled)
        {
            fRead = FALSE;

            // copy data form the local buffer to the provide buffer
            if (cbBuffer < _cbBufferFilled)
            {
                dwCopy = cbBuffer;
                memcpy(pBuffer, _lpBuffer, cbBuffer);
                // move the memory to the front
                memcpy(_lpBuffer, _lpBuffer + cbBuffer, _cbBufferFilled - cbBuffer);
                _cbBufferFilled -= cbBuffer;
                hr = S_OK;
            }
            else if (cbBuffer == _cbBufferFilled)
            {
                dwCopy = _cbBufferFilled;
                memcpy(pBuffer, _lpBuffer, _cbBufferFilled);
                _cbBufferFilled = 0;
                hr = S_OK;
            }
            else
            {
                //
                // user buffer is greater than what is available in
                //
                dwCopy = _cbBufferFilled;
                memcpy(pBuffer, _lpBuffer, _cbBufferFilled);
                _cbBufferFilled = 0;
                fRead = TRUE;
                hr = E_PENDING;
            }
        }

        // now read from the wire
        if ((_cbBufferFilled == 0) && (fRead == TRUE))
        {
            // read data from our buffer
            if (_TransDataState == TransData_ProtoTerminated)
            {
                // download completed
                hr = (dwCopy) ? S_OK : S_FALSE;
            }
            else if (pBuffer && cbBuffer)
            {
                hr = _pProt->Read(pBuffer + dwCopy, cbBuffer - dwCopy, &dwCopyNew);
                _cbTotalBytesRead += dwCopyNew;
            }
            else
            {
                hr = E_INVALIDARG;
            }
        }

        if (pdwRead)
        {
            *pdwRead = dwCopy + dwCopyNew;

            if (*pdwRead && (hr != E_PENDING))
            // some data in buffer
            {
                hr = S_OK;
            }
        }
    }
    break;

    case DataSink_StreamOnFile:
    {
        DbgLog(tagCTransData, this, "=== CTransData::ReadHere (_ds:DataSink_StreamNoCopyData)");
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        TransAssert((pdwRead != NULL));

        // read data from our buffer
        if (_TransDataState == TransData_ProtoTerminated)
        {
            // download completed
            hr = S_FALSE;
        }
        else if (pBuffer && cbBuffer)
        {
            hr = _pProt->Read(pBuffer, cbBuffer, pdwRead);
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    break;

    case DataSink_File:
    {
        TransAssert((_formatetc.tymed == TYMED_FILE));

    }
    break;

    default:
        // this needs to be implemented
        hr = E_FAIL;
        TransAssert((FALSE && "CTransData::ReadHere -- Invalid data location"));
    break;
    }

    TransAssert((   (   (hr == S_FALSE && *pdwRead == 0)
                     || (hr == S_OK && *pdwRead != 0)
                     || (hr == E_PENDING)
                     || (hr == E_INVALIDARG)
                     || (hr == INET_E_DATA_NOT_AVAILABLE)
                     || (hr == INET_E_DOWNLOAD_FAILURE)
                    )
                 && "CTransData::ReadHere -- Invalid return code"));


    PerfDbgLog3(tagCTransData, this, "-CTransData::ReadHere (hr:%lx,cbBuffer:%ld,pdwRead:%ld)", hr,cbBuffer,*pdwRead);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::Seek
//
//  Synopsis:
//
//  Arguments:  [DWORD] --
//              [ULARGE_INTEGER] --
//              [plibNewPosition] --
//
//  Returns:
//
//  History:    10-30-1996   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER *plibNewPosition)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::Seek",
                "this=%#x, %#x, %#x, %#x",
                this, dlibMove, dwOrigin, plibNewPosition
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::Seek");
    HRESULT hr;

    hr = _pProt->Seek(dlibMove, dwOrigin,plibNewPosition);

    PerfDbgLog1(tagCTransData, this, "-CTransData::Seek (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetReadBuffer
//
//  Synopsis:
//
//  Arguments:  [ppBuffer] --
//              [pcbBytes] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetReadBuffer(BYTE **ppBuffer, DWORD *pcbBytes)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetReadBuffer",
                "this=%#x, %#x, %#x",
                this, ppBuffer, pcbBytes
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetReadBuffer");
    HRESULT hr;

    TransAssert((_cbBufferSize >= _cbBufferFilled));

    *ppBuffer = _lpBuffer + _cbBufferFilled;
    *pcbBytes = _cbBufferSize - _cbBufferFilled;

    hr = ((_cbBufferSize - _cbBufferFilled) > 0) ? NOERROR : E_FAIL;

    PerfDbgLog3(tagCTransData, this, "-CTransData::GetReadBuffer (pBuffer:%lx,size:%ld,hr:%lx)",
        *ppBuffer, *pcbBytes, hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::OnDataInBuffer
//
//  Synopsis:
//
//  Arguments:  [pBuffer] --
//              [cbBytesAvailable] --
//              [dwBytesTotal] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::OnDataInBuffer(BYTE *pBuffer, DWORD cbBytesRead, DWORD dwBytesTotal)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::OnDataInBuffer",
                "this=%#x, %#x, %#x, %#x",
                this, pBuffer, cbBytesRead, dwBytesTotal
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::OnDataInBuffer");
    HRESULT hr = NOERROR;

    _cbTotalBytesRead += cbBytesRead;
    _cbBufferFilled += cbBytesRead;

    PerfDbgLog1(tagCTransData, this, "-CTransData::OnDataInBuffer (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::OnDataReceived
//
//  Synopsis:
//
//  Arguments:  [cbBytesAvailable] --
//              [dwBytesTotalRead] --
//              [dwTotalSize] --
//
//  Returns:
//
//  History:    2-23-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::OnDataReceived(DWORD grfBSC, DWORD cbBytesAvailable, DWORD dwTotalSize, DWORD *pcbNewAvailable)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::OnDataReceived",
                "this=%#x, %#x, %#x, %#x, %#x",
                this, grfBSC, cbBytesAvailable, dwTotalSize, pcbNewAvailable
                ));
                
    PerfDbgLog4(tagCTransData, this, "+CTransData::OnDataReceived (_ds:%s, grfBSC:%lx,  cbBytesAvailable:%ld, _cbTotalBytesRead:%ld)",
        GetDataSinkName(_ds), grfBSC, cbBytesAvailable, _cbTotalBytesRead);
    HRESULT hr = NOERROR;

    *pcbNewAvailable = cbBytesAvailable;
    _grfBSC |= grfBSC;

    switch (_ds)
    {
    case DataSink_StreamNoCopyData:
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));

    case DataSink_StreamBindToObject:
    {
        DWORD dwNewData = 0;
        TransAssert((_pProt && _cbDataSniffMin));

        // _cbTotalBytesRead = # of bytes read so far
        if (_cbTotalBytesRead < _cbDataSniffMin)
        {
            // no bytes read so far
            TransAssert((_cbTotalBytesRead < _cbDataSniffMin));
            // read data into buffer and report progess
            hr = _pProt->Read(_lpBuffer + _cbBufferFilled, _cbBufferSize - _cbBufferFilled, &dwNewData);
            _cbTotalBytesRead += dwNewData;
            _cbBufferFilled += dwNewData;

            // now check if this is docfile
            // if so download at least 2k
            if (!_fDocFile && _cbBufferFilled && (IsDocFile(_lpBuffer, _cbBufferFilled) == S_OK))
            {
                _fDocFile = TRUE;
                _cbDataSniffMin =  (dwTotalSize && dwTotalSize < DATASNIFSIZEDOCFILE_MIN) ? dwTotalSize : DATASNIFSIZEDOCFILE_MIN;
            }

            if ((hr == E_PENDING) && (_cbTotalBytesRead < _cbDataSniffMin))
            {
                // do not report anything - wait until we get more data
                // a request is pending at this time
                // need more data to sniff properly
                hr  = S_NEEDMOREDATA;
            }
            else if (hr == NOERROR || hr == E_PENDING)
            {
                TransAssert((_cbTotalBytesRead != 0));

                // report the data we have in the buffer or
                // the available #
                DWORD cbBytesReport =  (cbBytesAvailable > _cbTotalBytesRead) ? cbBytesAvailable : _cbTotalBytesRead + 1;

                if (dwTotalSize && ((cbBytesReport > dwTotalSize)))
                {
                    cbBytesReport =  dwTotalSize;
                }

               *pcbNewAvailable = cbBytesReport;
            }
        }
    }

    break;


    case DataSink_File:
    case DataSink_Storage:
    case DataSink_StreamOnFile:
    {
        DWORD dwNewData = 0;

        TransAssert((_pProt));

        if (_cbTotalBytesRead < _cbDataSniffMin)
        {
            _cbDataSniffMin =  (dwTotalSize && dwTotalSize < DATASNIFSIZEDOCFILE_MIN) ? dwTotalSize : DATASNIFSIZEDOCFILE_MIN;

            // read data into buffer and report progess
            hr = _pProt->Read(_lpBuffer + _cbBufferFilled, _cbBufferSize - _cbBufferFilled, &dwNewData);

            _cbTotalBytesRead += dwNewData;
            _cbBufferFilled += dwNewData;

            if ((hr == E_PENDING) && (_cbTotalBytesRead < _cbDataSniffMin))
            {
                // do not report anything - wait until we get more data
                // a request is pending at this time
                // need more data to sniff properly
                hr  = S_NEEDMOREDATA;
            }
            else if (hr == NOERROR || hr == E_PENDING)
            {
                TransAssert((_cbTotalBytesRead != 0));
            }
            *pcbNewAvailable = _cbTotalBytesRead;
        }

        // Note: read until pending or eof and report progress
        //       this is important to keep the download going
        if (hr == NOERROR)
        {
            // reset the buffer - don't overwrite sniffing data
            _cbBufferFilled = (_fMimeTypeVerified) ? 0 : _cbDataSniffMin;
            
            // bugbug: need special flag which indicates not to read if fully available
            //if (!(grfBSC & BSCF_DATAFULLYAVAILABLE))
            
            if (1)
            {
                //read as much data until S_OK or E_PENDING or error
                do
                {
                    hr = _pProt->Read(_lpBuffer + _cbBufferFilled, _cbBufferSize - _cbBufferFilled, &dwNewData);
                    _cbTotalBytesRead += dwNewData;

                } while (hr == NOERROR);

                // report available data
                if (hr == NOERROR || hr == E_PENDING)
                {
                    TransAssert((_cbTotalBytesRead != 0));
                }
                *pcbNewAvailable = (cbBytesAvailable > _cbTotalBytesRead) ? _cbTotalBytesRead : _cbTotalBytesRead;
            }
            else
            {
                TransAssert((dwTotalSize == cbBytesAvailable));
                *pcbNewAvailable = dwTotalSize;
            }

        }
    }
    break;

    default:
        TransAssert((FALSE && "CTransData::OnDataReceived -- Invalid data location"));
    break;
    }

    // cbBytesAvailable might be off be 1
    //TransAssert((cbBytesAvailable <= *pcbNewAvailable));

    PerfDbgLog1(tagCTransData, this, "-CTransData::OnDataReceived (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::OnStart
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    2-23-96   JohannP (Johann Posch)   Created
//
//  Notes:      TransData does not keep CINet alive
//              and will NOT call delete on it!
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::OnStart(IOInetProtocol *pCINet)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::OnStart",
                "this=%#x, %#x",
                this, pCINet
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog(tagCTransData, this, "+CTransData::OnStart");
    TransAssert((pCINet != NULL));

    if (_pProt)
    {
        _pProt->Release();
        _pProt = NULL;
    }

    switch (_ds)
    {
    case DataSink_StreamBindToObject:
    case DataSink_StreamNoCopyData:
    case DataSink_StreamOnFile:
    case DataSink_GenericStream:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        // this point is not addref by CTransData
        TransAssert((_pProt == NULL));
        _pProt = pCINet;
    }
    break;

    case DataSink_Storage:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTORAGE));
        // this point is not addref by CTransData
        TransAssert((_pProt == NULL));
        _pProt = pCINet;
    }
    break;


    case DataSink_File:
    {
        TransAssert((_formatetc.tymed == TYMED_FILE));
        // this point is not addref by CTransData
        TransAssert((_pProt == NULL));
        _pProt = pCINet;
    }
    break;

    default:
        TransAssert((FALSE && "Invalid data location"));
    break;
    }

    if (_pProt)
    {
        _pProt->AddRef();
    }
    else
    {
        TransAssert((FALSE));
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::OnStart (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::OnTerminate
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::OnTerminate()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::OnTerminate",
                "this=%#x",
                this
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog(tagCTransData, this, "+CTransData::OnTerminate");

    TransAssert((_TransDataState < TransData_ProtoTerminated));

    switch (_ds)
    {

    case DataSink_Storage:
    case DataSink_File:
    {
        TransAssert((_formatetc.tymed == TYMED_FILE) || (_formatetc.tymed == TYMED_ISTORAGE));
        DbgLog2(tagCTransData, this, ">>> CTransData::OnTerminate (hr:%lx, _wzTempFile:%ws)", hr, _wzFileName);
    }
    break;

    case DataSink_StreamBindToObject:
    case DataSink_StreamNoCopyData:
    case DataSink_StreamOnFile:
    case DataSink_GenericStream:
    {
        TransAssert((_formatetc.tymed == TYMED_ISTREAM));
        TransAssert((_pProt != NULL));
    }
    break;
    default:
        TransAssert((FALSE && "Invalid data location"));
    break;
    }

    if (_pBndCtx)
    {
        _pBndCtx->Release();
        _pBndCtx = NULL;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::OnTerminate (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::FindFormatETC
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::FindFormatETC()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::FindFormatETC",
                "this=%#x",
                this
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog1(tagCTransData, this, "+CTransData::FindFormatETC (_cbBufferFilled:%ld)", _cbBufferFilled);

    _formatetc.ptd = NULL;
    _formatetc.dwAspect = DVASPECT_CONTENT;
    _formatetc.lindex = -1;

    _formatetc.cfFormat = 0;
    TransAssert((   _formatetc.tymed == TYMED_ISTREAM
                 || _formatetc.tymed == TYMED_FILE
                 || _formatetc.tymed == TYMED_ISTORAGE ));

    LPCWSTR pwzStrOrg = GetMimeType();
    LPCWSTR pwzStr = pwzStrOrg;

    // If not already done so, attempt to
    // verify mime type by examining data.
    if (!_fMimeTypeVerified)
    {
        DWORD dwFlags = 0;
        DWORD dwSniffFlags = 0;
        DWORD cbLen = sizeof(dwFlags);
        LPWSTR pwzFileName = GetFileName();
        LPWSTR pwzStrOut = 0;



        // the buffer should contain data if the no mime
        TransAssert((    (_cbBufferFilled == 0 && (pwzStr || pwzFileName))
                     ||  ( _cbBufferFilled != 0) ));

        FindMimeFromData(NULL, pwzFileName,_lpBuffer, _cbBufferFilled, pwzStrOrg, dwSniffFlags,  &pwzStrOut, 0);

        if (pwzStrOut)
        {
            SetMimeType(pwzStrOut);
            pwzStr = GetMimeType();
        }
        
        delete [] pwzStrOut;
        _fMimeTypeVerified = TRUE;
    }

    // the new mime should never be NULL if we had a proposed mime
    TransAssert((   (pwzStrOrg && pwzStr)
                 || (pwzStrOrg == NULL)     ));

    if (pwzStr)
    {
        char szMime[SZMIMESIZE_MAX];
        W2A(pwzStr, szMime, SZMIMESIZE_MAX);

        CLIPFORMAT cfType;
        if (FindMediaTypeW(pwzStr,&cfType) != NOERROR)
        {
            _formatetc.cfFormat = (CLIPFORMAT) RegisterClipboardFormat(szMime);
        }
        else
        {
            _formatetc.cfFormat = cfType;
        }
    }
    else
    {
        _formatetc.cfFormat = CF_NULL;
    }

    // Check if the format that we got is one of the format that
    // is requested, and if so, use it
#ifdef UNUSED
    if (_pEnumFE)
    {
        FORMATETC FmtetcT;
        BOOL fDone = FALSE;

        _pEnumFE->Reset();

        while (!fDone && ((hr = _pEnumFE->Next(1, &FmtetcT,NULL)) == NOERROR))
        {
            TransAssert((SUCCEEDED(hr)));

            if (FmtetcT.cfFormat == _cfFormat)
            {
                _formatetc.cfFormat = _cfFormat;
            }
        }
    }
#endif //UNUSED

    PerfDbgLog3(tagCTransData, this, "-CTransData::FindFormatETC (hr:%lx, szStr:%ws, _formatetc.cfFormat:%lx)", hr, pwzStr?pwzStr:L"", _formatetc.cfFormat);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetAcceptStr
//
//  Synopsis:
//
//  Arguments:  [ppwzStr] --
//              [pcElements] --
//
//  Returns:
//
//  History:    3-29-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetAcceptStr(LPWSTR *ppwzStr, ULONG *pcElements)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetAcceptStr",
                "this=%#x, %#x, %#x",
                this, ppwzStr, pcElements
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog(tagCTransData, this, "+CTransData::GetAcceptStr");
    IEnumFORMATETC *pIEnumFE = NULL;

    TransAssert((ppwzStr));
    TransAssert((*pcElements > 0));

    CHAR pszUnknownName[MAX_PATH];
    ULONG cMimes = 0;
    
    *ppwzStr = 0;
    hr = GetObjectParam(_pBndCtx, REG_ENUMFORMATETC, IID_IEnumFORMATETC, (IUnknown**)&pIEnumFE);

    if (hr == NOERROR)
    {
        BOOL fCF_NULL = FALSE;
        #define ELEMENTS 10
        ULONG cElementsIn = ELEMENTS;
        ULONG cElements = 0;
        FORMATETC rgFormatEtc[ELEMENTS];


        pIEnumFE->Reset();
        // find # of elements
        do
        {
            ULONG cEl = 0;
            hr = pIEnumFE->Next(cElementsIn, rgFormatEtc ,&cEl);
            cElements += cEl;
        } while (hr == S_OK);

        UrlMkAssert((cElements > 0));

        if  (   (cElements > 0)
             && (hr == S_OK || hr == S_FALSE))
        {

            {
                ULONG cElementsOut = 0;
                pIEnumFE->Reset();
                do
                {
                    ULONG cEl = 0;
                    FORMATETC *pFmtEtc = rgFormatEtc;

                    hr = pIEnumFE->Next(cElementsIn, rgFormatEtc ,&cEl);                    
                    cElementsOut += cEl;
                    // (hr==S_FALSE) => (cElementsOut==cEl)
                    UrlMkAssert((!(hr==S_FALSE) || (cElementsOut == cElements)));

                    // On the last call to ->Next() enumerator, we get back an S_FALSE
                    // if the # of elements received (cEl) < asked for (cElementsIn)
                    if ((cElementsOut == cElements) && (hr == S_FALSE))
                        hr = S_OK;

                    for (ULONG i = 0; i < cEl; i++)
                    {
                        if( cMimes >=  (*pcElements - 1)  )
                        {
                            // exceeding the income array size, stop
                            break;
                        }


                        LPSTR szFormat = NULL;
                        CLIPFORMAT  cfFormat = (pFmtEtc + i)->cfFormat;
                        if (cfFormat == CF_NULL)
                        {
                            fCF_NULL = TRUE;
                        }
                        else
                        {
                            hr = FindMediaString(cfFormat, &szFormat);
                            if (hr != NOERROR || !szFormat)
                            {
                                // unknown cfFormat
                                if( GetClipboardFormatName(cfFormat, pszUnknownName, MAX_PATH))
                                {
                                    hr = NOERROR;
                                    szFormat = pszUnknownName;
                                } 
                                else
                                {
                                    // word97 will send out cfFormat=1
                                    // which associated to "" string
                                    hr = NOERROR;
                                }
                            }
                            if( szFormat )
                            {

                                *(ppwzStr + cMimes)= NULL; 
                                *(ppwzStr + cMimes) = DupA2W(szFormat);
                                if( *(ppwzStr + cMimes) )
                                {
                                    cMimes++;
                                } 
                                else
                                {
                                    hr = E_OUTOFMEMORY;
                                    break;
                                }
                            }
                        }
                    }

                } while ( (cElementsOut < cElements) && (hr == NOERROR) );

                // append the cf_null (*/*)
                if( hr == NOERROR && (fCF_NULL || (cMimes == 0)) )
                {
                    *(ppwzStr + cMimes) = DupA2W(g_szCF_NULL);
                    if( !*(ppwzStr + cMimes) )
                    {
                        hr = E_OUTOFMEMORY;
                    } 
                    else
                    { 
                        cMimes++;
                        hr = NOERROR;
                    }
                }

                if( hr == NOERROR )
                {
                    *(ppwzStr + cMimes) = NULL;
                }
            }
        }

        pIEnumFE->Release();
    }
    else
    {
        {
            *ppwzStr = DupA2W(g_szCF_NULL);
            if( *ppwzStr )
            {
                cMimes = 1;
                *(ppwzStr + 1) = NULL;
                hr = NOERROR;
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
    }

    if( hr == NOERROR )
    {
        *pcElements = cMimes;
    }
    else
    {
        *pcElements = 0;
        if( cMimes >= 1 )
        {
            for( ULONG i = 0; i < cMimes; i ++)
            {
                delete [] *(ppwzStr + i);
            }
        }
        *ppwzStr = NULL;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::GetAcceptStr (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetAcceptMimes
//
//  Synopsis:
//
//  Arguments:  [ppStr] --
//
//  Returns:
//
//  History:    3-29-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetAcceptMimes(LPWSTR *ppwzStr, ULONG cel, ULONG *pcElements)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetAcceptMimes",
                "this=%#x, %#x, %#x, %#x",
                this, ppwzStr, cel, pcElements
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog(tagCTransData, this, "+CTransData::GetAcceptMimes");

    TransAssert((ppwzStr && pcElements && *pcElements));

    if (ppwzStr && pcElements)
    {
        if( *pcElements > 1 )
        {
            hr = GetAcceptStr( ppwzStr, pcElements );
        }
        else if( *pcElements == 1)
        {
            // zero terminated 
            *ppwzStr = NULL;
        }
        else
        {
            hr =  E_INVALIDARG;
        }
    }
    else
    {
        hr =  E_INVALIDARG;
    }
    PerfDbgLog1(tagCTransData, this, "-CTransData::GetAcceptMimes (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::SetClipFormat
//
//  Synopsis:
//
//  Arguments:  [cfFormat] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::SetClipFormat(CLIPFORMAT  cfFormat)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::SetClipFormat",
                "this=%#x, %#x",
                this, cfFormat
                ));
                
    HRESULT hr = NOERROR;
    PerfDbgLog(tagCTransData, this, "+CTransData::SetClipFormat");

    _cfFormat = cfFormat;

    PerfDbgLog1(tagCTransData, this, "-CTransData::SetClipFormat (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::IsObjectReady
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::IsObjectReady(  )
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::IsObjectReady",
                "this=%#x",
                this
                ));
                
    HRESULT hr = E_FAIL;
    PerfDbgLog(tagCTransData, this, "+CTransData::IsObjectReady");

    // check size and
    if ((_cbDataSize != 0) && (_cbDataSize == _cbTotalBytesRead))
    {
        hr = NOERROR;
    }

    PerfDbgLog3(tagCTransData, this, "-CTransData::IsObjectReady (hr:%lx, _cbDataSize:%ld, _cbTotalBytesRead:%ld)", hr, _cbDataSize, _cbTotalBytesRead);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::InProgress
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::InProgress()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::InProgress",
                "this=%#x",
                this
                ));
                
    HRESULT hr = S_FALSE;
    PerfDbgLog(tagCTransData, this, "+CTransData::InProgress");

    if (_grfBSC & (BSCF_LASTDATANOTIFICATION | BSCF_FIRSTDATANOTIFICATION))
    {
        hr = S_FALSE;
    }
    else if (_cbTotalBytesRead == 0)
    {
        // check if some bits already in the buffer

        hr = S_OK;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::InProgress (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetFileName
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LPWSTR CTransData::GetFileName()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Pointer,
                "CTransData::GetFileName",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetFileName");
    LPWSTR pwzFileName = NULL;

    //TransAssert((_wzFileName[0] != 0));

    if (_wzFileName[0] != 0)
    {
        pwzFileName = _wzFileName;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::GetFileName (szFile:%ws)", pwzFileName?pwzFileName:L"");

    DEBUG_LEAVE(pwzFileName);
    return pwzFileName;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetClassID
//
//  Synopsis:
//
//  Arguments:  [pclsid] --
//              [fReOpen] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::GetClassID(CLSID clsidIn, CLSID *pclsid)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::GetClassID",
                "this=%#x, %#x, %#x",
                this, clsidIn, pclsid
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetClassID");
    WCHAR wzMime[SZMIMESIZE_MAX];
    HRESULT hr;
    LPWSTR pwzTempFile = NULL;

    DWORD fIgnoreMimeClsid = GetBindFlags() & BINDF_IGNOREMIMECLSID;
    LPCWSTR pwzMime = GetMimeType();

    pwzTempFile = GetFileName();

    if (   (_cbBufferFilled != 0)
             && (_cbTotalBytesRead <= _cbBufferSize)
             && (IsDocFile(_lpBuffer, _cbBufferFilled) == S_OK))
    {
        _fDocFile = TRUE;
        _fMimeTypeVerified = TRUE;

        // Storage file (docfile) case
        // GetClassFileOrMime takes care of class mapping
        hr = GetClassFileOrMime2(_pBndCtx, pwzTempFile, _lpBuffer, _cbBufferFilled, pwzMime, 0, pclsid, fIgnoreMimeClsid);
        if (hr != NOERROR)
        {
            // S_FALSE means keep downloading
            hr = S_FALSE;
        }
    }
    else
    {
        if (!_fMimeTypeVerified)
        {
            DWORD dwFlags = 0;
            DWORD dwSniffFlags = 0;
            DWORD cbLen = sizeof(dwFlags);

            LPWSTR  pwzStr = 0;
            FindMimeFromData(NULL, pwzTempFile, _lpBuffer, _cbBufferFilled, pwzMime, dwSniffFlags,  &pwzStr, 0);
            if (pwzStr)
            {
                SetMimeType(pwzStr);
            }
            _fMimeTypeVerified = TRUE;
            delete [] pwzStr;
        }

        hr = GetClassFileOrMime2(_pBndCtx, pwzTempFile, NULL, 0, pwzMime, 0, pclsid, fIgnoreMimeClsid);
    }

    #if DBG==1
    if (hr == NOERROR)
    {
        LPOLESTR pszStr;
        StringFromCLSID(*pclsid, &pszStr);
        DbgLog2(tagCTransData, this, "CTransData::GetClassID (file:%ws)(class:%ws)",
            pwzTempFile, pszStr);
        delete pszStr;
    }
    #endif

    PerfDbgLog1(tagCTransData, this, "-CTransData::GetClassID (hr:%lx)", hr);

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::SetMimeType
//
//  Synopsis:
//
//  Arguments:  [pszMime] --
//
//  Returns:
//
//  History:    2-07-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::SetMimeType(LPCWSTR pwzMime)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::SetMimeType",
                "this=%#x, %.80wq",
                this, pwzMime
                ));
                
    PerfDbgLog2(tagCTransData, this, "+CTransData::SetMimeType (OldMime:%ws; MimeStr:%ws)", _wzMime?_wzMime:L"", pwzMime?pwzMime:L"");
    HRESULT hr = NOERROR;

    if (pwzMime)
    {

        #if DBG_XXX
        if (pwzMime)
        {
            if (   !wcscmp(_wzMime, CFWSTR_MIME_HTML)
                || !wcscmp(_wzMime, CFWSTR_MIME_TEXT))
            {
                if (   !wcscmp(pwzMime, CFWSTR_MIME_RAWDATA)
                    || !wcscmp(pwzMime,L"application/octet-stream"))
                {
                    DbgLog2(tagCTransDataErr, "=== SetMimeType: OldMime:%ws, NewMime:%ws",
                        _wzMime, pwzMime);
                    //TransAssert((FALSE));
                }
            }
        }

        if (_wzMime[0] != 0 && strcmp(_wzMime,pwzMime))
        {
            DbgLog2(tagTransDataErr, "=== SetMimeType: OldMime:%s, NewMime:%s",
                _wzMime, pwzMime);
        }

        #endif //DBG


        DWORD cLen = wcslen((LPWSTR)pwzMime);

        if (cLen >= SZMIMESIZE_MAX)
        {
            cLen = SZMIMESIZE_MAX - 1;
            wcsncpy(_wzMime, (LPWSTR)pwzMime, cLen);
            _wzMime[cLen] = 0;
        }
        else
        {
            wcscpy(_wzMime, (LPWSTR)pwzMime);
        }
    }
    else
    {
        DbgLog(tagCTransDataErr, this, "CTransData::SetMimeType ->invalid mime");
    }

    PerfDbgLog2(tagCTransData, this, "-CTransData::SetMimeType (hr:%lx, Mime:%ws)", hr,_wzMime?_wzMime:L"");

    DEBUG_LEAVE(hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::SetFileName
//
//  Synopsis:
//
//  Arguments:  [szFile] --
//
//  Returns:
//
//  History:    1-27-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CTransData::SetFileName(LPWSTR pwzFile)
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Hresult,
                "CTransData::SetFileName",
                "this=%#x, %.80wq",
                this, pwzFile
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::SetFileName");

    TransAssert((pwzFile));
    if(pwzFile)
    {
        wcscpy(_wzFileName, pwzFile);
    }

    PerfDbgLog2(tagCTransData, this, "-CTransData::SetFileName (_wzFileName:%ws, hr:%lx)", _wzFileName?_wzFileName:L"", NOERROR);

    DEBUG_LEAVE(NOERROR);
    return NOERROR;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTransData::GetMimeType
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    4-24-96   JohannP (Johann Posch)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
LPCWSTR CTransData::GetMimeType()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                Pointer,
                "CTransData::GetMimeType",
                "this=%#x",
                this
                ));
                
    PerfDbgLog(tagCTransData, this, "+CTransData::GetMimeType");
    LPWSTR pwzStr = NULL;

    if (_wzMime[0] != 0)
    {
        pwzStr = _wzMime;
    }

    PerfDbgLog1(tagCTransData, this, "-CTransData::GetMimeType (pStr:%ws)", pwzStr?pwzStr:L"");

    DEBUG_LEAVE(pwzStr);
    return pwzStr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTransData::OnEndofData()
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    7-30-97   DanpoZ(Danpo Zhang)   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
void CTransData::OnEndofData()
{
    DEBUG_ENTER((DBG_TRANSDAT,
                None,
                "CTransData::OnEndofData",
                "this=%#x",
                this
                ));
                
    PerfDbgLog1(tagCTransData, this, "+CTransData::OnEndofData(_ds:%ld)", _ds);
    BOOL fNewStgMed = FALSE;

    if (_pStgMed  && _ds == DataSink_StreamOnFile && _pStgMed->pstm )
    {
        ((CReadOnlyStreamFile*)(_pStgMed->pstm))->SetDataFullyAvailable();
    }

    PerfDbgLog(tagCTransData, this, "-CTransData::OnEndofData");

    DEBUG_LEAVE(0);
}