Windows2003-3790/inetcore/urlmon/urlhlink/urlostrm.cxx
2020-09-30 16:53:55 +02:00

2148 lines
53 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: URLOSTRM.CXX
//
// Contents: Public interface and implementation of the URL
// Open Stream APIs
//
// Classes: many (see below)
//
// Functions:
//
// History: 04-22-96 VictorS Created
// 05-16-96 Jobi Modified
// 06-06-96 Jobi Modified
// 07-05-96 Ramesh Modified
//----------------------------------------------------------------------------
#ifndef unix
// For some reaon on unix this causes NOERROR compile errors
#include "winerror.h"
#endif /* unix */
#include "ocidl.h"
#include "servprov.h"
#include "tchar.h"
#include "wininet.h"
#include "urlmki.h"
#include "urlhlink.h"
#include <shlwapi.h>
#include <shlwapip.h>
#define URLOSTRM_DONOT_NOTIFY_ONDATA 0xFF
#define URLOSTRM_NOTIFY_ONDATA 0x00
//----------------------------------------------------------//
// //
// This file can never be compiled with the _UNICODE or //
// UNICODE macros defined. //
// //
//----------------------------------------------------------//
//----------------------------------------------------------//
// MACROS
//----------------------------------------------------------//
// These macros can go away when macros and implementation of
// the InetSDK has settled down...
#define IS_E_PENDING(x) (x == E_PENDING)
#define LPUOSCALLBACK LPBINDSTATUSCALLBACK
#define PUMPREAD(strm) \
{ \
DWORD dwSize = 0; \
char * x = new char[20]; \
hr = strm->Read(x, 20, &dwSize ); \
if( !IS_E_PENDING(hr) && (dwSize != 0) ) \
{ \
DPRINTF( ("Data on the over read! %d\n", dwSize) ); \
} \
delete x; \
}
#define HANDLE_ABORT(hr) \
{ if( hr == E_ABORT) \
{ m_bInAbort = 1; \
if( m_binding ) \
m_binding->Abort(); \
return(E_ABORT); \
} \
}
#define CHECK_MEMORY(ptr) \
{ if( !ptr ) \
{ DPRINTF( ("Failed to alloc memory") ); \
m_bInAbort = 1; \
if( m_binding ) \
m_binding->Abort(); \
return(E_OUTOFMEMORY); \
} \
}
//
// Refcount helper
//
// Standardized COM Ref counting. ASSUMES that class has a ULONG
// data member called 'm_ref'.
#define IMPLEMENT_REFCOUNT(clsname) \
STDMETHOD_(ULONG, AddRef)() \
{ CHECK_INTERFACE(this); \
DPRINTF( ("(%#08x) " #clsname "::Addref %d\n", this, m_ref+1) );\
return(++m_ref); }\
STDMETHOD_(ULONG, Release)()\
{ CHECK_INTERFACE(this); \
DPRINTF( ( "(%#08x) " #clsname "::Release : %d\n", this, m_ref-1) );\
if( !--m_ref )\
{ delete this; return 0; }\
return m_ref;\
}
//----------------------------------------------------------
//
// DEBUG MACROS
//
//----------------------------------------------------------
#ifdef _DEBUG
// Check the validity of a pointer - use this for all allocated memory
#define CHECK_POINTER(val) \
if (!(val) || IsBadWritePtr((void *)(val), sizeof(void *))) \
{ DPRINTF( ("BAD POINTER: %s!\n", #val ) ); \
return E_POINTER; }
// Check the validity of an interface pointer. Use this for all pointers
// to C++ objects that are supposed to have vtables.
#define CHECK_INTERFACE(val) \
if (!(val) || IsBadWritePtr((void *)(val), sizeof(void *)) \
|| IsBadCodePtr( FARPROC( *((DWORD **)val)) ) ) \
{ DPRINTF( ("BAD INTERFACE: %s!\n", #val ) ); \
return E_POINTER; }
// Simple assert. Map this to whatever general
// framework assert you want.
#define UOSASSERT(x) { if(!(x)) dprintf( "Assert in URLOSTRM API: %s\n", #x ); }
#define DUOS OutputDebugString( "URLOSTRM API: " );
#define DPRINTF(x) DUOS dprintf x ;
#ifndef CHECK_METHOD
#define CHECK_METHOD( m, args ) DUOS dprintf( "(%#08x) %s(", this, #m ); dprintf args ; dprintf(")\n");
#endif
#ifndef MEMPRINTF
#define MEMPRINTF(x) DPRINTF(x)
#endif
void dprintf( char * format, ... )
{
char out[1024];
va_list marker;
va_start(marker, format);
wvsprintf(out, format, marker);
va_end(marker);
OutputDebugString( out );
}
#else
#define CHECK_POINTER(x)
#define CHECK_INTERFACE(x)
#define CHECK_METHOD( m, args )
#define UOSASSERT(x)
#define DPRINTF(x)
#define MEMPRINTF(x)
#endif
//----------------------------------------------------------
//
// Local heap stuff
//
//----------------------------------------------------------
// Keeping this here makes this code portable to any .dll
static HANDLE g_hHeap;
#ifdef _DEBUG
// Uncomment the line below for Debug spew of memory stuff
//#define MONITER_MEMALLOC 1
#endif
#ifdef _DEBUG
static void * _cdecl
operator new( size_t size )
{
if( !g_hHeap )
g_hHeap = ::GetProcessHeap();
// Heap alloc is the fastest gun in the west
// for the type of allocations we do here.
void * p = HeapAlloc(g_hHeap, 0, size);
MEMPRINTF( ("operator new(%d) returns(%#08X)\n",size, DWORD(p)) );
return(p);
}
static void _cdecl
operator delete ( void *ptr)
{
MEMPRINTF( ("operator delete(%#08X)\n", DWORD(ptr) ) );
HeapFree(g_hHeap, 0, ptr);
}
#endif
//----------------------------------------------------------
//
// class CBuffer
//
//----------------------------------------------------------
// Generic CBuffer class for quick and dirty mem allocs.
// Caller must check return results.
class CBuffer
{
public:
CBuffer(ULONG cBytes);
~CBuffer();
void *GetBuffer();
private:
void * m_pBuf;
// we'll use this temp buffer for small cases.
//
char m_szTmpBuf[120];
unsigned m_fHeapAlloc:1;
};
inline
CBuffer::CBuffer(ULONG cBytes)
{
if( !g_hHeap )
g_hHeap = ::GetProcessHeap();
m_pBuf = (cBytes <= 120) ? m_szTmpBuf : HeapAlloc(g_hHeap, 0, cBytes);
m_fHeapAlloc = (cBytes > 120);
}
inline
CBuffer::~CBuffer()
{
if (m_pBuf && m_fHeapAlloc) HeapFree(g_hHeap, 0, m_pBuf);
}
inline
void * CBuffer::GetBuffer()
{
return m_pBuf;
}
//=--------------------------------------------------------------------------=
//
// String ANSI <-> WIDE helper macros
//
// This stuff stolen from marcwan...
//
// allocates a temporary buffer that will disappear when it goes out of scope
// NOTE: be careful of that -- make sure you use the string in the same or
// nested scope in which you created this buffer. people should not use this
// class directly. use the macro(s) below.
//
//=--------------------------------------------------------------------------=
#define MAKE_WIDE(ptrname) \
long __l##ptrname = (lstrlen(ptrname) + 1) * sizeof(WCHAR); \
CBuffer __CBuffer##ptrname(__l##ptrname); \
CHECK_POINTER(__CBuffer##ptrname.GetBuffer()); \
if( !__CBuffer##ptrname.GetBuffer()) \
return( E_OUTOFMEMORY ); \
MultiByteToWideChar(CP_ACP, 0, ptrname, -1, \
(LPWSTR)__CBuffer##ptrname.GetBuffer(), __l##ptrname); \
LPWSTR __w##ptrname = (LPWSTR)__CBuffer##ptrname.GetBuffer()
#define WIDE_NAME(ptrname) __w##ptrname
#define MAKE_ANSI(ptrname) \
long __l##ptrname = (lstrlenW(ptrname)*2 + 1) * sizeof(char); \
CBuffer __CBuffer##ptrname(__l##ptrname); \
CHECK_POINTER(__CBuffer##ptrname.GetBuffer()); \
if( !__CBuffer##ptrname.GetBuffer()) \
return( E_OUTOFMEMORY ); \
WideCharToMultiByte(CP_ACP, 0, ptrname, -1, \
(LPSTR)__CBuffer##ptrname.GetBuffer(), __l##ptrname, NULL, NULL); \
LPSTR __a##ptrname = (LPSTR)__CBuffer##ptrname.GetBuffer()
#define ANSI_NAME(ptrname) __a##ptrname
//----------------------------------------------------------
//
// Misc helper functions
//
//----------------------------------------------------------
// These registry functions are here for support of backdoor
// flags and screamer features.
static HRESULT
GetRegDword( HKEY mainkey, LPCTSTR subkey, LPCTSTR valueName, DWORD * result )
{
HKEY hkey = 0;
DWORD dwDisposition;
LONG dwResult = RegCreateKeyEx(
mainkey, subkey,
0, // DWORD Reserved, // reserved
0, // LPTSTR lpClass, // address of class string
REG_OPTION_NON_VOLATILE, // DWORD dwOptions, // special options flag
KEY_ALL_ACCESS, // REGSAM samDesired, // desired security access
0, // LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of key security structure
&hkey, // PHKEY phkResult, // address of buffer for opened handle
&dwDisposition // LPDWORD lpdwDisposition // address of disposition value buffer
);
HRESULT hr = dwResult == ERROR_SUCCESS ? NOERROR : E_FAIL;
if( SUCCEEDED(hr) )
{
DWORD dwType;
DWORD dwSize = sizeof(DWORD);
DWORD dwSavedResult = *result;
dwResult = RegQueryValueEx(
hkey, // handle of key to query
valueName,
0, // LPDWORD lpReserved, // reserved
&dwType, // LPDWORD lpType, // address of buffer for value type
(LPBYTE)result, // LPBYTE lpData, // address of data buffer
&dwSize // LPDWORD lpcbData // address of data buffer size
);
hr = dwResult == ERROR_SUCCESS ? NOERROR : E_FAIL;
if( FAILED(hr) )
*result = dwSavedResult;
}
if( hkey )
RegCloseKey(hkey);
return(hr);
}
static HRESULT
GetDLMRegDWord( LPCTSTR valueName, DWORD * result )
{
return(GetRegDword( HKEY_LOCAL_MACHINE,
_TEXT("Software\\Microsoft\\DownloadManager"),
valueName,
result ) );
}
static HRESULT
MyCreateFile( LPCWSTR filename, HANDLE & hfile )
{
// BUGBUG: in retrospect this should be a ansi function
// not a wide string one.
HRESULT hr = NOERROR;
/**********
MAKE_ANSI( filename );
hfile = ::CreateFileA(
ANSI_NAME(filename), // LPCTSTR lpFileName, // pointer to name of the file
GENERIC_WRITE, // DWORD dwDesiredAccess, // access (read-write) mode
0, // DWORD dwShareMode, // share mode
0, // LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security descriptor
CREATE_ALWAYS, // DWORD dwCreationDistribution, // how to create
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes, // file attributes
0 // HANDLE hTemplateFile // handle to file with attributes to copy
);
*************/
hfile = CreateFileWrapW(
filename,
GENERIC_WRITE,
0,
0,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0
);
// Our code likes HRESULT style error handling
if( hfile == INVALID_HANDLE_VALUE )
hr = MK_E_CANTOPENFILE;
return(hr);
}
//----------------------------------------------------------
//
// BindStatusCallback base class
//
//----------------------------------------------------------
//
// This is the base class for the download objects. It implements
// the url mon callback interface (IBindStatusCallback) and
// IServiceProvider -- which it delegates to the caller's IBSCB.
//
// State flags
class CBaseBSCB : public IBindStatusCallbackMsg,
public IServiceProvider
{
public:
CBaseBSCB( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback );
virtual ~CBaseBSCB();
STDMETHOD(KickOffDownload)( LPCWSTR szURL );
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObjOut);
IMPLEMENT_REFCOUNT(CBaseBSCB);
// IBindStatusCallback
STDMETHODIMP OnStartBinding(
DWORD grfBSCOption,
IBinding *pib);
STDMETHODIMP GetPriority(
LONG *pnPriority);
STDMETHODIMP OnLowResource(
DWORD reserved);
STDMETHODIMP OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR szStatusText);
STDMETHODIMP OnDataAvailable(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed);
STDMETHODIMP OnStopBinding(
HRESULT hresult,
LPCWSTR szError);
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
STDMETHODIMP OnObjectAvailable(
REFIID riid,
IUnknown *punk);
STDMETHODIMP MessagePending(
DWORD dwPendingType,
DWORD dwPendingRecursion,
DWORD dwReserved);
// IServiceProvider
STDMETHODIMP QueryService(
REFGUID rsid,
REFIID iid,
void **ppvObj);
// Local methods
void Abort();
BOOL IsAborted();
BOOL DownloadDone();
HRESULT FinalResult();
void SetEncodingFlags( ULONG flags );
IUnknown * Caller();
// I guess at one point I thought it would be cool
// to make all of these inlines and isolated from
// the core functionality.
HRESULT SignalOnData( DWORD flags, ULONG size, FORMATETC *pformatetc);
HRESULT SignalOnProgress( ULONG status, ULONG size, ULONG maxSize, LPCWSTR msg );
HRESULT SignalOnStopBinding( HRESULT hr, LPCWSTR msg );
HRESULT SignalOnStartBinding( DWORD grfBSCOption, IBinding *pib);
HRESULT SignalOnGetPriority(LONG*);
HRESULT SignalOnLowResource(DWORD);
HRESULT SignalGetBindInfo(DWORD *grfBINDF,BINDINFO *pbindinfo);
virtual void Neutralize();
ULONG m_ref;
LPUOSCALLBACK m_callback;
IUnknown * m_caller;
IBinding * m_binding;
IServiceProvider * m_callbackServiceProvider;
IBindStatusCallbackMsg *_pBSCBMsg;
BOOL m_bInAbort : 1;
BOOL m_bInCache : 1;
BOOL m_bCheckedForServiceProvider : 1;
DWORD m_readSoFar;
HRESULT m_finalResult;
// See notes above about IStream usage
IStream * m_UserStream;
ULONG m_maxSize;
DWORD m_bscoFlags;
UINT m_encoding;
char m_szCacheFileName[MAX_PATH];
};
/*---------------------*/
/* INLINES */
/*---------------------*/
inline void CBaseBSCB::Abort() { m_bInAbort = 1; }
inline BOOL CBaseBSCB::IsAborted() { return(m_bInAbort); }
inline HRESULT CBaseBSCB::FinalResult(){ return( m_finalResult ); }
inline IUnknown*CBaseBSCB::Caller() { return( m_caller ); }
inline void CBaseBSCB::SetEncodingFlags( ULONG flags ) { m_encoding = flags; }
inline HRESULT
CBaseBSCB::SignalOnData( DWORD flags, ULONG size, FORMATETC *pformatetc )
{
HRESULT hr=NOERROR;
if(m_bscoFlags!=URLOSTRM_NOTIFY_ONDATA)
return(hr);
STGMEDIUM stg;
stg.tymed = TYMED_ISTREAM;
stg.pstm = m_UserStream;
stg.pUnkForRelease = NULL;
if(m_callback)
hr=m_callback->OnDataAvailable(flags,size,pformatetc,&stg);
return(hr);
}
inline HRESULT
CBaseBSCB::SignalOnProgress( ULONG status, ULONG size, ULONG maxSize, LPCWSTR msg )
{
if( !m_callback )
return(NOERROR);
if( size && !maxSize )
maxSize = size;
if( maxSize > m_maxSize )
m_maxSize = maxSize;
HRESULT hr = m_callback->OnProgress( size, m_maxSize, status, msg );
return(hr);
}
inline HRESULT
CBaseBSCB::SignalOnStopBinding( HRESULT hres, LPCWSTR msg )
{
if( !m_callback )
return(NOERROR);
HRESULT hr = m_callback->OnStopBinding( hres, msg );
return(hr);
}
inline HRESULT
CBaseBSCB::SignalOnStartBinding( DWORD grfBSCOption, IBinding *pib)
{
if( !m_callback )
return(NOERROR);
return( m_callback->OnStartBinding(grfBSCOption,pib) );
}
inline HRESULT
CBaseBSCB:: SignalOnGetPriority(LONG* lng)
{
if( !m_callback )
return(E_NOTIMPL);
return(m_callback->GetPriority(lng));
}
inline HRESULT
CBaseBSCB:: SignalOnLowResource(DWORD dw)
{
if( !m_callback )
return( NOERROR );
return( m_callback->OnLowResource(dw) );
}
inline HRESULT
CBaseBSCB::SignalGetBindInfo(DWORD *grfBINDF, BINDINFO * pbindinfo)
{
if( !m_callback )
return(E_NOTIMPL);
return( m_callback->GetBindInfo(grfBINDF, pbindinfo) );
}
/*---------------------*/
/* OUT-OF-LINES */
/*---------------------*/
// Do nothing CTOR
CBaseBSCB::CBaseBSCB
(
IUnknown * caller,
DWORD bscof,
LPUOSCALLBACK callback
)
{
m_binding = 0;
m_ref = 0;
m_bInAbort = 0;
m_bCheckedForServiceProvider = 0;
m_bInCache = 0;
m_readSoFar = 0;
m_UserStream = 0;
m_encoding = 0;
m_bscoFlags = bscof;
m_callbackServiceProvider = 0;
m_szCacheFileName[0] = NULL;
m_finalResult = S_OK;
_pBSCBMsg = 0;
if( (m_callback = callback) != 0 )
m_callback->AddRef();
if( (m_caller = caller) != 0 )
caller->AddRef();
}
// Cleanup just call Neutralize();
CBaseBSCB::~CBaseBSCB()
{
Neutralize();
}
void
CBaseBSCB::Neutralize()
{
if( m_binding )
{
m_binding->Release();
m_binding = 0;
}
if( m_caller )
{
m_caller->Release();
m_caller = 0;
}
if( m_callback )
{
m_callback->Release();
m_callback = 0;
}
if( m_callbackServiceProvider )
{
m_callbackServiceProvider->Release();
m_callbackServiceProvider = 0;
}
if( m_UserStream )
{
m_UserStream->Release();
m_UserStream = 0;
}
if (_pBSCBMsg)
{
_pBSCBMsg->Release();
}
}
// IUnknown::QueryInterface
STDMETHODIMP
CBaseBSCB::QueryInterface
(
const GUID &iid,
void ** ppv
)
{
CHECK_METHOD(CBaseBSCB::QueryInterface, ("") );
if (iid==IID_IUnknown || iid==IID_IBindStatusCallback)
{
*ppv =(IBindStatusCallback*)this;
AddRef();
return(NOERROR);
}
if( iid==IID_IServiceProvider)
{
*ppv =(IServiceProvider*)this;
AddRef();
return(NOERROR);
}
if (iid==IID_IBindStatusCallbackMsg)
{
*ppv =(IBindStatusCallbackMsg*)this;
AddRef();
return(NOERROR);
}
return( E_NOINTERFACE );
}
// IServiceProvider::QueryService
STDMETHODIMP
CBaseBSCB::QueryService
(
REFGUID rsid,
REFIID iid,
void **ppvObj
)
{
CHECK_METHOD(CBaseBSCB::QueryService, ("") );
HRESULT hr = E_NOINTERFACE;
if (iid==IID_IBindStatusCallback)
{
*ppvObj =(IBindStatusCallbackMsg*)this;
AddRef();
return(NOERROR);
}
if( m_callback )
hr = m_callback->QueryInterface( iid, ppvObj );
if( FAILED(hr) && !m_callbackServiceProvider && !m_bCheckedForServiceProvider )
{
m_bCheckedForServiceProvider = 1;
if( m_callback )
{
hr = m_callback->QueryInterface
(
IID_IServiceProvider,
(void**)&m_callbackServiceProvider
);
}
if( SUCCEEDED(hr) && m_callbackServiceProvider )
hr = m_callbackServiceProvider->QueryService(rsid,iid,ppvObj);
else
hr = E_NOINTERFACE; // BUGBUG: what's that error code again?
}
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::OnStartBinding
STDMETHODIMP
CBaseBSCB::OnStartBinding
(
DWORD grfBSCOption,
IBinding *pib
)
{
CHECK_METHOD(CBaseBSCB::OnStartBinding, ("flags: %#08x, IBinding: %#08x",grfBSCOption,pib) );
CHECK_INTERFACE(pib);
HRESULT hr = SignalOnStartBinding(grfBSCOption,pib);
// smooth over user's e_not_implemented for when we
// return to urlmon
if( hr == E_NOTIMPL )
hr = NOERROR;
else
HANDLE_ABORT(hr);
if( SUCCEEDED(hr) )
{
pib->AddRef();
m_binding = pib;
}
return( hr );
}
// IBindStatusCallback::GetPriority
STDMETHODIMP
CBaseBSCB::GetPriority
(
LONG *pnPriority
)
{
CHECK_METHOD(CBaseBSCB::GetPriority, ("pnPriority: %#08x", pnPriority) );
CHECK_POINTER(pnPriority);
if (!pnPriority)
return E_POINTER;
HRESULT hr = SignalOnGetPriority(pnPriority);
if( hr == E_NOTIMPL )
{
// only override if caller doesn't implement.
*pnPriority = NORMAL_PRIORITY_CLASS;
hr = NOERROR;
}
else
{
HANDLE_ABORT(hr);
}
return( hr );
}
// IBindStatusCallback::OnLowResource
STDMETHODIMP
CBaseBSCB::OnLowResource( DWORD rsv)
{
CHECK_METHOD(CBaseBSCB::OnLowResource, ("resv: %#08x",rsv) );
HRESULT hr = SignalOnLowResource(rsv);
// Keep downloading...
if( hr == E_NOTIMPL )
hr = NOERROR;
else
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::OnStopBinding
STDMETHODIMP
CBaseBSCB::OnStopBinding
(
HRESULT hresult,
LPCWSTR szError
)
{
CHECK_METHOD(CBaseBSCB::OnStopBinding, ("%#08X %ws", hresult, szError ? szError : L"[no error]" ) );
// Store the hresult so we can return it to caller in the
// blocking/sync case.
HRESULT hr = SignalOnStopBinding( m_finalResult = hresult, szError );
if( m_binding )
{
m_binding->Release();
m_binding = 0;
}
if( hr == E_NOTIMPL )
hr = NOERROR;
else
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::GetBindInfo
STDMETHODIMP
CBaseBSCB::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO* pbindinfo
)
{
CHECK_METHOD(CBaseBSCB::GetBindInfo, ("grfBINDF: %#08x, pbinfinfo ",grfBINDF) );
CHECK_POINTER(grfBINDF);
CHECK_POINTER(pbindinfo);
*grfBINDF = 0;
HRESULT hr = SignalGetBindInfo(grfBINDF,pbindinfo);
if( SUCCEEDED(hr) || (hr == E_NOTIMPL) )
{
// Let the derived class choose the bind flags
if(m_encoding)
{
*grfBINDF |= m_encoding;
pbindinfo->grfBindInfoF |= m_encoding;
}
hr = NOERROR;
}
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::OnObjectAvailable
STDMETHODIMP
CBaseBSCB::OnObjectAvailable
(
REFIID riid,
IUnknown *punk
)
{
// This should never be called
CHECK_METHOD(CBaseBSCB::OnObjectAvailable, ("!") );
UOSASSERT(0 && "This should never be called");
return(NOERROR);
}
STDMETHODIMP
CBaseBSCB::OnProgress
(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR szStatusText
)
{
CHECK_METHOD(CBaseBSCB::OnProgress, ("!") );
// URL moniker has a habit of passing ZERO
// into ulProgressMax. So.. let's at least
// pass in the amount we have so far...
m_maxSize = ulProgressMax ? ulProgressMax : ulProgress;
// This is useful information for the IStream implementation
if( ulStatusCode == BINDSTATUS_USINGCACHEDCOPY )
m_bInCache = TRUE;
HRESULT hr;
hr = SignalOnProgress( ulStatusCode, ulProgress, ulProgressMax, szStatusText );
if( hr == E_NOTIMPL )
hr = NOERROR;
else
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::OnDataAvailable.
STDMETHODIMP
CBaseBSCB::OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed
)
{
CHECK_METHOD(CBaseBSCB::OnDataAvailable,
("Flags: %x, dwSize: %d", grfBSCF, dwSize) );
HRESULT hr = NOERROR;
// N.B Assumption here is that the pstgmed->pstm will always be the same
if( !m_UserStream )
{
// We need to bump the refcount every time we
// copy and store the pointer.
m_UserStream = pstgmed->pstm;
m_UserStream->AddRef();
}
if (*m_szCacheFileName == NULL)
{
STATSTG statstg;
DWORD dwVal = 0;
if (m_UserStream->Stat(&statstg,dwVal) == S_OK)
{
if (0==WideCharToMultiByte( CP_ACP, 0, statstg.pwcsName, lstrlenW(statstg.pwcsName)+1, m_szCacheFileName,
MAX_PATH, NULL, NULL))
{
m_szCacheFileName[0] = NULL;
}
if (statstg.pwcsName)
{
CoTaskMemFree(statstg.pwcsName);
statstg.pwcsName = NULL;
}
}
else
m_szCacheFileName[0] = NULL;
}
hr = SignalOnData( grfBSCF, dwSize, pformatetc );
// Tell the blocking state machine we are have data.
// ClearState( WAITING_FOR_DATA );
if( hr == E_NOTIMPL )
hr = NOERROR;
else
HANDLE_ABORT(hr);
return( hr );
}
// IBindStatusCallback::MessagePending.
STDMETHODIMP CBaseBSCB::MessagePending(DWORD dwPendingType, DWORD dwPendingRecursion, DWORD dwReserved)
{
MSG msg;
HRESULT hr = NOERROR;
if (m_callback && !_pBSCBMsg)
{
hr = m_callback->QueryInterface(IID_IBindStatusCallbackMsg, (void **) &_pBSCBMsg);
}
if (_pBSCBMsg && hr == NOERROR )
{
hr = _pBSCBMsg->MessagePending(dwPendingType, dwPendingRecursion, dwReserved );
}
return hr;
}
HRESULT
CBaseBSCB::KickOffDownload( LPCWSTR szURL )
{
HRESULT hr;
IOleObject * pOleObject = 0;
IServiceProvider * pServiceProvider = 0;
BOOL bUseCaller = (Caller() != 0);
IMoniker * pmkr = 0;
IBindCtx * pBndCtx = 0;
CHECK_POINTER(szURL);
UOSASSERT(*szURL);
IStream * pstrm = 0;
// Don't bother if we don't have a caller...
if( bUseCaller )
{
// By convention the we give the caller first crack at service
// provider. The assumption here is that if they implement it
// they have the decency to forward QS's to their container.
hr = Caller()->QueryInterface( IID_IServiceProvider,
(void**)&pServiceProvider );
if( FAILED(hr) )
{
// Ok, now try the 'slow way' : maybe the object is an 'OLE' object
// that knows about it's client site:
hr = Caller()->QueryInterface( IID_IOleObject, (void**)&pOleObject );
if( SUCCEEDED(hr) )
{
IOleClientSite * pClientSite = 0;
hr = pOleObject->GetClientSite(&pClientSite);
if( SUCCEEDED(hr) )
{
// Now see if we have a service provider at that site
hr = pClientSite->QueryInterface
( IID_IServiceProvider,
(void**)&pServiceProvider );
}
if( pClientSite )
pClientSite->Release();
}
else
{
// Ok, it's not an OLE object, maybe it's one of these
// new fangled 'ObjectWithSites':
IObjectWithSite * pObjWithSite = 0;
hr = Caller()->QueryInterface( IID_IObjectWithSite,
(void**)&pObjWithSite );
if( SUCCEEDED(hr) )
{
// Now see if we have a service provider at that site
hr = pObjWithSite->GetSite(IID_IServiceProvider,
(void**)&pServiceProvider);
}
if( pObjWithSite )
pObjWithSite->Release();
}
if( pOleObject )
pOleObject->Release();
}
// BUGBUG: In the code above we stop looking at one level up --
// this may be too harsh and we should loop on client sites
// until we get to the top...
if( !pServiceProvider )
hr = E_UNEXPECTED;
IBindHost * pBindHost = 0;
// Ok, we have a service provider, let's see if BindHost is
// available. (Here there is some upward delegation going on
// via service provider).
if( SUCCEEDED(hr) )
hr = pServiceProvider->QueryService( SID_SBindHost, IID_IBindHost,
(void**)&pBindHost );
if( pServiceProvider )
pServiceProvider->Release();
pmkr = 0;
if( pBindHost )
{
// This allows the container to actually drive the download
// by creating it's own moniker.
hr = pBindHost->CreateMoniker( LPOLESTR(szURL),NULL, &pmkr,0 );
if( SUCCEEDED(hr) )
{
// This allows containers to hook the download for
// doing progress and aborting
hr = pBindHost->MonikerBindToStorage(pmkr, NULL, this, IID_IStream,(void**)&pstrm);
}
pBindHost->Release();
}
else
{
bUseCaller = 0;
}
}
if( !bUseCaller )
{
// If you are here, then either the caller didn't pass
// a 'caller' pointer or the caller is not in a BindHost
// friendly environment.
hr = ::CreateURLMoniker( 0, szURL, &pmkr );
if( SUCCEEDED(hr) )
hr = ::CreateBindCtx( 0, &pBndCtx );
if( SUCCEEDED(hr) )
{
// Register US (not the caller) as the callback. This allows
// us to hook all notfiications from URL moniker and filter
// and manipulate to our satifisfaction.
hr = ::RegisterBindStatusCallback( pBndCtx, this, 0, 0L );
}
if( SUCCEEDED(hr) )
{
hr = pmkr->BindToStorage( pBndCtx, NULL, IID_IStream, (void**)&pstrm );
// Smooth out the error code
if( IS_E_PENDING(hr) )
hr = S_OK;
}
}
if( pstrm )
pstrm->Release();
if( pmkr )
pmkr->Release();
if( pBndCtx )
pBndCtx->Release();
return(hr);
}
//----------------------------------------------------------
//
// CPullDownload
//
//----------------------------------------------------------
// placeholder for covering the URL moniker anomolies
class CPullDownload : public CBaseBSCB
{
public:
CPullDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback );
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
};
inline
CPullDownload::CPullDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback )
: CBaseBSCB(caller,bscof,callback)
{
}
STDMETHODIMP
CPullDownload::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO * pbindinfo
)
{
// pointers are validated in base class
HRESULT hr = CBaseBSCB::GetBindInfo( grfBINDF, pbindinfo );
if( SUCCEEDED(hr))
{
if (*grfBINDF & BINDF_ENFORCERESTRICTED)
*grfBINDF = BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_ASYNCHRONOUS | BINDF_ENFORCERESTRICTED;
else
*grfBINDF = BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_ASYNCHRONOUS;
}
return(hr);
}
//----------------------------------------------------------
//
// Push Stream API
//
//----------------------------------------------------------
//
// Class used for implementing push model downloading when used
// in combination with the CStream object.
//
// The general design for is this class pumps a
// CBitBucket object with bits and the CStream object makes
// those bits available to the caller for reading.
//
class CPushDownload : public CBaseBSCB
{
public:
CPushDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback );
~CPushDownload();
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
protected:
// CBaseBSCB
virtual void Neutralize();
// IBindStatusCallback
STDMETHODIMP OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC * pFmtetc,
STGMEDIUM * pstgmed
) ;
private:
HRESULT CleanupPush();
};
CPushDownload::CPushDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback )
: CBaseBSCB(caller,bscof, callback)
{
}
CPushDownload::~CPushDownload()
{
CleanupPush();
}
void
CPushDownload::Neutralize()
{
// We have to do special cleanup.
CleanupPush();
CBaseBSCB::Neutralize();
}
HRESULT
CPushDownload::CleanupPush()
{
return(NOERROR);
}
STDMETHODIMP
CPushDownload::OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC * pFmtetc,
STGMEDIUM * pstgmed
)
{
HRESULT hr = NOERROR;
if( SUCCEEDED(hr) && pstgmed->pstm )
{
m_UserStream = pstgmed->pstm;
// Add ref again because we are copying and storing the ptr
m_UserStream->AddRef();
}
if( SUCCEEDED(hr) || IS_E_PENDING(hr) )
hr = CBaseBSCB::OnDataAvailable(grfBSCF,dwSize,pFmtetc,pstgmed);
return(hr);
}
STDMETHODIMP
CPushDownload::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO * pbindinfo
)
{
HRESULT hr = CBaseBSCB::GetBindInfo( grfBINDF, pbindinfo );
// PushDownload can not be ASYNC
if (*grfBINDF & BINDF_ENFORCERESTRICTED)
*grfBINDF = BINDF_ENFORCERESTRICTED;
else
*grfBINDF = 0;
return(hr);
}
//----------------------------------------------------------
//
// Block Stream API
//
//----------------------------------------------------------
class CBlockDownload : public CPushDownload
{
public:
CBlockDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback );
~CBlockDownload();
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
HRESULT GetStream( IStream ** ppStream );
};
inline
CBlockDownload::CBlockDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback )
: CPushDownload(caller,bscof, callback)
{
}
template <class T> inline
HRESULT CheckThis( T * AThisPtr )
{
CHECK_INTERFACE(AThisPtr);
return(NOERROR);
}
CBlockDownload::~CBlockDownload()
{
CheckThis(this);
}
HRESULT
CBlockDownload::GetStream( IStream ** ppStream )
{
// REMEMBER: If you get this pointer and return it
// to caller YOU MUST add ref it before handing
// it back via an API
HRESULT hr = E_FAIL;
if( m_UserStream )
{
*ppStream = m_UserStream;
hr = S_OK;
}
return( hr );
}
STDMETHODIMP
CBlockDownload::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO * pbindinfo
)
{
HRESULT hr = CBaseBSCB::GetBindInfo( grfBINDF, pbindinfo );
return(hr);
}
//----------------------------------------------------------
//
// Download to file
//
//----------------------------------------------------------
//
// This class implements the File downloading code. It reads from the
// stream from urlmon and writes every buffer directly to disk.
//
class CFileDownload : public CBaseBSCB
{
public:
CFileDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback, LPCWSTR szFileName=0);
~CFileDownload();
void SetFileName(LPCWSTR);
STDMETHODIMP OnDataAvailable(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed);
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
STDMETHOD(KickOffDownload)( LPCWSTR szURL );
virtual void Neutralize();
private:
HRESULT Cleanup();
unsigned char * m_buffer;
unsigned long m_bufsize;
HANDLE m_file;
LPCWSTR m_filename;
ULONG m_okFromCache;
};
inline void
CFileDownload::SetFileName(LPCWSTR newFileName)
{
// ASSUMES Calls to this class are synchronous
m_filename = newFileName;
}
CFileDownload::CFileDownload
(
IUnknown * caller,
DWORD bscof,
LPUOSCALLBACK callback,
LPCWSTR szFileName
)
: CBaseBSCB(caller, bscof, callback)
{
m_buffer = 0;
m_bufsize = 0;
m_file = INVALID_HANDLE_VALUE;
m_filename = szFileName;
m_okFromCache = 0;
}
CFileDownload::~CFileDownload()
{
Cleanup();
}
STDMETHODIMP
CFileDownload::KickOffDownload( LPCWSTR szURL )
{
// MAGIC: registry flag determines whether we
// nuke this guy from the cache or not
GetDLMRegDWord( _TEXT("CacheOk"), &m_okFromCache );
return( CBaseBSCB::KickOffDownload(szURL) );
}
HRESULT CFileDownload::Cleanup()
{
if( m_buffer )
{
delete m_buffer;
m_buffer = 0;
}
if( m_file != INVALID_HANDLE_VALUE )
{
CloseHandle(m_file);
m_file = INVALID_HANDLE_VALUE;
}
return(NOERROR);
}
STDMETHODIMP
CFileDownload::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO * pbindinfo
)
{
HRESULT hr = CBaseBSCB::GetBindInfo( grfBINDF, pbindinfo );
if( SUCCEEDED(hr) && !m_okFromCache )
{
if (*grfBINDF & BINDF_ENFORCERESTRICTED)
*grfBINDF = BINDF_ENFORCERESTRICTED | BINDF_PULLDATA;
else
*grfBINDF = BINDF_PULLDATA;
}
return(hr);
}
STDMETHODIMP
CFileDownload::OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed
)
{
// Pointers are validated in base class
HRESULT hr = CBaseBSCB::OnDataAvailable(grfBSCF,dwSize,pformatetc,pstgmed);
if( FAILED(hr) || (dwSize == m_readSoFar) )
return(hr);
if( (m_file == INVALID_HANDLE_VALUE) && dwSize )
{
CHECK_POINTER(m_filename);
hr = MyCreateFile(m_filename,m_file);
if( FAILED(hr) )
hr = E_ABORT;
}
HANDLE_ABORT(hr);
UOSASSERT( (m_file != INVALID_HANDLE_VALUE) );
// Only allocate a read buffer if the one we have is not
// big enough.
if( m_buffer && (m_bufsize < (dwSize- m_readSoFar+1)) )
{
delete m_buffer;
m_buffer = 0;
m_bufsize=0;
}
if( !m_buffer )
{
m_bufsize=dwSize- m_readSoFar+1;
DPRINTF( ("Allocating read buffer %d\n",m_bufsize) );
m_buffer = new unsigned char [(dwSize- m_readSoFar+1)];
}
CHECK_MEMORY(m_buffer);
DWORD dwReadThisMuch = dwSize - m_readSoFar;
DWORD dwActual = 0;
DWORD dwCurrentRead = 0;
unsigned char * temp = m_buffer;
do
{
hr = m_UserStream->Read(temp,dwReadThisMuch,&dwActual);
dwCurrentRead += dwActual;
dwReadThisMuch -= dwActual;
temp += dwActual;
}
while (!(hr == S_FALSE || hr == E_PENDING) && SUCCEEDED(hr));
if( dwCurrentRead )
{
m_readSoFar += (dwReadThisMuch = dwCurrentRead);
BOOL bWriteOk = ::WriteFile(
m_file, // HANDLE hFile, // handle to file to write to
m_buffer, // LPCVOID lpBuffer, // pointer to data to write to file
dwReadThisMuch, // DWORD nNumberOfBytesToWrite, // number of bytes to write
&dwActual, // pointer to number of bytes written
0 ); // LPOVERLAPPED lpOverlapped // addr. of structure needed for overlapped
if( !bWriteOk )
hr = E_FAIL;
}
// PUMPREAD(pstgmed->pstm);
if (grfBSCF & BSCF_LASTDATANOTIFICATION)
{
CloseHandle(m_file);
m_file = INVALID_HANDLE_VALUE;
}
return( hr );
}
void
CFileDownload::Neutralize()
{
// We have to do special cleanup.
Cleanup();
CBaseBSCB::Neutralize();
}
//----------------------------------------------------------
//
// Download to Cache file
// Implementation of the CCacheFileDownload
//
//----------------------------------------------------------
// This class downloads the file to the cache and returns the cache file name
class CCacheFileDownload : public CBaseBSCB
{
public:
CCacheFileDownload( IUnknown * caller, DWORD bscof, LPUOSCALLBACK callback, LPCWSTR szFileName=0);
~CCacheFileDownload();
STDMETHODIMP OnDataAvailable(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed);
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO *pbindinfo);
STDMETHOD(KickOffDownload)( LPCWSTR szURL );
private:
DWORD m_readSoFar;
};
CCacheFileDownload::CCacheFileDownload
(
IUnknown * caller,
DWORD bscof,
LPUOSCALLBACK callback,
LPCWSTR szFileName
)
: CBaseBSCB(caller, bscof, callback)
{
m_readSoFar=0;
}
CCacheFileDownload::~CCacheFileDownload()
{
// Cleanup();
}
STDMETHODIMP
CCacheFileDownload::KickOffDownload( LPCWSTR szURL )
{
return( CBaseBSCB::KickOffDownload(szURL) );
}
STDMETHODIMP
CCacheFileDownload::GetBindInfo
(
DWORD * grfBINDF,
BINDINFO * pbindinfo
)
{
HRESULT hr = CBaseBSCB::GetBindInfo( grfBINDF, pbindinfo );
*grfBINDF &= ~BINDF_ASYNCHRONOUS; //to fix MSN 5.0 bug 103719.
return(hr);
}
#define READBLOCKSIZE 8192
STDMETHODIMP
CCacheFileDownload::OnDataAvailable
(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC *pformatetc,
STGMEDIUM *pstgmed
)
{
// Pointers are validated in base class
HRESULT hr = CBaseBSCB::OnDataAvailable(grfBSCF,dwSize,pformatetc,pstgmed);
if( FAILED(hr) || (dwSize == m_readSoFar) )
return(hr);
IStream * pstm = pstgmed->pstm;
if (pstm && dwSize > m_readSoFar)
{
DWORD dwToRead = dwSize - m_readSoFar;
DWORD dwActuallyRead = 1; //initialize to force it into loop
char* lp = NULL;
lp = new char[READBLOCKSIZE+1];
CHECK_MEMORY(lp);
while (dwActuallyRead)
{
dwActuallyRead=0;
hr = pstm->Read(lp, READBLOCKSIZE, &dwActuallyRead);
if(hr!=S_OK && hr!=E_PENDING) // If Read Fails then return Error
break;
m_readSoFar += dwActuallyRead;
}
delete lp;
}
return (hr);
}
STDAPI
URLOpenPullStreamW
(
LPUNKNOWN caller,
LPCWSTR szURL,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
CHECK_POINTER(szURL);
HRESULT hr;
CPullDownload * download = new CPullDownload(caller,URLOSTRM_NOTIFY_ONDATA,callback);
CHECK_POINTER(download);
if( !download )
hr = E_OUTOFMEMORY;
else
{
download->SetEncodingFlags(dwReserved);
hr = download->KickOffDownload(szURL);
}
return(hr);
}
STDAPI
URLDownloadToFileW
(
LPUNKNOWN caller,
LPCWSTR szURL,
LPCWSTR szFileName,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
CHECK_POINTER(szURL);
HRESULT hr;
CFileDownload * strm = new CFileDownload(caller,URLOSTRM_DONOT_NOTIFY_ONDATA,callback,szFileName);
CHECK_POINTER(strm);
if( !strm )
hr = E_OUTOFMEMORY;
else
{
strm->AddRef(); // So that we have valid handle even after OnStopBinding()
strm->SetEncodingFlags(dwReserved);
hr = strm->KickOffDownload(szURL);
}
if (strm)
strm->Release();
return(hr);
}
extern void DoThreadCleanup(BOOL bInThreadDetach);
extern BOOL g_bNT5OrGreater;
STDAPI
URLDownloadToCacheFileW
(
LPUNKNOWN caller,
LPCWSTR szURL,
LPWSTR szFileName,
DWORD dwBufLength,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
CHECK_POINTER(szURL);
HRESULT hr=S_OK;
BOOL fFileURL = FALSE;
if (dwBufLength <= 0)
{
hr = E_OUTOFMEMORY; // Buffer length invalid
return hr;
}
//
// 1,2, and 3 are reserved for the constants
// URLOSTRM_USECACHE, CACHEONLY and GETNEWESTVERSION constants
// For Compatibility with previous versions
//
dwBufLength = (dwBufLength < 4) ? MAX_PATH : dwBufLength;
szFileName[0] = NULL;
// For Cache calls
MAKE_ANSI(szURL);
CCacheFileDownload * strm = new CCacheFileDownload(caller,dwBufLength,callback,szFileName);
CHECK_POINTER(strm);
if( !strm )
{
hr = E_OUTOFMEMORY;
}
else
{
strm->AddRef(); // So that we have valid handle even after OnStopBinding()
strm->SetEncodingFlags(dwReserved);
hr = strm->KickOffDownload(szURL);
}
if(SUCCEEDED(hr))
{
if (*strm->m_szCacheFileName)
{
// If it is a file URL we have to convert it to WIN32 file
CHAR szPath[MAX_PATH];
DWORD cchPath = MAX_PATH;
if(SUCCEEDED(PathCreateFromUrl(strm->m_szCacheFileName, szPath, &cchPath, 0)))
{
fFileURL = TRUE;
// url should now look like a DOS path
if (0==MultiByteToWideChar(CP_ACP, 0,
szPath,-1,
szFileName, dwBufLength))
{
hr = E_OUTOFMEMORY;
szFileName[0] = NULL;
}
else
hr=S_OK;
}
if (!fFileURL)
{
if (0==MultiByteToWideChar(CP_ACP, 0, strm->m_szCacheFileName,
lstrlen(strm->m_szCacheFileName)+1,
szFileName, dwBufLength))
{
hr = E_OUTOFMEMORY;
szFileName[0] = NULL;
}
else
hr=S_OK;
}
}
else
hr = E_FAIL;
}
if (strm)
strm->Release();
// WinSE QFE #3411
// At a minimum, we may need to cleanup the notification hwnd created
// because this thread can become unresponsive if a broadcast message
// is sent and the client app isn't pumping messages on this thread.
// We'll go ahead an do a full tls cleanup.
// If this is NOT NT5 or greater, then our notification window is not a message window.
// Clean up our thread data if no other activity on this thread.
//
if (!g_bNT5OrGreater)
{
DoThreadCleanup(FALSE);
}
return(hr);
}
STDAPI
URLOpenBlockingStreamW
(
LPUNKNOWN caller,
LPCWSTR szURL,
LPSTREAM* ppStream,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
CHECK_POINTER(szURL);
CHECK_POINTER(ppStream);
HRESULT hr;
if (!ppStream)
return E_INVALIDARG;
CBlockDownload * strm = new CBlockDownload(caller,URLOSTRM_DONOT_NOTIFY_ONDATA,callback);
CHECK_POINTER(strm);
if( !strm )
hr = E_OUTOFMEMORY;
else
{
strm->AddRef();
strm->SetEncodingFlags(dwReserved);
hr = strm->KickOffDownload(szURL);
}
if( SUCCEEDED(hr) )
{
hr = strm->GetStream(ppStream);
// We add ref this pointer because we are handing
// it back to the user
if( SUCCEEDED(hr) )
(*ppStream)->AddRef();
}
if (strm)
strm->Release();
return(hr);
}
STDAPI
URLOpenStreamW
(
LPUNKNOWN caller,
LPCWSTR szURL,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
CHECK_POINTER(szURL);
HRESULT hr;
CPushDownload * strm = new CPushDownload(caller,URLOSTRM_NOTIFY_ONDATA,callback);
CHECK_POINTER(strm);
if( !strm )
hr = E_OUTOFMEMORY;
else
{
strm->SetEncodingFlags(dwReserved);
hr = strm->KickOffDownload(szURL);
}
return(hr);
}
//
// ANSI VERSION OF PUBLIC API
//
STDAPI
URLOpenPullStreamA
(
LPUNKNOWN caller,
LPCSTR szURL,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
MAKE_WIDE(szURL);
return( URLOpenPullStreamW(caller, WIDE_NAME(szURL), dwReserved, callback) );
}
STDAPI
URLDownloadToFileA
(
LPUNKNOWN caller,
LPCSTR szURL,
LPCSTR szFileName,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
MAKE_WIDE(szURL);
MAKE_WIDE(szFileName);
return( URLDownloadToFileW( caller, WIDE_NAME(szURL), WIDE_NAME(szFileName),dwReserved, callback ) );
}
STDAPI
URLDownloadToCacheFileA
(
LPUNKNOWN caller,
LPCSTR szURL,
LPSTR szFileName,
DWORD dwBufLength,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
HRESULT hr=E_OUTOFMEMORY;
if (dwBufLength <= 0)
return hr;
MAKE_WIDE(szURL);
LPWSTR lpwszfilename= new WCHAR[MAX_PATH];
if (lpwszfilename!=NULL)
{
hr=URLDownloadToCacheFileW( caller, WIDE_NAME(szURL), lpwszfilename, MAX_PATH, dwReserved, callback );
if (SUCCEEDED(hr))
{
// Convert to ANSI.
dwBufLength = (dwBufLength < 4) ? MAX_PATH : dwBufLength;
if (0==WideCharToMultiByte( CP_ACP, 0, lpwszfilename, lstrlenW(lpwszfilename)+1,szFileName,
dwBufLength, NULL, NULL))
{
hr = E_OUTOFMEMORY;
szFileName[0] = NULL;
}
}
else
szFileName[0] = NULL;
delete[] lpwszfilename;
}
return(hr);
}
STDAPI
URLOpenBlockingStreamA
(
LPUNKNOWN caller,
LPCSTR szURL,
LPSTREAM* ppStream,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
MAKE_WIDE(szURL);
return( URLOpenBlockingStreamW(caller,WIDE_NAME(szURL),ppStream,dwReserved,callback) );
}
STDAPI
URLOpenStreamA
(
LPUNKNOWN caller,
LPCSTR szURL,
DWORD dwReserved,
LPUOSCALLBACK callback
)
{
MAKE_WIDE(szURL);
return( URLOpenStreamW(caller,WIDE_NAME(szURL),dwReserved,callback) );
}