Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

914 lines
25 KiB

#include <windows.h>
#include <ole2.h>
#include <stdio.h>
#include <urlmon.h>
#include <string.h>
#include <malloc.h>
#include <urlmon.hxx>
#define IE5
#ifdef PRODUCT_PROF
extern "C" void _stdcall StartCAP(void);
extern "C" void _stdcall StopCAP(void);
extern "C" void _stdcall SuspendCAP(void);
extern "C" void _stdcall ResumeCAP(void);
extern "C" void _stdcall StartCAPAll(void);
extern "C" void _stdcall StopCAPAll(void);
#else
#define StartCAP()
#define StopCAP()
#define SuspendCAP()
#define ResumeCAP()
#define StartCAPAll()
#define StopCAPAll()
#endif
#define FLAG_TRACE 1
#define FLAG_DUMPDATA 2
#define MAX_DOWNLOADS 2000
#define MAX_URL INTERNET_MAX_URL_LENGTH
const INT BUF_SIZE = 2 * 1024;
const INT MAX_BUF_SIZE = 1024 * 16;
BOOL g_dwVerbose = 0;
DWORD g_dwNumDownloads = 1;
DWORD g_dwDownloads = 1;
DWORD g_dwTotalBytes = 0;
DWORD g_dwCacheFlag = BINDF_NOWRITECACHE | BINDF_GETNEWESTVERSION;
DWORD dwBuf_Size = BUF_SIZE;
LPWSTR g_pwzUrl = NULL;
LPCSTR g_pInfile = NULL;
LPCSTR g_pTitle = NULL;
LPCSTR g_pRun = NULL;
LPCSTR g_pModule = NULL;
HANDLE g_hCompleted;
__int64 g_ibeg = 0, g_iend, g_ifreq;
//------------------------------------------------------------------------
// Class: COInetProtocolHook
//
// Purpose: Sample Implementation of ProtocolSink and BindInfo
// interface for simplified urlmon async download
//
// Interfaces:
// [Needed For All]
// IOInetProtocolSink
// - provide sink for pluggable prot's callback
// IOInetBindInfo
// - provide the bind options
//
// [Needed For Http]
// IServiceProvider
// - used to query http specific services
// e.g. HttpNegotiate, Authentication, UIWindow
// IHttpNegotiate
// - http negotiation service, it has two methods,
// one is the http pluggable protocol asks the
// client for additional headers, the other is
// callback for returned http server status
// e.g 200, 401, etc.
//
//
// Author: DanpoZ (Danpo Zhang)
//
// History: 11-20-97 Created
// 05-19-98 Modified to act as performance test
//
// NOTE: IOInetXXXX == IInternetXXXX
// on the SDK, you will see IInternetXXXX, these are same
// interfaces
//
//------------------------------------------------------------------------
class COInetProtocolHook : public IOInetProtocolSink,
public IOInetBindInfo,
public IHttpNegotiate,
public IServiceProvider
{
public:
COInetProtocolHook(HANDLE g_hCompleted, IOInetProtocol* pProt);
virtual ~COInetProtocolHook();
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void **ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IOInetProtocolSink methods
STDMETHODIMP Switch( PROTOCOLDATA *pStateInfo);
STDMETHODIMP ReportProgress( ULONG ulStatusCode, LPCWSTR szStatusText);
STDMETHODIMP ReportData(
DWORD grfBSCF,
ULONG ulProgress,
ULONG ulProgressMax
);
STDMETHODIMP ReportResult(
HRESULT hrResult,
DWORD dwError,
LPCWSTR wzResult
);
//IOInetBindInfo methods
STDMETHODIMP GetBindInfo(
DWORD *grfBINDF,
BINDINFO * pbindinfo
);
STDMETHODIMP GetBindString(
ULONG ulStringType,
LPOLESTR *ppwzStr,
ULONG cEl,
ULONG *pcElFetched
);
//IService Provider methods
STDMETHODIMP QueryService(
REFGUID guidService,
REFIID riid,
void **ppvObj
);
//IHttpNegotiate methods
STDMETHODIMP BeginningTransaction(
LPCWSTR szURL,
LPCWSTR szHeaders,
DWORD dwReserved,
LPWSTR *pszAdditionalHeaders
);
STDMETHODIMP OnResponse(
DWORD dwResponseCode,
LPCWSTR szResponseHeaders,
LPCWSTR szRequestHeaders,
LPWSTR *pszAdditionalHeaders
);
private:
IOInetProtocol* _pProt;
HANDLE _hCompleted;
CRefCount _CRefs;
};
typedef struct tagInfo
{
WCHAR wzUrl[INTERNET_MAX_URL_LENGTH];
IOInetProtocol* pProt;
COInetProtocolHook* pHook;
IOInetProtocolSink* pSink;
IOInetBindInfo* pBindInfo;
} INFO, *PINFO;
typedef BOOL (WINAPI *PFNSPA)(HANDLE, DWORD);
INFO Info[MAX_DOWNLOADS];
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void MylstrcpyW(WCHAR *pwd, WCHAR *pws)
{
while (*pws)
{
*pwd++ = *pws++;
}
*pwd = 0;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
WCHAR *MyDupA2W(LPSTR pa)
{
int i;
WCHAR *pw, *pwd;
i = lstrlen(pa);
pw = (WCHAR *)CoTaskMemAlloc((i+1) * sizeof(WCHAR));
pwd = pw;
while (*pa)
{
*pwd++ = (WCHAR)*pa++;
}
*pwd++ = 0;
return pw;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void SetSingleProcessorAffinity()
{
PFNSPA pfn;
pfn = (PFNSPA)GetProcAddress(GetModuleHandleA("KERNEL32.DLL"),
"SetProcessAffinityMask");
if (pfn)
{
pfn(GetCurrentProcess(), 1);
}
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
VOID DisplayUsage(char **argv)
{
printf("Usage: %s /u:url [/n:# /t:Title /r:RunStr /v:#]\n", argv[0]);
printf(" %s /i:infile [/t:Title /r:RunStr /v:#]\n", argv[0]);
printf(" /c - write to cache (default is NOWRITECACHE)\n");
printf(" /g - read from cache (default is GETNEWESTVERSION)\n");
printf(" /d - direct read (default uses QueryDataAvailable)\n");
printf(" /l:# - read buffer length\n");
printf(" /m:module - pre load module\n");
printf(" /n:# - download # times.\n");
printf(" /1 - single processor affinity (default multiprocessor)\n");
printf(" /v:# - verbose level 1=trace 2=dump data\n");
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
BOOL ProcessCommandLine(int argcIn, char **argvIn)
{
BOOL bRC = FALSE;
int argc = argcIn;
char **argv = argvIn;
argv++; argc--;
if(argc == 0)
{
DisplayUsage(argvIn);
return(FALSE);
}
while( argc > 0 && argv[0][0] == '/' )
{
switch (argv[0][1])
{
case 'c':
case 'C':
g_dwCacheFlag &= ~BINDF_NOWRITECACHE;
break;
case 'g':
case 'G':
g_dwCacheFlag &= ~BINDF_GETNEWESTVERSION;
break;
case 'd':
case 'D':
g_dwCacheFlag |= BINDF_DIRECT_READ;
break;
case 'i':
case 'I':
g_pInfile = &argv[0][3];
bRC = TRUE;
break;
case 'l':
case 'L':
dwBuf_Size = atoi(&argv[0][3]);
if(dwBuf_Size > MAX_BUF_SIZE)
dwBuf_Size = MAX_BUF_SIZE;
break;
case 'm':
case 'M':
g_pModule = &argv[0][3];
break;
case 'n':
case 'N':
g_dwNumDownloads = (DWORD)atoi(&argv[0][3]);
g_dwNumDownloads = max(1, g_dwNumDownloads);
g_dwNumDownloads = min(MAX_DOWNLOADS, g_dwNumDownloads);
break;
case 'r':
case 'R':
g_pRun = &argv[0][3];
break;
case 't':
case 'T':
g_pTitle = &argv[0][3];
break;
case 'u':
case 'U':
g_pwzUrl = MyDupA2W(&argv[0][3]);
bRC = TRUE;
break;
case 'v':
case 'V':
g_dwVerbose = (DWORD)atoi(&argv[0][3]);
break;
case '1':
SetSingleProcessorAffinity();
break;
case '?':
default:
DisplayUsage(argvIn);
bRC = FALSE;
}
argv++; argc--;
}
return(bRC);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
BOOL BuildInfoList(PINFO pInfo, DWORD dwNumDownloads)
{
DWORD i = 0;
if(g_pInfile != NULL)
{
TCHAR szName[INTERNET_MAX_URL_LENGTH+1];
FILE *fp;
if((fp = fopen(g_pInfile, "r")) == NULL)
{
printf("error opening file:%s GLE=%d\n", g_pInfile, GetLastError());
return NULL;
}
while(fgets(szName, INTERNET_MAX_URL_LENGTH, fp) != NULL)
{
if(szName[0] != '#')
{
szName[strlen(szName) - sizeof(char)] = '\0';
int rc;
rc = MultiByteToWideChar(CP_ACP, 0, szName, -1, (pInfo+i)->wzUrl, INTERNET_MAX_URL_LENGTH);
if (!rc)
{
(pInfo+i)->wzUrl[INTERNET_MAX_URL_LENGTH-1] = 0;
wprintf(L"BuildInfoList:string too long; truncated to %s\n", (pInfo+i)->wzUrl);
}
i++;
}
}
g_dwNumDownloads = i;
fclose(fp);
}
else
{
for(i =0; i < dwNumDownloads; i++)
{
MylstrcpyW((pInfo+i)->wzUrl, g_pwzUrl);
}
}
g_dwDownloads = g_dwNumDownloads;
return(TRUE);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
HRESULT StartDownloads(IOInetSession* pSession, PINFO pInfo, DWORD dwNumDownloads)
{
HRESULT hr = NOERROR;
for(DWORD i =0; i < dwNumDownloads; i++)
{
// Create a pluggable protocol
hr = pSession->CreateBinding(
NULL, // [in ] BindCtx, always NULL
(pInfo+i)->wzUrl, // [in ] url
NULL, // [in ] IUnknown for Aggregration
NULL, // [out] IUNknown for Aggregration
&(pInfo+i)->pProt, // [out] return pProt pointer
0 // [in ] bind option, pass 0
);
if(g_dwVerbose & FLAG_TRACE)
printf("MAIN: Session->CreateBinding: %lx\n", hr);
// Create a protocolHook (sink) and Start the async operation
if( hr == NOERROR )
{
(pInfo+i)->pHook = new COInetProtocolHook(g_hCompleted, (pInfo+i)->pProt);
(pInfo+i)->pSink = NULL;
(pInfo+i)->pBindInfo = NULL;
if( (pInfo+i)->pHook )
{
hr = (pInfo+i)->pHook->QueryInterface(IID_IOInetProtocolSink, (void**)&(pInfo+i)->pSink);
hr = (pInfo+i)->pHook->QueryInterface(IID_IOInetBindInfo, (void**)&(pInfo+i)->pBindInfo);
}
if( (pInfo+i)->pProt && (pInfo+i)->pSink && (pInfo+i)->pBindInfo )
{
hr = (pInfo+i)->pProt->Start(
(pInfo+i)->wzUrl,
(pInfo+i)->pSink,
(pInfo+i)->pBindInfo,
PI_FORCE_ASYNC,
0
);
if(g_dwVerbose & FLAG_TRACE)
printf("MAIN: pProtocol->Start: %lx\n", hr);
}
}
}
return(hr);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
VOID CleanInfoList(PINFO pInfo, DWORD dwNumDownloads)
{
for(DWORD i = 0; i < dwNumDownloads; i++)
{
if((pInfo+i)->pProt)
(pInfo+i)->pProt->Terminate(0);
if( (pInfo+i)->pSink )
{
(pInfo+i)->pSink->Release();
}
if( (pInfo+i)->pBindInfo )
{
(pInfo+i)->pBindInfo->Release();
}
if( (pInfo+i)->pHook )
{
(pInfo+i)->pHook->Release();
}
// release COM objects
if( (pInfo+i)->pProt )
{
//
// BUG (POSSIBLE RESOURCE LEAK)
// If the pProt is IE's http/gopher/ftp implementation,
// calling pProt->Release() now might cause resource leak
// since pProt (although finished the download), might
// be still waiting wininet to call back about the
// confirmation of the handle closing.
// The correct time to release pProt is to wait after
// pProtSink get destroyed.
//
(pInfo+i)->pProt->Release();
}
}
}
//------------------------------------------------------------------------
//
// Purpose: create a session object
// Get a pluggable procotol from the session
// Start the pluggable protocol async download
//
// Author: DanpoZ (Danpo Zhang)
//
// History: 11-20-97 Created
//
//------------------------------------------------------------------------
int _cdecl main(int argc, char** argv)
{
IOInetSession* pSession = NULL;
IOInetProtocol* pProt = NULL;
HRESULT hr = NOERROR;
DWORD dwLoadTime;
HMODULE hMod = NULL;
if(!ProcessCommandLine(argc, argv))
return 0;
// Init COM
CoInitialize(NULL);
if(g_pModule != NULL)
{
hMod = LoadLibrary(g_pModule);
}
g_hCompleted = CreateEvent(NULL, FALSE, FALSE, NULL);
// Get a Session
hr = CoInternetGetSession(0, &pSession, 0);
if(g_dwVerbose & FLAG_TRACE)
printf("MAIN: Created Session: %lx\n", hr);
if( hr == NOERROR )
{
if(!BuildInfoList(&Info[0], g_dwNumDownloads))
return(2);
hr = StartDownloads(pSession, &Info[0], g_dwNumDownloads);
if( hr == NOERROR )
{
// wait until the async download finishes
WaitForSingleObject(g_hCompleted, INFINITE);
}
StopCAP();
QueryPerformanceCounter((LARGE_INTEGER *)&g_iend);
QueryPerformanceFrequency((LARGE_INTEGER *)&g_ifreq);
dwLoadTime = (LONG)(((g_iend - g_ibeg) * 1000) / g_ifreq);
float fKB;
float fSec;
float fKBSec;
if(dwLoadTime == 0)
dwLoadTime = 1;
fKB = ((float)g_dwTotalBytes)/1024;
fSec = ((float)dwLoadTime)/1000;
fKBSec = fKB / fSec;
printf("%s,%s,%d,%d,%2.0f\n",
g_pTitle ?g_pTitle :"Oinetperf",
g_pRun ?g_pRun :"1",
dwLoadTime, g_dwTotalBytes, fKBSec);
CleanInfoList(&Info[0], g_dwNumDownloads);
}
if( pSession )
{
pSession->Release();
}
CoTaskMemFree(g_pwzUrl);
if((g_pModule != NULL) && (hMod != NULL))
{
FreeLibrary(hMod);
}
// kill COM
CoUninitialize();
return(0);
}
COInetProtocolHook::COInetProtocolHook
(
HANDLE g_hCompleted,
IOInetProtocol* pProt
)
{
_hCompleted = g_hCompleted;
_pProt = pProt;
}
COInetProtocolHook::~COInetProtocolHook()
{
CloseHandle(_hCompleted);
}
HRESULT
COInetProtocolHook::QueryInterface(REFIID iid, void **ppvObj)
{
HRESULT hr = NOERROR;
*ppvObj = NULL;
if( iid == IID_IUnknown || iid == IID_IOInetProtocolSink )
{
*ppvObj = static_cast<IOInetProtocolSink*>(this);
}
else
if( iid == IID_IOInetBindInfo )
{
*ppvObj = static_cast<IOInetBindInfo*>(this);
}
else
if( iid == IID_IServiceProvider)
{
*ppvObj = static_cast<IServiceProvider*>(this);
}
else
if( iid == IID_IHttpNegotiate )
{
*ppvObj = static_cast<IHttpNegotiate*>(this);
}
else
{
hr = E_NOINTERFACE;
}
if( *ppvObj )
{
AddRef();
}
return hr;
}
ULONG
COInetProtocolHook::AddRef(void)
{
LONG lRet = ++_CRefs;
return lRet;
}
ULONG
COInetProtocolHook::Release(void)
{
LONG lRet = --_CRefs;
if (lRet == 0)
{
delete this;
}
return lRet;
}
HRESULT
COInetProtocolHook::Switch( PROTOCOLDATA *pStateInfo)
{
printf("Are you crazy? I don't know how to do Thread switching!!!\n");
return E_NOTIMPL;
}
HRESULT
COInetProtocolHook::ReportProgress( ULONG ulStatusCode, LPCWSTR szStatusText)
{
switch( ulStatusCode )
{
case BINDSTATUS_FINDINGRESOURCE:
if(g_dwVerbose & FLAG_TRACE)
wprintf(
L"CALLBACK(ReportProgress): Resolving name %s\n", szStatusText );
break;
case BINDSTATUS_CONNECTING:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): Connecting to %s\n", szStatusText );
break;
case BINDSTATUS_SENDINGREQUEST:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): Sending request\n");
break;
case BINDSTATUS_CACHEFILENAMEAVAILABLE:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): cache filename available\n");
break;
case BINDSTATUS_MIMETYPEAVAILABLE:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): mimetype available = %s\n", szStatusText);
break;
case BINDSTATUS_REDIRECTING:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): Redirecting to %s\n", szStatusText);
break;
default:
if(g_dwVerbose & FLAG_TRACE)
wprintf(L"CALLBACK(ReportProgress): others...\n");
break;
}
return NOERROR;
}
HRESULT
COInetProtocolHook::ReportData(
DWORD grfBSCF,
ULONG ulProgress,
ULONG ulProgressMax
)
{
if(g_dwVerbose & FLAG_TRACE)
printf("CALLBACK(ReportData) %d, %d, %d \n", grfBSCF, ulProgress, ulProgressMax);
// Pull data via pProt->Read(), here are the possible returned
// HRESULT values and how we should act upon:
//
// if E_PENDING is returned:
// client already get all the data in buffer, there is nothing
// can be done here, client should walk away and wait for the
// next chuck of data, which will be notified via ReportData()
// callback.
//
// if S_FALSE is returned:
// this is EOF, everything is done, however, client must wait
// for ReportResult() callback to indicate that the pluggable
// protocol is ready to shutdown.
//
// if S_OK is returned:
// keep on reading, until you hit E_PENDING/S_FALSE/ERROR, the deal
// is that the client is supposed to pull ALL the available
// data in the buffer
//
// if none of the above is returning:
// Error occured, client should decide how to handle it, most
// commonly, client will call pProt->Abort() to abort the download
char *pBuf = (char *)_alloca(dwBuf_Size);
HRESULT hr = NOERROR;
ULONG cbRead;
while( hr == S_OK )
{
cbRead = 0;
if (g_ibeg == 0)
{
QueryPerformanceCounter((LARGE_INTEGER *)&g_ibeg);
StartCAP();
}
// pull data
if(g_dwVerbose & FLAG_TRACE)
{
printf("MAIN: pProtocol->Read attempting %d bytes\n", dwBuf_Size);
}
hr = _pProt->Read((void*)pBuf, dwBuf_Size, &cbRead);
if( (hr == S_OK || hr == E_PENDING || hr == S_FALSE) && cbRead )
{
if( g_dwVerbose & FLAG_DUMPDATA )
{
for( ULONG i = 0; i < cbRead; i++)
{
printf("%c", pBuf[i]);
}
}
if(g_dwVerbose & FLAG_TRACE)
{
printf("MAIN: pProtocol->Read %d bytes\n", cbRead);
}
g_dwTotalBytes += cbRead;
}
}
if( hr == S_FALSE )
{
if(g_dwVerbose & FLAG_TRACE)
printf("MAIN: pProtocol->Read returned EOF \n");
}
else
if( hr != E_PENDING )
{
if(g_dwVerbose & FLAG_TRACE)
{
printf("MAIN: pProtocol->Read returned Error %1x \n, hr");
printf("MAIN: pProtocol->Abort called \n", hr);
}
_pProt->Abort(hr, 0);
}
return NOERROR;
}
HRESULT
COInetProtocolHook::ReportResult(
HRESULT hrResult,
DWORD dwError,
LPCWSTR wzResult
)
{
// This is the last call back from the pluggable protocol,
// this call is equivlant to the IBindStatusCallBack::OnStopBinding()
// it basically tells you that the pluggable protocol is ready
// to shutdown
if(g_dwVerbose & FLAG_TRACE)
printf("CALLBACK(ReportResult): Download completed with status %1x\n", hrResult);
// set event to the main thread
if(InterlockedDecrement((LONG*)&g_dwDownloads ) == 0)
SetEvent(g_hCompleted);
return NOERROR;
}
HRESULT
COInetProtocolHook::GetBindInfo(
DWORD *grfBINDF,
BINDINFO * pbindinfo
)
{
HRESULT hr = NOERROR;
// *grfBINDF = BINDF_DIRECT_READ | BINDF_ASYNCHRONOUS | BINDF_PULLDATA;
// *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
*grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_IGNORESECURITYPROBLEM;
*grfBINDF |= g_dwCacheFlag;
// for HTTP GET, VERB is the only field we interested
// for HTTP POST, BINDINFO will point to Storage structure which
// contains data
BINDINFO bInfo;
ZeroMemory(&bInfo, sizeof(BINDINFO));
// all we need is size and verb field
bInfo.cbSize = sizeof(BINDINFO);
bInfo.dwBindVerb = BINDVERB_GET;
// src -> dest
hr = CopyBindInfo(&bInfo, pbindinfo );
return hr;
}
static LPSTR g_szAcceptStrAll = "*/*";
HRESULT
COInetProtocolHook::GetBindString(
ULONG ulStringType,
LPOLESTR *ppwzStr,
ULONG cEl,
ULONG *pcElFetched
)
{
HRESULT hr = INET_E_USE_DEFAULT_SETTING;
switch (ulStringType)
{
case BINDSTRING_HEADERS :
case BINDSTRING_EXTRA_URL :
case BINDSTRING_LANGUAGE :
case BINDSTRING_USERNAME :
case BINDSTRING_PASSWORD :
case BINDSTRING_ACCEPT_ENCODINGS:
case BINDSTRING_URL:
case BINDSTRING_USER_AGENT :
case BINDSTRING_POST_COOKIE :
case BINDSTRING_POST_DATA_MIME:
break;
case BINDSTRING_ACCEPT_MIMES:
// IE4's http pluggable protocol implementation does not
// honer INET_E_USE_DEFAULT_SETTING returned by this function
// starting from IE5, client can just return the USE_DEFAULT
// use for ie5 so we don't need a seperate bin for ie4 // #ifndef IE5
// this will be freed by the caller
*(ppwzStr + 0) = MyDupA2W(g_szAcceptStrAll);
*(ppwzStr + 1) = NULL;
*pcElFetched = 1;
hr = NOERROR;
//#endif
break;
default:
break;
}
return hr;
}
HRESULT
COInetProtocolHook::QueryService(
REFGUID guidService,
REFIID riid,
void **ppvObj
)
{
HRESULT hr = E_NOINTERFACE;
*ppvObj = NULL;
if( guidService == IID_IHttpNegotiate )
{
*ppvObj = static_cast<IHttpNegotiate*>(this);
}
if( *ppvObj )
{
AddRef();
hr = NOERROR;
}
return hr;
}
HRESULT
COInetProtocolHook::BeginningTransaction(
LPCWSTR szURL,
LPCWSTR szHeaders,
DWORD dwReserved,
LPWSTR *pszAdditionalHeaders
)
{
if(g_dwVerbose & FLAG_TRACE)
printf("HTTPNEGOTIATE: Additional Headers? - No \n");
*pszAdditionalHeaders = NULL;
return NOERROR;
}
HRESULT
COInetProtocolHook::OnResponse(
DWORD dwResponseCode,
LPCWSTR szResponseHeaders,
LPCWSTR szRequestHeaders,
LPWSTR *pszAdditionalHeaders
)
{
if(g_dwVerbose & FLAG_TRACE)
printf("HTTPNEGOTIATE: Http server response code %d\n", dwResponseCode);
return NOERROR;
}