mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-14 01:49:05 +00:00
add xmlrpc-c 1.03.14 to in tree libs
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3772 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
924
libs/xmlrpc-c/src/xmlrpc_server_w32httpsys.c
Normal file
924
libs/xmlrpc-c/src/xmlrpc_server_w32httpsys.c
Normal file
@@ -0,0 +1,924 @@
|
||||
/* Copyright (C) 2005 by Steven A. Bone, sbone@pobox.com. All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
** SUCH DAMAGE. */
|
||||
|
||||
/* COMPILATION NOTE:
|
||||
Note that the Platform SDK headers and
|
||||
link libraries for Windows XP SP2 or newer are required to compile
|
||||
xmlrpc-c for this module. If you are not using this server, it is
|
||||
safe to exclude the xmlrpc_server_w32httpsys.c file from the xmlrpc
|
||||
project and these dependencies will not be required. You can get the
|
||||
latest platform SDK at
|
||||
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
|
||||
Be sure after installation to choose the program to "register the PSDK
|
||||
directories with Visual Studio" so the newer headers are found.
|
||||
*/
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include "xmlrpc-c/base.h"
|
||||
#include "xmlrpc-c/server.h"
|
||||
#include "xmlrpc-c/server_w32httpsys.h"
|
||||
#include "version.h"
|
||||
|
||||
#if MUST_BUILD_HTTP_SYS_SERVER > 0
|
||||
|
||||
/* See compilation note above if this header is not found! */
|
||||
#include <http.h>
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#pragma comment( lib, "httpapi" )
|
||||
|
||||
|
||||
/* XXX - This variable is *not* currently threadsafe. Once the server has
|
||||
** been started, it must be treated as read-only. */
|
||||
static xmlrpc_registry *global_registryP;
|
||||
|
||||
//set TRUE if you want a log
|
||||
static BOOL g_bDebug;
|
||||
//set log filename
|
||||
static char g_fLogFile[MAX_PATH];
|
||||
//do you want OutputDebugString() to be called?
|
||||
static BOOL g_bDebugString;
|
||||
|
||||
//
|
||||
// Macros.
|
||||
//
|
||||
#define INITIALIZE_HTTP_RESPONSE( resp, status, reason ) \
|
||||
do \
|
||||
{ \
|
||||
RtlZeroMemory( (resp), sizeof(*(resp)) ); \
|
||||
(resp)->StatusCode = (status); \
|
||||
(resp)->pReason = (reason); \
|
||||
(resp)->ReasonLength = (USHORT) strlen(reason); \
|
||||
} while (FALSE)
|
||||
|
||||
|
||||
#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \
|
||||
do \
|
||||
{ \
|
||||
(Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \
|
||||
(Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \
|
||||
(USHORT) strlen(RawValue); \
|
||||
} while(FALSE)
|
||||
|
||||
#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
|
||||
#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
|
||||
|
||||
//
|
||||
// Prototypes for Internal Functions.
|
||||
//
|
||||
DWORD
|
||||
DoReceiveRequests(
|
||||
HANDLE hReqQueue,
|
||||
const xmlrpc_server_httpsys_parms * const parmsP
|
||||
);
|
||||
|
||||
DWORD
|
||||
SendHttpResponse(
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest,
|
||||
IN USHORT StatusCode,
|
||||
IN PSTR pReason,
|
||||
IN PSTR pEntity
|
||||
);
|
||||
|
||||
DWORD
|
||||
SendHttpResponseAuthRequired(
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest
|
||||
);
|
||||
|
||||
void
|
||||
processRPCCall(
|
||||
xmlrpc_env * const envP,
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest
|
||||
);
|
||||
|
||||
__inline void TraceA(const char *format, ...);
|
||||
__inline void TraceW(const wchar_t *format, ...);
|
||||
|
||||
|
||||
//
|
||||
// External Function Implementation.
|
||||
//
|
||||
|
||||
void
|
||||
xmlrpc_server_httpsys(
|
||||
xmlrpc_env * const envP,
|
||||
const xmlrpc_server_httpsys_parms * const parmsP,
|
||||
unsigned int const parm_size
|
||||
)
|
||||
{
|
||||
ULONG retCode;
|
||||
HANDLE hReqQueue = NULL;
|
||||
HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
|
||||
WCHAR wszURL[35];
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(envP);
|
||||
|
||||
if (parm_size < XMLRPC_HSSIZE(authfn))
|
||||
{
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_INTERNAL_ERROR,
|
||||
"You must specify members at least up through "
|
||||
"'authfn' in the server parameters argument. "
|
||||
"That would mean the parameter size would be >= %u "
|
||||
"but you specified a size of %u",
|
||||
XMLRPC_HSSIZE(authfn), parm_size);
|
||||
return;
|
||||
}
|
||||
|
||||
//Set logging options
|
||||
if (parmsP->logLevel>0)
|
||||
g_bDebug=TRUE;
|
||||
else
|
||||
g_bDebug=FALSE;
|
||||
|
||||
if (parmsP->logLevel>1)
|
||||
g_bDebugString=TRUE;
|
||||
else
|
||||
g_bDebugString=FALSE;
|
||||
|
||||
if (!parmsP->logFile)
|
||||
g_bDebug=FALSE;
|
||||
else
|
||||
StringCchPrintfA(g_fLogFile,MAX_PATH,parmsP->logFile);
|
||||
|
||||
//construct the URL we are listening on
|
||||
if (parmsP->useSSL!=0)
|
||||
StringCchPrintf(wszURL,35,L"https://+:%u/RPC2",parmsP->portNum);
|
||||
else
|
||||
StringCchPrintf(wszURL,35,L"http://+:%u/RPC2",parmsP->portNum);
|
||||
|
||||
global_registryP = parmsP->registryP;
|
||||
|
||||
// Initialize HTTP APIs.
|
||||
retCode = HttpInitialize(
|
||||
HttpApiVersion,
|
||||
HTTP_INITIALIZE_SERVER, // Flags
|
||||
NULL // Reserved
|
||||
);
|
||||
if (retCode != NO_ERROR)
|
||||
{
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_INTERNAL_ERROR,
|
||||
"HttpInitialize failed with %lu \n ",
|
||||
retCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a Request Queue Handle
|
||||
retCode = HttpCreateHttpHandle(
|
||||
&hReqQueue, // Req Queue
|
||||
0 // Reserved
|
||||
);
|
||||
if (retCode != NO_ERROR)
|
||||
{
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_INTERNAL_ERROR,
|
||||
"HttpCreateHttpHandle failed with %lu \n ",
|
||||
retCode);
|
||||
goto CleanUp;
|
||||
}
|
||||
|
||||
retCode = HttpAddUrl(
|
||||
hReqQueue, // Req Queue
|
||||
wszURL, // Fully qualified URL
|
||||
NULL // Reserved
|
||||
);
|
||||
|
||||
if (retCode != NO_ERROR)
|
||||
{
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_INTERNAL_ERROR,
|
||||
"HttpAddUrl failed with %lu \n ",
|
||||
retCode);
|
||||
goto CleanUp;
|
||||
}
|
||||
|
||||
TraceW( L"we are listening for requests on the following url: %ws\n", wszURL);
|
||||
|
||||
// Loop while receiving requests
|
||||
for(;;)
|
||||
{
|
||||
TraceW( L"Calling DoReceiveRequests()\n");
|
||||
retCode = DoReceiveRequests(hReqQueue, parmsP);
|
||||
if(NO_ERROR == retCode)
|
||||
{
|
||||
TraceW( L"DoReceiveRequests() returned NO_ERROR, breaking");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CleanUp:
|
||||
|
||||
TraceW( L"Tearing down the server.\n", wszURL);
|
||||
|
||||
// Call HttpRemoveUrl for the URL that we added.
|
||||
HttpRemoveUrl( hReqQueue, wszURL );
|
||||
|
||||
// Close the Request Queue handle.
|
||||
if(hReqQueue)
|
||||
CloseHandle(hReqQueue);
|
||||
|
||||
// Call HttpTerminate.
|
||||
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Internal Function Implementations.
|
||||
//
|
||||
|
||||
__inline void TraceA(const char *format, ...)
|
||||
{
|
||||
if(g_bDebug)
|
||||
{
|
||||
if (format)
|
||||
{
|
||||
va_list arglist;
|
||||
char str[4096];
|
||||
|
||||
va_start(arglist, format);
|
||||
if (g_fLogFile)
|
||||
{
|
||||
FILE *fout = fopen(g_fLogFile, "a+t");
|
||||
if (fout)
|
||||
{
|
||||
vfprintf(fout, format, arglist);
|
||||
fclose(fout);
|
||||
}
|
||||
}
|
||||
|
||||
StringCchVPrintfA(str,4096, format, arglist);
|
||||
printf(str);
|
||||
|
||||
if (g_bDebugString)
|
||||
{
|
||||
|
||||
OutputDebugStringA(str);
|
||||
}
|
||||
|
||||
va_end(arglist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__inline void TraceW(const wchar_t *format, ...)
|
||||
{
|
||||
if(g_bDebug)
|
||||
{
|
||||
if (format)
|
||||
{
|
||||
va_list arglist;
|
||||
wchar_t str[4096];
|
||||
|
||||
va_start(arglist, format);
|
||||
if (g_fLogFile)
|
||||
{
|
||||
FILE *fout = fopen(g_fLogFile, "a+t");
|
||||
if (fout)
|
||||
{
|
||||
vfwprintf(fout, format, arglist);
|
||||
fclose(fout);
|
||||
}
|
||||
}
|
||||
|
||||
StringCchVPrintfW(str, 4096, format, arglist);
|
||||
wprintf(str);
|
||||
|
||||
if (g_bDebugString)
|
||||
{
|
||||
OutputDebugStringW(str);
|
||||
}
|
||||
|
||||
va_end(arglist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a blocking function that merely sits on the request queue
|
||||
* for our URI and processes them one at a time. Once a request comes
|
||||
* in, we check it for content-type, content-length, and verb. As long
|
||||
* as the initial validations are done, we pass the request to the
|
||||
* processRPCCall() function, which collects the body of the request
|
||||
* and processes it. If we get an error back other than network type,
|
||||
* we are responsible for notifing the client.
|
||||
*/
|
||||
DWORD
|
||||
DoReceiveRequests(
|
||||
IN HANDLE hReqQueue,
|
||||
const xmlrpc_server_httpsys_parms * const parmsP
|
||||
)
|
||||
{
|
||||
ULONG result;
|
||||
HTTP_REQUEST_ID requestId;
|
||||
DWORD bytesRead;
|
||||
PHTTP_REQUEST pRequest;
|
||||
PCHAR pRequestBuffer;
|
||||
ULONG RequestBufferLength;
|
||||
xmlrpc_env env;
|
||||
char szHeaderBuf[255];
|
||||
long lContentLength;
|
||||
|
||||
// Allocate a 2K buffer. Should be good for most requests, we'll grow
|
||||
// this if required. We also need space for a HTTP_REQUEST structure.
|
||||
RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
|
||||
pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );
|
||||
if (pRequestBuffer == NULL)
|
||||
{
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
pRequest = (PHTTP_REQUEST)pRequestBuffer;
|
||||
|
||||
// Wait for a new request -- This is indicated by a NULL request ID.
|
||||
HTTP_SET_NULL_ID( &requestId );
|
||||
for(;;)
|
||||
{
|
||||
RtlZeroMemory(pRequest, RequestBufferLength);
|
||||
|
||||
result = HttpReceiveHttpRequest(
|
||||
hReqQueue, // Req Queue
|
||||
requestId, // Req ID
|
||||
0, // Flags
|
||||
pRequest, // HTTP request buffer
|
||||
RequestBufferLength,// req buffer length
|
||||
&bytesRead, // bytes received
|
||||
NULL // LPOVERLAPPED
|
||||
);
|
||||
|
||||
if(NO_ERROR == result)
|
||||
{
|
||||
// Got a request with a filled buffer.
|
||||
switch(pRequest->Verb)
|
||||
{
|
||||
case HttpVerbPOST:
|
||||
|
||||
TraceW(L"Got a POST request for %ws \n",pRequest->CookedUrl.pFullUrl);
|
||||
|
||||
//Check if we need use authorization.
|
||||
if(parmsP->authfn)
|
||||
{
|
||||
xmlrpc_env_init(&env);
|
||||
if(pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength<6)
|
||||
{
|
||||
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
|
||||
"Authorization header too short.");
|
||||
}
|
||||
else
|
||||
{
|
||||
//unencode the headers
|
||||
if(_strnicmp("basic ",pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue,6)!=0)
|
||||
{
|
||||
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
|
||||
"Authorization header is not of type basic.");
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlrpc_mem_block * decoded;
|
||||
|
||||
decoded = xmlrpc_base64_decode(&env,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue+6,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength-6);
|
||||
if(!env.fault_occurred)
|
||||
{
|
||||
char *pDecodedStr;
|
||||
char *pUser;
|
||||
char *pPass;
|
||||
char *pColon;
|
||||
|
||||
pDecodedStr = (char*)malloc(decoded->_size+1);
|
||||
memcpy(pDecodedStr,decoded->_block,decoded->_size);
|
||||
pDecodedStr[decoded->_size]='\0';
|
||||
pUser = pPass = pDecodedStr;
|
||||
pColon=strchr(pDecodedStr,':');
|
||||
if(pColon)
|
||||
{
|
||||
*pColon='\0';
|
||||
pPass=pColon+1;
|
||||
//The authfn should set env to fail if auth is denied.
|
||||
parmsP->authfn(&env,pUser,pPass);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
|
||||
"Decoded auth not of the correct format.");
|
||||
}
|
||||
free(pDecodedStr);
|
||||
}
|
||||
if(decoded)
|
||||
XMLRPC_MEMBLOCK_FREE(char, decoded);
|
||||
}
|
||||
}
|
||||
if(env.fault_occurred)
|
||||
{
|
||||
//request basic authorization, as the user did not provide it.
|
||||
xmlrpc_env_clean(&env);
|
||||
TraceW(L"POST request did not provide valid authorization header.");
|
||||
result = SendHttpResponseAuthRequired( hReqQueue, pRequest);
|
||||
break;
|
||||
}
|
||||
xmlrpc_env_clean(&env);
|
||||
}
|
||||
|
||||
//Check content type to make sure it is text/xml.
|
||||
memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentType].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength);
|
||||
szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength]='\0';
|
||||
if (_stricmp(szHeaderBuf,"text/xml")!=0)
|
||||
{
|
||||
//We only handle text/xml data. Anything else is not valid.
|
||||
TraceW(L"POST request had an unsupported content-type: %s \n", szHeaderBuf);
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
400,
|
||||
"Bad Request",
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
//Check content length to make sure it exists and is not too big.
|
||||
memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength);
|
||||
szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength]='\0';
|
||||
lContentLength = atol(szHeaderBuf);
|
||||
if (lContentLength<=0)
|
||||
{
|
||||
//Make sure a content length was supplied.
|
||||
TraceW(L"POST request did not include a content-length \n", szHeaderBuf);
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
411,
|
||||
"Length Required",
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
}
|
||||
if((size_t) lContentLength > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
|
||||
{
|
||||
//Content-length is too big for us to handle
|
||||
TraceW(L"POST request content-length is too big for us to handle: %d bytes \n", lContentLength);
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
500,
|
||||
"content-length too large",
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
//our initial validations of POST, content-type, and content-length
|
||||
//all check out. Collect and pass the complete buffer to the
|
||||
//XMLRPC-C library
|
||||
|
||||
xmlrpc_env_init(&env);
|
||||
processRPCCall(&env,hReqQueue, pRequest);
|
||||
if (env.fault_occurred)
|
||||
{
|
||||
//if we fail and it is anything other than a network error,
|
||||
//we should return a failure response to the client.
|
||||
if (env.fault_code != XMLRPC_NETWORK_ERROR)
|
||||
{
|
||||
if (env.fault_string)
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
500,
|
||||
env.fault_string,
|
||||
NULL
|
||||
);
|
||||
else
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
500,
|
||||
"Unknown Error",
|
||||
NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
xmlrpc_env_clean(&env);
|
||||
break;
|
||||
|
||||
default:
|
||||
//We only handle POST data. Anything else is not valid.
|
||||
TraceW(L"Got an unsupported Verb request for URI %ws \n", pRequest->CookedUrl.pFullUrl);
|
||||
|
||||
result = SendHttpResponse(
|
||||
hReqQueue,
|
||||
pRequest,
|
||||
405,
|
||||
"Method Not Allowed",
|
||||
NULL
|
||||
);
|
||||
break;
|
||||
}
|
||||
if(result != NO_ERROR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the Request ID so that we pick up the next request.
|
||||
HTTP_SET_NULL_ID( &requestId );
|
||||
}
|
||||
else if(result == ERROR_MORE_DATA)
|
||||
{
|
||||
// The input buffer was too small to hold the request headers
|
||||
// We have to allocate more buffer & call the API again.
|
||||
//
|
||||
// When we call the API again, we want to pick up the request
|
||||
// that just failed. This is done by passing a RequestID.
|
||||
// This RequestID is picked from the old buffer.
|
||||
requestId = pRequest->RequestId;
|
||||
|
||||
// Free the old buffer and allocate a new one.
|
||||
RequestBufferLength = bytesRead;
|
||||
FREE_MEM( pRequestBuffer );
|
||||
pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );
|
||||
|
||||
if (pRequestBuffer == NULL)
|
||||
{
|
||||
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
pRequest = (PHTTP_REQUEST)pRequestBuffer;
|
||||
|
||||
}
|
||||
else if(ERROR_CONNECTION_INVALID == result &&
|
||||
!HTTP_IS_NULL_ID(&requestId))
|
||||
{
|
||||
// The TCP connection got torn down by the peer when we were
|
||||
// trying to pick up a request with more buffer. We'll just move
|
||||
// onto the next request.
|
||||
HTTP_SET_NULL_ID( &requestId );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
} // for(;;)
|
||||
Cleanup:
|
||||
|
||||
if(pRequestBuffer)
|
||||
{
|
||||
FREE_MEM( pRequestBuffer );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* SendHttpResponse sends a text/html content type back with
|
||||
* the user specified status code and reason. Used for returning
|
||||
* errors to clients.
|
||||
*/
|
||||
DWORD
|
||||
SendHttpResponse(
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest,
|
||||
IN USHORT StatusCode,
|
||||
IN PSTR pReason,
|
||||
IN PSTR pEntityString
|
||||
)
|
||||
{
|
||||
HTTP_RESPONSE response;
|
||||
HTTP_DATA_CHUNK dataChunk;
|
||||
DWORD result;
|
||||
DWORD bytesSent;
|
||||
CHAR szServerHeader[20];
|
||||
|
||||
// Initialize the HTTP response structure.
|
||||
INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
|
||||
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
|
||||
|
||||
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
|
||||
|
||||
if(pEntityString)
|
||||
{
|
||||
// Add an entity chunk
|
||||
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
||||
dataChunk.FromMemory.pBuffer = pEntityString;
|
||||
dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString);
|
||||
|
||||
response.EntityChunkCount = 1;
|
||||
response.pEntityChunks = &dataChunk;
|
||||
}
|
||||
|
||||
// Since we are sending all the entity body in one call, we don't have
|
||||
// to specify the Content-Length.
|
||||
result = HttpSendHttpResponse(
|
||||
hReqQueue, // ReqQueueHandle
|
||||
pRequest->RequestId, // Request ID
|
||||
0, // Flags
|
||||
&response, // HTTP response
|
||||
NULL, // pReserved1
|
||||
&bytesSent, // bytes sent (OPTIONAL)
|
||||
NULL, // pReserved2 (must be NULL)
|
||||
0, // Reserved3 (must be 0)
|
||||
NULL, // LPOVERLAPPED (OPTIONAL)
|
||||
NULL // pReserved4 (must be NULL)
|
||||
);
|
||||
|
||||
if(result != NO_ERROR)
|
||||
{
|
||||
TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* SendHttpResponseAuthRequired sends a 401 status code requesting authorization
|
||||
*/
|
||||
DWORD
|
||||
SendHttpResponseAuthRequired(
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest
|
||||
)
|
||||
{
|
||||
HTTP_RESPONSE response;
|
||||
DWORD result;
|
||||
DWORD bytesSent;
|
||||
CHAR szServerHeader[20];
|
||||
|
||||
// Initialize the HTTP response structure.
|
||||
INITIALIZE_HTTP_RESPONSE(&response, 401, "Authentication Required");
|
||||
|
||||
// Add the WWW_Authenticate header.
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderWwwAuthenticate, "Basic realm=\"xmlrpc\"");
|
||||
|
||||
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
|
||||
|
||||
// Since we are sending all the entity body in one call, we don't have
|
||||
// to specify the Content-Length.
|
||||
result = HttpSendHttpResponse(
|
||||
hReqQueue, // ReqQueueHandle
|
||||
pRequest->RequestId, // Request ID
|
||||
0, // Flags
|
||||
&response, // HTTP response
|
||||
NULL, // pReserved1
|
||||
&bytesSent, // bytes sent (OPTIONAL)
|
||||
NULL, // pReserved2 (must be NULL)
|
||||
0, // Reserved3 (must be 0)
|
||||
NULL, // LPOVERLAPPED (OPTIONAL)
|
||||
NULL // pReserved4 (must be NULL)
|
||||
);
|
||||
|
||||
if(result != NO_ERROR)
|
||||
{
|
||||
TraceW(L"SendHttpResponseAuthRequired failed with %lu \n", result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* processRPCCall() is called after some validations. The assumption is that
|
||||
* the request is an HTTP post of content-type text/xml with a content-length
|
||||
* that is less than the maximum the library can handle.
|
||||
*
|
||||
* The caller should check the error status, and if the error was other than
|
||||
* a network type, respond back to the client to let them know the call failed.
|
||||
*/
|
||||
void
|
||||
processRPCCall(
|
||||
xmlrpc_env * const envP,
|
||||
IN HANDLE hReqQueue,
|
||||
IN PHTTP_REQUEST pRequest
|
||||
)
|
||||
{
|
||||
HTTP_RESPONSE response;
|
||||
DWORD result;
|
||||
DWORD bytesSent;
|
||||
PUCHAR pEntityBuffer;
|
||||
ULONG EntityBufferLength;
|
||||
ULONG BytesRead;
|
||||
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
|
||||
CHAR szContentLength[MAX_ULONG_STR];
|
||||
CHAR szServerHeader[20];
|
||||
HTTP_DATA_CHUNK dataChunk;
|
||||
ULONG TotalBytesRead = 0;
|
||||
xmlrpc_mem_block * body;
|
||||
xmlrpc_mem_block * output;
|
||||
|
||||
BytesRead = 0;
|
||||
body = NULL;
|
||||
output = NULL;
|
||||
|
||||
// Allocate some space for an entity buffer.
|
||||
EntityBufferLength = 2048;
|
||||
pEntityBuffer = (PUCHAR) ALLOC_MEM( EntityBufferLength );
|
||||
if (pEntityBuffer == NULL)
|
||||
{
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_INTERNAL_ERROR,
|
||||
"Out of Memory");
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
|
||||
// flag with HttpReceiveHttpRequest(), the entity would have
|
||||
// been a part of HTTP_REQUEST (using the pEntityChunks field).
|
||||
// Since we have not passed that flag, we can be assured that
|
||||
// there are no entity bodies in HTTP_REQUEST.
|
||||
if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
|
||||
{
|
||||
//Allocate some space for an XMLRPC memory block.
|
||||
body = xmlrpc_mem_block_new(envP, 0);
|
||||
if (envP->fault_occurred)
|
||||
goto Done;
|
||||
|
||||
// The entity body can be sent over multiple calls. Let's collect all
|
||||
// of these in a buffer and send the buffer to the xmlrpc-c library
|
||||
do
|
||||
{
|
||||
// Read the entity chunk from the request.
|
||||
BytesRead = 0;
|
||||
result = HttpReceiveRequestEntityBody(
|
||||
hReqQueue,
|
||||
pRequest->RequestId,
|
||||
0,
|
||||
pEntityBuffer,
|
||||
EntityBufferLength,
|
||||
&BytesRead,
|
||||
NULL
|
||||
);
|
||||
switch(result)
|
||||
{
|
||||
case NO_ERROR:
|
||||
if(BytesRead != 0)
|
||||
{
|
||||
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
|
||||
pEntityBuffer, BytesRead);
|
||||
if(envP->fault_occurred)
|
||||
goto Done;
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR_HANDLE_EOF:
|
||||
// We have read the last request entity body. We can now
|
||||
// process the suppossed XMLRPC data.
|
||||
if(BytesRead != 0)
|
||||
{
|
||||
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
|
||||
pEntityBuffer, BytesRead);
|
||||
if(envP->fault_occurred)
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// We will send the response over multiple calls.
|
||||
// This is achieved by passing the
|
||||
// HTTP_SEND_RESPONSE_FLAG_MORE_DATA flag.
|
||||
|
||||
// NOTE: Since we are accumulating the TotalBytesRead in
|
||||
// a ULONG, this will not work for entity bodies that
|
||||
// are larger than 4 GB. For supporting large entity
|
||||
// bodies, we would have to use a ULONGLONG.
|
||||
TraceA("xmlrpc_server RPC2 handler processing RPC request.\n");
|
||||
|
||||
// Process the RPC.
|
||||
output = xmlrpc_registry_process_call(
|
||||
envP, global_registryP, NULL,
|
||||
XMLRPC_MEMBLOCK_CONTENTS(char, body),
|
||||
XMLRPC_MEMBLOCK_SIZE(char, body));
|
||||
if (envP->fault_occurred)
|
||||
goto Done;
|
||||
|
||||
// Initialize the HTTP response structure.
|
||||
INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
|
||||
|
||||
//Add the content-length
|
||||
StringCchPrintfA(szContentLength,MAX_ULONG_STR, "%lu",
|
||||
XMLRPC_MEMBLOCK_SIZE(char, output));
|
||||
ADD_KNOWN_HEADER(
|
||||
response,
|
||||
HttpHeaderContentLength,
|
||||
szContentLength );
|
||||
|
||||
//Add the content-type
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/xml");
|
||||
|
||||
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
|
||||
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
|
||||
|
||||
//send the response
|
||||
result = HttpSendHttpResponse(
|
||||
hReqQueue, // ReqQueueHandle
|
||||
pRequest->RequestId, // Request ID
|
||||
HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
|
||||
&response, // HTTP response
|
||||
NULL, // pReserved1
|
||||
&bytesSent, // bytes sent (optional)
|
||||
NULL, // pReserved2
|
||||
0, // Reserved3
|
||||
NULL, // LPOVERLAPPED
|
||||
NULL // pReserved4
|
||||
);
|
||||
if(result != NO_ERROR)
|
||||
{
|
||||
TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_NETWORK_ERROR,
|
||||
"HttpSendHttpResponse failed with %lu", result);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// Send entity body from a memory chunk.
|
||||
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
||||
dataChunk.FromMemory.BufferLength = (ULONG)XMLRPC_MEMBLOCK_SIZE(char, output);
|
||||
dataChunk.FromMemory.pBuffer = XMLRPC_MEMBLOCK_CONTENTS(char, output);
|
||||
|
||||
result = HttpSendResponseEntityBody(
|
||||
hReqQueue,
|
||||
pRequest->RequestId,
|
||||
0, // This is the last send.
|
||||
1, // Entity Chunk Count.
|
||||
&dataChunk,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
if(result != NO_ERROR)
|
||||
{
|
||||
TraceW(L"HttpSendResponseEntityBody failed with %lu \n", result);
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_NETWORK_ERROR,
|
||||
"HttpSendResponseEntityBody failed with %lu", result);
|
||||
goto Done;
|
||||
}
|
||||
goto Done;
|
||||
break;
|
||||
default:
|
||||
TraceW(L"HttpReceiveRequestEntityBody failed with %lu \n", result);
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_NETWORK_ERROR,
|
||||
"HttpReceiveRequestEntityBody failed with %lu", result);
|
||||
goto Done;
|
||||
}
|
||||
} while(TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This request does not have an entity body.
|
||||
TraceA("Received a bad request (no body in HTTP post).\n");
|
||||
xmlrpc_env_set_fault_formatted(
|
||||
envP, XMLRPC_PARSE_ERROR,
|
||||
"Bad POST request (no body)");
|
||||
goto Done;
|
||||
}
|
||||
|
||||
Done:
|
||||
|
||||
if(pEntityBuffer)
|
||||
FREE_MEM(pEntityBuffer);
|
||||
|
||||
if(output)
|
||||
XMLRPC_MEMBLOCK_FREE(char, output);
|
||||
|
||||
if(body)
|
||||
XMLRPC_MEMBLOCK_FREE(char, body);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* #if MUST_BUILD_HTTP_SYS_SERVER <> 0 */
|
Reference in New Issue
Block a user