1075 lines
30 KiB
C++
1075 lines
30 KiB
C++
// File: clstencil.h
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Classes Reference and related electronic
|
|
// documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft C++ Libraries products.
|
|
|
|
#pragma once
|
|
|
|
#include <atlisapi.h>
|
|
#include <stdio.h>
|
|
#include "resource.h"
|
|
|
|
ATL_NOINLINE inline void GetHrErrorDescription(HRESULT hr, CString& strErr) throw()
|
|
{
|
|
LPTSTR pszMsg = NULL;
|
|
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, (LPSTR)&pszMsg, 0, NULL);
|
|
strErr+= pszMsg;
|
|
::LocalFree(pszMsg);
|
|
}
|
|
|
|
inline bool Emit(CStringA& strBuffer, UINT uID, ...)
|
|
{
|
|
_ATLTRY
|
|
{
|
|
CStringA str;
|
|
if (str.LoadString(uID))
|
|
{
|
|
va_list arglist;
|
|
va_start(arglist, uID);
|
|
int nLen = _vscprintf((LPCSTR) str, arglist);
|
|
char *szBuf = strBuffer.GetBuffer(nLen+1);
|
|
if (szBuf != NULL)
|
|
{
|
|
vsprintf_s(szBuf, nLen+1, str, arglist);
|
|
strBuffer.ReleaseBuffer(nLen);
|
|
}
|
|
else
|
|
{
|
|
strBuffer.ReleaseBuffer();
|
|
}
|
|
|
|
va_end(arglist);
|
|
return true;
|
|
}
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
class CSProcServerContext : public IHttpServerContext
|
|
{
|
|
protected:
|
|
|
|
CAtlFile m_OutFile;
|
|
CAtlFile m_InFile;
|
|
CAtlFile m_ErrFile;
|
|
|
|
bool m_bStdInput;
|
|
bool m_bStdOutput;
|
|
bool m_bStdError;
|
|
|
|
char m_szPathTranslated[MAX_PATH];
|
|
char m_szScriptPathTranslated[MAX_PATH];
|
|
char m_szQueryString[ATL_URL_MAX_URL_LENGTH];
|
|
char m_szContentType[ATL_URL_MAX_URL_LENGTH];
|
|
char m_szVerb[MAX_PATH];
|
|
|
|
void CloseFiles()
|
|
{
|
|
if(m_bStdInput)
|
|
m_InFile.Detach();
|
|
else
|
|
m_InFile.Close();
|
|
|
|
if(m_bStdOutput)
|
|
m_OutFile.Detach() ;
|
|
else
|
|
m_OutFile.Close();
|
|
|
|
if(m_bStdError)
|
|
m_ErrFile.Detach() ;
|
|
else
|
|
m_ErrFile.Close();
|
|
}
|
|
|
|
public:
|
|
CString m_strErr;
|
|
|
|
CSProcServerContext()
|
|
{
|
|
m_szQueryString[0] = '\0';
|
|
m_szScriptPathTranslated[0] = '\0';
|
|
m_szQueryString[0] = '\0';
|
|
m_szContentType[0] = '\0';
|
|
m_szVerb[0] = '\0';
|
|
m_bStdInput = false;
|
|
m_bStdOutput = false;
|
|
m_bStdError = false;
|
|
}
|
|
|
|
void LogServerError(LPCSTR szErr) throw()
|
|
{
|
|
if (m_ErrFile.m_h == NULL)
|
|
return;
|
|
|
|
CStringA strBuffer;
|
|
if (Emit(strBuffer, IDS_NOTSUPPORTED, szErr))
|
|
{
|
|
m_ErrFile.Write(strBuffer, strBuffer.GetLength());
|
|
}
|
|
}
|
|
|
|
// Implementation
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
|
|
{
|
|
if (!ppv)
|
|
return E_POINTER;
|
|
if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
|
|
InlineIsEqualGUID(riid, __uuidof(IHttpServerContext)))
|
|
{
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// Implementation
|
|
ULONG STDMETHODCALLTYPE AddRef() throw()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Implementation
|
|
ULONG STDMETHODCALLTYPE Release() throw()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
HRESULT Initialize(
|
|
LPCSTR szFileName,
|
|
LPCSTR szQueryString,
|
|
LPCSTR szErrLog,
|
|
LPCSTR szFormFile,
|
|
LPCSTR szContentType,
|
|
LPCSTR szVerb) throw()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (szFileName == NULL)
|
|
{
|
|
// use Standard output
|
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if (hOut == INVALID_HANDLE_VALUE)
|
|
return E_FAIL;
|
|
m_OutFile.Attach(hOut);
|
|
m_bStdOutput = true;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = m_OutFile.Create(szFileName, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
m_strErr.Format("ERROR: %s : ", szFileName ? szFileName : "stdout");
|
|
GetHrErrorDescription(hr, m_strErr);
|
|
return hr;
|
|
}
|
|
|
|
if (szErrLog)
|
|
{
|
|
hr = m_ErrFile.Create(szErrLog, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
|
|
}
|
|
else
|
|
{
|
|
HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE);
|
|
if (hErr == INVALID_HANDLE_VALUE)
|
|
hr = E_FAIL;
|
|
else
|
|
{
|
|
m_ErrFile.Attach(hErr);
|
|
m_bStdError = true;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
if (hr != S_OK)
|
|
{
|
|
m_strErr.Format("ERROR: %s : ", szErrLog ? szErrLog : "stderr");
|
|
GetHrErrorDescription(hr, m_strErr);
|
|
CloseFiles();
|
|
return hr;
|
|
}
|
|
|
|
if (szFormFile)
|
|
{
|
|
hr = m_InFile.Create(szFormFile, GENERIC_READ, FILE_SHARE_READ, OPEN_ALWAYS);
|
|
}
|
|
else
|
|
{
|
|
HANDLE hForm = GetStdHandle(STD_INPUT_HANDLE);
|
|
if (hForm == INVALID_HANDLE_VALUE)
|
|
hr = E_FAIL;
|
|
else
|
|
{
|
|
m_InFile.Attach(hForm);
|
|
m_bStdInput = true;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
if (hr != S_OK)
|
|
{
|
|
m_strErr.Format("ERROR: %s : ", szFormFile ? szFormFile : "stdin");
|
|
GetHrErrorDescription(hr, m_strErr);
|
|
CloseFiles();
|
|
return hr;
|
|
}
|
|
|
|
if ( szQueryString != NULL)
|
|
if( strlen(szQueryString) < ( sizeof(m_szQueryString)-1 ) )
|
|
strcpy_s(m_szQueryString, sizeof(m_szQueryString), szQueryString);
|
|
else
|
|
{
|
|
m_strErr.LoadString(IDS_QS_TOO_LONG);
|
|
CloseFiles();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( szContentType != NULL)
|
|
if(strlen(szContentType) < ( sizeof(m_szContentType)-1 ) )
|
|
strcpy_s(m_szContentType, sizeof(m_szContentType), szContentType);
|
|
else
|
|
{
|
|
m_strErr.LoadString(IDS_CT_TOO_LONG);
|
|
CloseFiles();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (szVerb == NULL)
|
|
strcpy_s(m_szVerb, sizeof(m_szVerb), "GET");
|
|
else
|
|
{
|
|
if ( strlen(szVerb) < ( sizeof(m_szVerb)-1 ) )
|
|
strcpy_s(m_szVerb, sizeof(m_szVerb), szVerb);
|
|
else
|
|
{
|
|
m_strErr.LoadString(IDS_VERB_TOO_LONG);
|
|
CloseFiles();
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
~CSProcServerContext() throw()
|
|
{
|
|
}
|
|
|
|
// Returns the HTTP status code.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::dwHttpStatusCode.
|
|
DWORD GetHttpStatusCode() throw()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Returns a nul-terminated string that contains the HTTP method of the current request.
|
|
// Examples of common HTTP methods include "GET" and "POST".
|
|
// Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
|
|
LPCSTR GetRequestMethod() throw()
|
|
{
|
|
return m_szVerb;
|
|
}
|
|
|
|
// Returns a nul-terminated string that contains the query information.
|
|
// This is the part of the URL that appears after the question mark (?).
|
|
// Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
|
|
LPCSTR GetQueryString() throw()
|
|
{
|
|
return m_szQueryString;
|
|
}
|
|
|
|
// Returns a nul-terminated string that contains the path of the current request.
|
|
// This is the part of the URL that appears after the server name, but before the query string.
|
|
// Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
|
|
LPCSTR GetPathInfo() throw()
|
|
{
|
|
return m_szPathTranslated;
|
|
}
|
|
|
|
// Call this function to retrieve a nul-terminated string containing the physical path of the script.
|
|
//
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
//
|
|
// On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
|
|
// On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
|
|
// The script path is the same as GetPathTranslated up to the first .srf or .dll.
|
|
// For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
|
|
// then this function returns "c:\inetpub\vcisapi\hello.srf".
|
|
LPCSTR GetScriptPathTranslated() throw()
|
|
{
|
|
return m_szScriptPathTranslated;
|
|
}
|
|
|
|
// Returns a nul-terminated string that contains the translated path of the requested resource.
|
|
// This is the path of the resource on the local server.
|
|
// Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
|
|
LPCSTR GetPathTranslated() throw()
|
|
{
|
|
return m_szPathTranslated;
|
|
}
|
|
|
|
BOOL SetPathTranslated(LPCSTR szPath)
|
|
{
|
|
ATLENSURE(szPath != NULL);
|
|
m_szPathTranslated[0] = '\0';
|
|
|
|
if ( strlen(szPath) < sizeof(m_szPathTranslated)-1 )
|
|
strcpy_s(m_szPathTranslated, sizeof(m_szPathTranslated), szPath);
|
|
else
|
|
{
|
|
ATLTRACE("SetPathTranslated got a translated path that was too long.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialize the translated script path
|
|
LPCSTR szEnd = m_szPathTranslated;
|
|
|
|
while (TRUE)
|
|
{
|
|
while (*szEnd != '.' && *szEnd != '\0')
|
|
szEnd++;
|
|
if (*szEnd == '\0')
|
|
break;
|
|
|
|
szEnd++;
|
|
if (!_strnicmp(szEnd, "dll", 3) ||
|
|
!_strnicmp(szEnd, c_AtlSRFExtension+1, 3))
|
|
{
|
|
szEnd += 3;
|
|
if (!*szEnd || *szEnd == '/' || *szEnd == '\\' || *szEnd == '?' || *szEnd == '#')
|
|
break;
|
|
}
|
|
}
|
|
|
|
ptrdiff_t nResult = szEnd - m_szPathTranslated;
|
|
|
|
if (nResult >= 0 && nResult <= ( sizeof(m_szScriptPathTranslated)-1 ) )
|
|
memcpy_s(m_szScriptPathTranslated, sizeof(m_szScriptPathTranslated), m_szPathTranslated, nResult);
|
|
else if (nResult < 0)
|
|
{
|
|
ATLTRACE("Unexpected path length.\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ATLTRACE("Path translated was too long.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
m_szScriptPathTranslated[nResult] = '\0';
|
|
return TRUE;
|
|
}
|
|
|
|
// Returns the total number of bytes to be received from the client.
|
|
// If this value is 0xffffffff, then there are four gigabytes or more of available data.
|
|
// In this case, ReadClient or AsyncReadClient should be called until no more data is returned.
|
|
// Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
|
|
DWORD GetTotalBytes() throw()
|
|
{
|
|
ULONGLONG nLen = 0;
|
|
m_InFile.GetSize(nLen);
|
|
return (DWORD)nLen;
|
|
}
|
|
|
|
// Returns the number of bytes available in the request buffer accessible via GetAvailableData.
|
|
// If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
|
|
// Otherwise, the remaining data should be read from the client using ReadClient or AsyncReadClient.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
|
|
DWORD GetAvailableBytes() throw()
|
|
{
|
|
// LogServerError("IHttpServerContext::GetAvailableBytes not supported in command line mode.\r\n");
|
|
return 0;
|
|
}
|
|
|
|
// Returns a pointer to the request buffer containing the data sent by the client.
|
|
// The size of the buffer can be determined by calling GetAvailableBytes.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
|
|
BYTE *GetAvailableData() throw()
|
|
{
|
|
// LogServerError("IHttpServerContext::GetAvailableData not supported in command line mode.\r\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Returns a nul-terminated string that contains the content type of the data sent by the client.
|
|
// Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
|
|
LPCSTR GetContentType() throw()
|
|
{
|
|
// LogServerError("IHttpServerContext::GetContentType not supported in command line mode.\r\n");
|
|
return m_szContentType;
|
|
}
|
|
|
|
// Call this function to retrieve a nul-terminated string containing the value of the requested server variable.
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
// On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
|
|
// On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
|
|
BOOL GetServerVariable(
|
|
LPCSTR szVariableName,
|
|
LPSTR pvBuffer,
|
|
DWORD * pdwSize) throw()
|
|
{
|
|
if( !pdwSize )
|
|
return FALSE;
|
|
|
|
BOOL bRet = TRUE;
|
|
|
|
LPCSTR szVar;
|
|
size_t retVal = 0;
|
|
size_t bufSize = 0;
|
|
|
|
// retrieve size of buffer
|
|
getenv_s(&retVal, NULL, bufSize, szVariableName);
|
|
|
|
if (retVal != 0)
|
|
{
|
|
char *buf = new char[retVal];
|
|
|
|
bufSize = retVal;
|
|
getenv_s(&retVal, buf, bufSize, szVariableName);
|
|
szVar = buf;
|
|
|
|
if( pvBuffer && *pdwSize > strlen(szVar) )
|
|
strcpy_s( pvBuffer, bufSize, szVar );
|
|
else
|
|
{
|
|
::SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
*pdwSize = 1 + (DWORD)strlen( szVar );
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_ErrFile.m_h != NULL )
|
|
{
|
|
CStringA strErrMsg;
|
|
Emit(strErrMsg, IDS_SERVER_VARIABLE_NOT_FOUND, szVariableName ? szVariableName : "<NULL>");
|
|
m_ErrFile.Write(strErrMsg, strErrMsg.GetLength());
|
|
}
|
|
|
|
*pdwSize = 0;
|
|
::SetLastError( ERROR_NO_DATA);
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
|
|
|
|
}
|
|
|
|
// Synchronously sends the data present in the given buffer to the client that made the request.
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_SYNC).
|
|
BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
|
|
{
|
|
return (m_OutFile.Write(pvBuffer, *pdwBytes) == S_OK ? TRUE : FALSE);
|
|
|
|
}
|
|
|
|
// Asynchronously sends the data present in the given buffer to the client that made the request.
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_ASYNC).
|
|
BOOL AsyncWriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
|
|
{
|
|
return WriteClient(pvBuffer, pdwBytes);
|
|
}
|
|
|
|
// Call this function to synchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
// Equivalent to EXTENSION_CONTROL_BLOCK::ReadClient.
|
|
BOOL ReadClient(void * pvBuffer, DWORD * pdwSize) throw()
|
|
{
|
|
ATLENSURE(pdwSize != NULL);
|
|
DWORD dwRead = 0;
|
|
HRESULT hr = m_InFile.Read(pvBuffer, *pdwSize, dwRead);
|
|
*pdwSize = dwRead;
|
|
return (hr == S_OK ? TRUE : FALSE);
|
|
}
|
|
|
|
// Call this function to asynchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
|
|
// Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
|
|
// Equivalent to the HSE_REQ_ASYNC_READ_CLIENT server support function.
|
|
BOOL AsyncReadClient(void * pvBuffer, DWORD * pdwSize) throw()
|
|
{
|
|
return ReadClient(pvBuffer, pdwSize);
|
|
}
|
|
|
|
// Call this function to redirect the client to the specified URL.
|
|
// The client receives a 302 (Found) HTTP status code.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_SEND_URL_REDIRECT_RESP server support function.
|
|
BOOL SendRedirectResponse(LPCSTR /*pszRedirectURL*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::SendRedirectResponse");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call this function to retrieve a handle to the impersonation token for this request.
|
|
// An impersonation token represents a user context. You can use the handle in calls to ImpersonateLoggedOnUser or SetThreadToken.
|
|
// Do not call CloseHandle on the handle.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_GET_IMPERSONATION_TOKEN server support function.
|
|
|
|
BOOL GetImpersonationToken(HANDLE * /*pToken*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::GetImpersonationToken");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call this function to send an HTTP response header to the client including the HTTP status, server version, message time, and MIME version.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_SEND_RESPONSE_HEADER_EX server support function.
|
|
BOOL SendResponseHeader(
|
|
LPCSTR /*pszHeader = "Content-Type: text/html\r\n\r\n"*/,
|
|
LPCSTR /*pszStatusCode = "200 OK"*/,
|
|
BOOL /*fKeepConn=FALSE*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::SendResponseHeader");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call this function to terminate the session for the current request.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_DONE_WITH_SESSION server support function.
|
|
BOOL DoneWithSession(DWORD /*dwStatusCode*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::DoneWithSession");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call this function to set a special callback function that will be used for handling the completion of asynchronous I/O operations.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_IO_COMPLETION server support function.
|
|
BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::RequestIOCompletion");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call this function to transmit a file asynchronously to the client.
|
|
// Returns TRUE on success, and FALSE on failure.
|
|
// Equivalent to the HSE_REQ_TRANSMIT_FILE server support function.
|
|
BOOL TransmitFile(
|
|
HANDLE hFile,
|
|
PFN_HSE_IO_COMPLETION /*pfn*/,
|
|
void * /*pContext*/,
|
|
LPCSTR /*szStatusCode*/,
|
|
DWORD /*dwBytesToWrite*/,
|
|
DWORD /*dwOffset*/,
|
|
void * /*pvHead*/,
|
|
DWORD /*dwHeadLen*/,
|
|
void * /*pvTail*/,
|
|
DWORD /*dwTailLen*/,
|
|
DWORD /*dwFlags*/) throw()
|
|
{
|
|
char szBuffer[1024];
|
|
DWORD dwLen;
|
|
OVERLAPPED overlapped;
|
|
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
HANDLE hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (!hEvent)
|
|
return FALSE;
|
|
overlapped.hEvent = hEvent;
|
|
CHandle hdlEvent;
|
|
hdlEvent.Attach(hEvent);
|
|
DWORD dwErr;
|
|
do
|
|
{
|
|
BOOL bRet = ::ReadFile(hFile, szBuffer, 1024, &dwLen, &overlapped);
|
|
if (!bRet && (dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
|
|
return FALSE;
|
|
|
|
if (!GetOverlappedResult(hFile, &overlapped, &dwLen, TRUE))
|
|
return FALSE;
|
|
|
|
if (dwLen)
|
|
{
|
|
DWORD dwWritten = dwLen;
|
|
if (!WriteClient(szBuffer, &dwWritten))
|
|
return FALSE;
|
|
}
|
|
|
|
} while (dwLen != 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Appends the string szMessage to the web server log for the current
|
|
// request.
|
|
// Returns TRUE on success, FALSE on failure.
|
|
// Equivalent to the HSE_APPEND_LOG_PARAMETER server support function.
|
|
BOOL AppendToLog(LPCSTR /*szMessage*/, DWORD * /*pdwLen*/) throw()
|
|
{
|
|
LogServerError("IHttpServerContext::AppendToLog");
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL MapUrlToPathEx(LPCSTR /*szLogicalPath*/, DWORD /*dwLen*/, HSE_URL_MAPEX_INFO * /*pumInfo*/)
|
|
{
|
|
LogServerError("IHttpServerContext::MapUrlToPathEx");
|
|
return FALSE;
|
|
}
|
|
}; // class CSprocServerContext
|
|
|
|
|
|
class CSProcExtension : public IServiceProvider, public IIsapiExtension
|
|
{
|
|
protected:
|
|
|
|
typedef CStencilCache<CWorkerThread<> > stencilCacheType;
|
|
CWorkerThread<> m_WorkerThread;
|
|
CDllCache<CWorkerThread<>, CDllCachePeer> m_DllCache;
|
|
CComObjectGlobal<CStencilCache<CWorkerThread<> > > m_StencilCache;
|
|
CDefaultErrorProvider m_UserErrorProvider;
|
|
|
|
CIsapiWorker m_Worker;
|
|
|
|
// Dynamic services stuff
|
|
struct ServiceNode
|
|
{
|
|
HINSTANCE hInst;
|
|
IUnknown *punk;
|
|
GUID guidService;
|
|
IID riid;
|
|
|
|
ServiceNode() throw()
|
|
{
|
|
}
|
|
|
|
ServiceNode(const ServiceNode& that) throw()
|
|
:hInst(that.hInst), punk(that.punk), guidService(that.guidService), riid(that.riid)
|
|
{
|
|
}
|
|
|
|
const ServiceNode& operator=(const ServiceNode& that) throw()
|
|
{
|
|
if (this != &that)
|
|
{
|
|
hInst = that.hInst;
|
|
punk = that.punk;
|
|
memcpy_s(&guidService, sizeof(guidService), &that.guidService, sizeof(guidService));
|
|
memcpy_s(&riid, sizeof(riid), &that.riid, sizeof(riid));
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class CServiceEqualHelper
|
|
{
|
|
public:
|
|
static bool IsEqual(const ServiceNode& t1, const ServiceNode& t2)
|
|
{
|
|
return (InlineIsEqualGUID(t1.guidService, t2.guidService) != 0);
|
|
}
|
|
};
|
|
|
|
CSimpleArray<ServiceNode, CServiceEqualHelper> m_serviceMap;
|
|
|
|
public:
|
|
|
|
CString m_strErr;
|
|
|
|
CSProcExtension() throw()
|
|
{
|
|
}
|
|
|
|
AtlServerRequest *CreateRequest()
|
|
{
|
|
AtlServerRequest *pRequest = (AtlServerRequest *) malloc(max(sizeof(AtlServerRequest), sizeof(CComObjectNoLock<CServerContext>)));
|
|
if (!pRequest)
|
|
return NULL;
|
|
pRequest->cbSize = sizeof(AtlServerRequest);
|
|
return pRequest;
|
|
}
|
|
|
|
void FreeRequest(AtlServerRequest *pRequest)
|
|
{
|
|
if (pRequest)
|
|
{
|
|
if (pRequest->pHandler)
|
|
pRequest->pHandler->Release();
|
|
if (pRequest->pServerContext)
|
|
pRequest->pServerContext->Release();
|
|
if (pRequest->pDllCache && pRequest->hInstDll)
|
|
pRequest->pDllCache->ReleaseModule(pRequest->hInstDll);
|
|
}
|
|
|
|
//free(pRequest);
|
|
}
|
|
|
|
BOOL Initialize() throw()
|
|
{
|
|
if (!m_Worker.Initialize(static_cast<IIsapiExtension*>(this)))
|
|
return FALSE;
|
|
|
|
if (S_OK != m_WorkerThread.Initialize())
|
|
return FALSE;
|
|
|
|
if (FAILED(m_DllCache.Initialize(&m_WorkerThread, ATL_DLL_CACHE_TIMEOUT)))
|
|
{
|
|
m_WorkerThread.Shutdown();
|
|
return FALSE;
|
|
}
|
|
|
|
if (S_OK != m_StencilCache.Initialize(static_cast<IServiceProvider*>(this), &m_WorkerThread,
|
|
ATL_STENCIL_CACHE_TIMEOUT, ATL_STENCIL_LIFESPAN))
|
|
{
|
|
m_DllCache.Uninitialize();
|
|
m_WorkerThread.Shutdown();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL Uninitialize() throw()
|
|
{
|
|
for (int i=0; i < m_serviceMap.GetSize(); i++)
|
|
{
|
|
ATLASSERT(m_serviceMap[i].punk != NULL);
|
|
m_serviceMap[i].punk->Release();
|
|
m_DllCache.ReleaseModule(m_serviceMap[i].hInst);
|
|
}
|
|
|
|
m_StencilCache.Uninitialize();
|
|
m_DllCache.Uninitialize();
|
|
m_Worker.Terminate(static_cast<IIsapiExtension*>(this));
|
|
m_WorkerThread.Shutdown();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void RequestComplete(AtlServerRequest * /*pRequestInfo*/, DWORD /*dwStatus*/, DWORD /*dwSubStatus*/) throw()
|
|
{
|
|
}
|
|
|
|
HTTP_CODE GetHandlerName(LPCSTR szFileName, LPSTR szHandlerName) throw()
|
|
{
|
|
return _AtlGetHandlerName(szFileName, szHandlerName);
|
|
}
|
|
|
|
HTTP_CODE LoadDispatchFile(LPCSTR szFileName, AtlServerRequest *pRequestInfo) throw()
|
|
{
|
|
CStencil *pStencil = NULL;
|
|
HCACHEITEM hStencil = NULL;
|
|
CHAR szDllPath[MAX_PATH+1];
|
|
CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
|
|
|
|
pRequestInfo->pHandler = NULL;
|
|
pRequestInfo->hInstDll = NULL;
|
|
|
|
USES_CONVERSION;
|
|
|
|
m_StencilCache.LookupStencil(szFileName, &hStencil);
|
|
if (!hStencil)
|
|
{
|
|
char szHandlerDllName[MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+2];
|
|
// not in the cache, so open the file
|
|
HTTP_CODE hcErr = GetHandlerName(szFileName, szHandlerDllName);
|
|
if (hcErr)
|
|
return hcErr;
|
|
DWORD dwDllPathLen = MAX_PATH+1;
|
|
DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
|
|
if (!_AtlCrackHandler(szHandlerDllName, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
|
|
{
|
|
HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
|
|
}
|
|
ATLASSERT(*szHandlerName);
|
|
ATLASSERT(*szDllPath);
|
|
if (!*szHandlerName)
|
|
return HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
|
|
}
|
|
else
|
|
{
|
|
m_StencilCache.GetStencil(hStencil, (void **) &pStencil);
|
|
pStencil->GetHandlerName(szDllPath, MAX_PATH+1, szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1);
|
|
m_StencilCache.ReleaseStencil(hStencil);
|
|
|
|
if (!*szHandlerName)
|
|
return HTTP_ERROR(500, ISE_SUBERR_BADSRF); // bad srf file
|
|
}
|
|
return LoadRequestHandler(szDllPath, szHandlerName, pRequestInfo->pServerContext, &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
|
|
}
|
|
|
|
|
|
HTTP_CODE LoadDllHandler(LPCSTR szFileName, AtlServerRequest *pRequestInfo) throw()
|
|
{
|
|
HTTP_CODE hcErr = HTTP_FAIL;
|
|
CHttpRequest Request;
|
|
BOOL bRet = Request.Initialize(pRequestInfo->pServerContext, 0);
|
|
if (bRet)
|
|
{
|
|
LPCSTR szHandler = Request.QueryParams.Lookup("Handler");
|
|
if (!szHandler)
|
|
{
|
|
szHandler = "Default";
|
|
}
|
|
|
|
hcErr = LoadRequestHandler(szFileName, szHandler, pRequestInfo->pServerContext,
|
|
&pRequestInfo->hInstDll, &pRequestInfo->pHandler);
|
|
}
|
|
else
|
|
{
|
|
hcErr = HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
}
|
|
|
|
return hcErr;
|
|
}
|
|
|
|
|
|
BOOL DispatchStencilCall(AtlServerRequest * /*pRequestInfo*/) throw()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
virtual void ThreadTerminate(DWORD /*dwThreadId*/) { }
|
|
virtual BOOL QueueRequest(AtlServerRequest * /*pRequestInfo*/) { return FALSE; }
|
|
virtual CIsapiWorker *GetThreadWorker() { return &m_Worker; }
|
|
virtual BOOL SetThreadWorker(CIsapiWorker * /*pWorker*/) { return TRUE; }
|
|
BOOL OnThreadAttach() { return TRUE; }
|
|
void OnThreadTerminate() {}
|
|
|
|
|
|
BOOL DispatchStencilCall(
|
|
LPCSTR szFile,
|
|
LPCSTR szOutputFile,
|
|
LPCSTR szQueryString,
|
|
LPCSTR szErrFile,
|
|
LPCSTR szFormFile,
|
|
LPCSTR szContentType,
|
|
LPCSTR szVerb)
|
|
{
|
|
ATLASSERT(szFile != NULL);
|
|
|
|
CSProcServerContext ServerContext;
|
|
HRESULT hr = ServerContext.Initialize(szOutputFile, szQueryString, szErrFile, szFormFile, szContentType, szVerb);
|
|
if (hr != S_OK)
|
|
{
|
|
m_strErr = ServerContext.m_strErr;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ServerContext.SetPathTranslated(szFile))
|
|
return FALSE;
|
|
|
|
AtlServerRequest RequestInfo;
|
|
AtlServerRequest *pRequestInfo = &RequestInfo;
|
|
pRequestInfo->pServerContext = static_cast<IHttpServerContext*>(&ServerContext);
|
|
pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
|
|
pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
|
|
pRequestInfo->pExtension = static_cast<IIsapiExtension*>(this);
|
|
pRequestInfo->pDllCache = static_cast<IDllCache *>(&m_DllCache);
|
|
pRequestInfo->dwStartTicks = 0;
|
|
|
|
HTTP_CODE hcErr = HTTP_SUCCESS;
|
|
if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
|
|
{
|
|
LPCSTR szFileName = ServerContext.GetScriptPathTranslated();
|
|
|
|
if (!szFileName)
|
|
{
|
|
m_strErr.Format("ERROR: Invalid file: %s", szFile);
|
|
RequestComplete(pRequestInfo, 500, 8);
|
|
return FALSE;
|
|
}
|
|
|
|
LPCSTR szDot = szFileName + strlen(szFileName)-4;
|
|
if (_tcsicmp(szDot, c_AtlSRFExtension) == 0)
|
|
{
|
|
pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
|
|
hcErr = LoadDispatchFile(szFileName, pRequestInfo);
|
|
}
|
|
else if (_stricmp(szDot, ".dll") == 0)
|
|
{
|
|
pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
|
|
hcErr = LoadDllHandler(szFileName, pRequestInfo);
|
|
}
|
|
else
|
|
{
|
|
hcErr = HTTP_FAIL;
|
|
}
|
|
|
|
if (hcErr)
|
|
{
|
|
if (hcErr == HTTP_ERROR(500, ISE_SUBERR_BADSRF))
|
|
m_strErr.Format("ERROR: Invalid SRF file: %s", szFile);
|
|
else if (hcErr == HTTP_ERROR(500, ISE_SUBERR_READFILEFAIL))
|
|
m_strErr.Format("ERROR: Failed to read SRF file: %s", szFile);
|
|
else if (hcErr == HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED))
|
|
m_strErr = "ERROR: Unexpected Error Loading DLL";
|
|
RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
|
|
return FALSE;
|
|
}
|
|
|
|
// initialize the handler
|
|
DWORD dwStatus = 0;
|
|
|
|
hcErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
|
|
// Ignoring caching options
|
|
// Log errors on async
|
|
if (dwStatus & ATLSRV_INIT_USEASYNC)
|
|
{
|
|
// TODO: Log error, or come up with a way to make it work
|
|
RequestComplete(pRequestInfo, 500, SUBERR_NONE);
|
|
return FALSE;
|
|
}
|
|
|
|
hcErr = pRequestInfo->pHandler->InitializeHandler(pRequestInfo, static_cast<IServiceProvider*>(this));
|
|
if (hcErr == HTTP_SUCCESS)
|
|
hcErr = pRequestInfo->pHandler->HandleRequest(pRequestInfo, static_cast<IServiceProvider*>(this));
|
|
}
|
|
|
|
if (hcErr == HTTP_SUCCESS_ASYNC || hcErr == HTTP_SUCCESS_ASYNC_DONE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
pRequestInfo->pHandler->UninitializeHandler();
|
|
RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
|
|
}
|
|
|
|
FreeRequest(pRequestInfo);
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
|
|
{
|
|
if (!ppv)
|
|
return E_POINTER;
|
|
if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
|
|
InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
|
|
{
|
|
*ppv = static_cast<IUnknown*>(static_cast<IServiceProvider*>(this));
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
if (InlineIsEqualGUID(riid, __uuidof(IIsapiExtension)))
|
|
{
|
|
*ppv = static_cast<IUnknown*>(static_cast<IIsapiExtension*>(this));
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE QueryService(
|
|
REFGUID guidService,
|
|
REFIID riid,
|
|
void **ppvObject) throw()
|
|
{
|
|
if (!ppvObject)
|
|
return E_POINTER;
|
|
|
|
if (InlineIsEqualGUID(guidService, __uuidof(IDllCache)))
|
|
return m_DllCache.QueryInterface(riid, ppvObject);
|
|
else if (InlineIsEqualGUID(guidService, __uuidof(IStencilCache)))
|
|
return m_StencilCache.QueryInterface(riid, ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
virtual HRESULT AddService(REFGUID guidService, REFIID riid, IUnknown *punk, HINSTANCE hInstance) throw()
|
|
{
|
|
if (!m_DllCache.AddRefModule(hInstance))
|
|
return E_FAIL;
|
|
|
|
ServiceNode srvNode;
|
|
srvNode.hInst = hInstance;
|
|
srvNode.punk = punk;
|
|
memcpy_s(&srvNode.guidService, sizeof(guidService), &guidService, sizeof(guidService));
|
|
memcpy_s(&srvNode.riid, sizeof(guidService), &riid, sizeof(riid));
|
|
|
|
// if the service is already there, return S_FALSE
|
|
int nIndex = m_serviceMap.Find(srvNode);
|
|
if (nIndex >= 0)
|
|
return S_FALSE;
|
|
|
|
if (!m_serviceMap.Add(srvNode))
|
|
return E_OUTOFMEMORY;
|
|
|
|
punk->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
virtual HRESULT RemoveService(REFGUID guidService, REFIID riid) throw()
|
|
{
|
|
ServiceNode srvNode;
|
|
memcpy_s(&srvNode.guidService, sizeof(guidService), &guidService, sizeof(guidService));
|
|
memcpy_s(&srvNode.riid, sizeof(riid), &riid, sizeof(riid));
|
|
int nIndex = m_serviceMap.Find(srvNode);
|
|
if (nIndex < 0)
|
|
return S_FALSE;
|
|
|
|
ATLASSERT(m_serviceMap[nIndex].punk != NULL);
|
|
m_serviceMap[nIndex].punk->Release();
|
|
|
|
HINSTANCE hInstRemove = m_serviceMap[nIndex].hInst;
|
|
|
|
m_serviceMap.RemoveAt(nIndex);
|
|
|
|
if (!m_DllCache.ReleaseModule(hInstRemove))
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GetService(REFGUID guidService, REFIID riid, void **ppvObject) throw()
|
|
{
|
|
if (!ppvObject)
|
|
return E_POINTER;
|
|
|
|
ServiceNode srvNode;
|
|
memcpy_s(&srvNode.guidService, sizeof(guidService), &guidService, sizeof(guidService));
|
|
memcpy_s(&srvNode.riid, sizeof(guidService), &riid, sizeof(riid));
|
|
|
|
int nIndex = m_serviceMap.Find(srvNode);
|
|
if (nIndex < 0)
|
|
return E_NOINTERFACE;
|
|
|
|
ATLASSERT(m_serviceMap[nIndex].punk != NULL);
|
|
return m_serviceMap[nIndex].punk->QueryInterface(riid, ppvObject);
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE AddRef()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE Release()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
HTTP_CODE LoadRequestHandler(LPCSTR szDllPath, LPCSTR szHandlerName, IHttpServerContext *pServerContext,
|
|
HINSTANCE *phInstance, IRequestHandler **ppHandler) throw()
|
|
{
|
|
return _AtlLoadRequestHandler(szDllPath, szHandlerName, pServerContext, phInstance,
|
|
ppHandler, this, static_cast<IDllCache*>(&m_DllCache));
|
|
} // LoadRequestHandler
|
|
|
|
|
|
HTTP_CODE TransferRequest(
|
|
AtlServerRequest *pRequest,
|
|
IServiceProvider *pServiceProvider,
|
|
IWriteStream *pWriteStream,
|
|
IHttpRequestLookup *pLookup,
|
|
LPCSTR szNewUrl,
|
|
WORD nCodePage,
|
|
bool bContinueAfterProcess,
|
|
CStencilState *pState)
|
|
{
|
|
return _AtlTransferRequest(pRequest, pServiceProvider, pWriteStream,
|
|
pLookup, szNewUrl, nCodePage, bContinueAfterProcess, pState);
|
|
}
|
|
};
|