initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_server_w32httpsys.c
1 /* Copyright (C) 2005 by Steven A. Bone, sbone@pobox.com. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 ** 1. Redistributions of source code must retain the above copyright
7 **    notice, this list of conditions and the following disclaimer.
8 ** 2. Redistributions in binary form must reproduce the above copyright
9 **    notice, this list of conditions and the following disclaimer in the
10 **    documentation and/or other materials provided with the distribution.
11 ** 3. The name of the author may not be used to endorse or promote products
12 **    derived from this software without specific prior written permission. 
13 **  
14 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ** SUCH DAMAGE. */
25
26 /* COMPILATION NOTE:
27    Note that the Platform SDK headers and
28    link libraries for Windows XP SP2 or newer are required to compile
29    xmlrpc-c for this module.  If you are not using this server, it is 
30    safe to exclude the xmlrpc_server_w32httpsys.c file from the xmlrpc
31    project and these dependencies will not be required.  You can get the 
32    latest platform SDK at 
33    http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
34    Be sure after installation to choose the program to "register the PSDK
35    directories with Visual Studio" so the newer headers are found.
36 */
37
38 #ifndef UNICODE
39 #define UNICODE
40 #endif
41
42 #ifndef _UNICODE
43 #define _UNICODE
44 #endif
45
46 #include "xmlrpc-c/base.h"
47 #include "xmlrpc-c/server.h"
48 #include "xmlrpc-c/server_w32httpsys.h"
49 #include "version.h"
50
51 #if MUST_BUILD_HTTP_SYS_SERVER > 0
52
53 /* See compilation note above if this header is not found! */
54 #include <http.h>
55 #include <windows.h>
56 #include <strsafe.h>
57
58 #pragma comment( lib, "httpapi" )
59
60
61 /* XXX - This variable is *not* currently threadsafe. Once the server has
62 ** been started, it must be treated as read-only. */
63 static xmlrpc_registry *global_registryP;
64
65 //set TRUE if you want a log
66 static BOOL g_bDebug;
67 //set log filename
68 static char g_fLogFile[MAX_PATH];
69 //do you want OutputDebugString() to be called?
70 static BOOL g_bDebugString;
71
72 //
73 // Macros.
74 //
75 #define INITIALIZE_HTTP_RESPONSE( resp, status, reason )                    \
76     do                                                                      \
77     {                                                                       \
78         RtlZeroMemory( (resp), sizeof(*(resp)) );                           \
79         (resp)->StatusCode = (status);                                      \
80         (resp)->pReason = (reason);                                         \
81         (resp)->ReasonLength = (USHORT) strlen(reason);                     \
82     } while (FALSE)
83
84
85 #define ADD_KNOWN_HEADER(Response, HeaderId, RawValue)                      \
86     do                                                                      \
87     {                                                                       \
88         (Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \
89         (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength =        \
90             (USHORT) strlen(RawValue);                                      \
91     } while(FALSE)
92
93 #define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
94 #define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
95
96 //
97 // Prototypes for Internal Functions.
98 //
99 DWORD
100 DoReceiveRequests(
101     HANDLE hReqQueue,
102         const xmlrpc_server_httpsys_parms * const parmsP
103     );
104
105 DWORD
106 SendHttpResponse(
107     IN HANDLE        hReqQueue,
108     IN PHTTP_REQUEST pRequest,
109     IN USHORT        StatusCode,
110     IN PSTR          pReason,
111     IN PSTR          pEntity
112     );
113
114 DWORD
115 SendHttpResponseAuthRequired(
116     IN HANDLE        hReqQueue,
117     IN PHTTP_REQUEST pRequest
118     );
119
120 void
121 processRPCCall(
122     xmlrpc_env *     const envP,
123     IN HANDLE        hReqQueue,
124     IN PHTTP_REQUEST pRequest
125     );
126
127 __inline void TraceA(const char *format, ...);
128 __inline void TraceW(const wchar_t *format, ...);
129
130
131 //
132 // External Function Implementation.
133 //
134
135 void
136 xmlrpc_server_httpsys(
137         xmlrpc_env *                        const envP,
138     const xmlrpc_server_httpsys_parms * const parmsP,
139     unsigned int                        const parm_size
140         )
141 {
142         ULONG           retCode;
143     HANDLE          hReqQueue      = NULL;
144     HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
145         WCHAR           wszURL[35];
146
147         XMLRPC_ASSERT_ENV_OK(envP);
148
149     if (parm_size < XMLRPC_HSSIZE(authfn))
150         {
151         xmlrpc_env_set_fault_formatted(
152             envP, XMLRPC_INTERNAL_ERROR,
153             "You must specify members at least up through "
154             "'authfn' in the server parameters argument.  "
155             "That would mean the parameter size would be >= %u "
156             "but you specified a size of %u",
157             XMLRPC_HSSIZE(authfn), parm_size);
158                 return;
159         }
160
161         //Set logging options
162         if (parmsP->logLevel>0)
163                 g_bDebug=TRUE;
164         else
165                 g_bDebug=FALSE;
166
167         if (parmsP->logLevel>1)
168                 g_bDebugString=TRUE;
169         else
170                 g_bDebugString=FALSE;
171
172         if (!parmsP->logFile)
173                 g_bDebug=FALSE;
174         else
175                 StringCchPrintfA(g_fLogFile,MAX_PATH,parmsP->logFile);
176
177         //construct the URL we are listening on
178         if (parmsP->useSSL!=0)
179                 StringCchPrintf(wszURL,35,L"https://+:%u/RPC2",parmsP->portNum);
180         else
181                 StringCchPrintf(wszURL,35,L"http://+:%u/RPC2",parmsP->portNum);
182
183         global_registryP = parmsP->registryP;
184
185         // Initialize HTTP APIs.
186         retCode = HttpInitialize( 
187                                 HttpApiVersion,
188                                 HTTP_INITIALIZE_SERVER,    // Flags
189                                 NULL                       // Reserved
190                                 );
191         if (retCode != NO_ERROR)
192         {
193                 xmlrpc_env_set_fault_formatted(
194                         envP, XMLRPC_INTERNAL_ERROR,
195                         "HttpInitialize failed with %lu \n ",
196                         retCode);
197                 return;
198         }
199
200         // Create a Request Queue Handle
201         retCode = HttpCreateHttpHandle(
202                                 &hReqQueue,        // Req Queue
203                                 0                  // Reserved
204                                 );
205         if (retCode != NO_ERROR)
206         { 
207                 xmlrpc_env_set_fault_formatted(
208                         envP, XMLRPC_INTERNAL_ERROR,
209                         "HttpCreateHttpHandle failed with %lu \n ",
210                         retCode);
211                 goto CleanUp;
212         }
213
214         retCode = HttpAddUrl(
215                                 hReqQueue,    // Req Queue
216                                 wszURL,      // Fully qualified URL
217                                 NULL          // Reserved
218                                 );
219
220         if (retCode != NO_ERROR)
221         {
222                 xmlrpc_env_set_fault_formatted(
223                         envP, XMLRPC_INTERNAL_ERROR,
224                         "HttpAddUrl failed with %lu \n ",
225                         retCode);
226                 goto CleanUp;
227         }
228
229         TraceW( L"we are listening for requests on the following url: %ws\n", wszURL);
230
231         // Loop while receiving requests
232         for(;;)
233         {
234                 TraceW( L"Calling DoReceiveRequests()\n");
235                 retCode = DoReceiveRequests(hReqQueue, parmsP);
236                 if(NO_ERROR == retCode)
237                 {
238                         TraceW( L"DoReceiveRequests() returned NO_ERROR, breaking");
239                         break;
240                 }
241         }
242
243 CleanUp:
244
245         TraceW( L"Tearing down the server.\n", wszURL);
246
247         // Call HttpRemoveUrl for the URL that we added.
248         HttpRemoveUrl( hReqQueue, wszURL );
249
250         // Close the Request Queue handle.
251         if(hReqQueue)
252                 CloseHandle(hReqQueue);
253
254         // Call HttpTerminate.
255         HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
256         return;
257 }
258
259 //
260 // Internal Function Implementations.
261 //
262
263 __inline void TraceA(const char *format, ...)
264 {
265         if(g_bDebug)
266         {
267                 if (format)
268                 {
269                         va_list arglist;
270                         char str[4096];
271
272                         va_start(arglist, format);
273                         if (g_fLogFile)
274                         {
275                                 FILE *fout = fopen(g_fLogFile, "a+t");
276                                 if (fout)
277                                 {
278                                         vfprintf(fout, format, arglist);
279                                         fclose(fout);
280                                 }
281                         }
282
283                         StringCchVPrintfA(str,4096, format, arglist);
284                         printf(str);
285
286                         if (g_bDebugString)
287                         {
288                                 
289                                 OutputDebugStringA(str);
290                         }
291
292                         va_end(arglist);
293                 }
294         }
295 }
296
297 __inline void TraceW(const wchar_t *format, ...)
298 {
299         if(g_bDebug)
300         {
301                 if (format)
302                 {
303                         va_list arglist;
304                         wchar_t str[4096];
305
306                         va_start(arglist, format);
307                         if (g_fLogFile)
308                         {
309                                 FILE *fout = fopen(g_fLogFile, "a+t");
310                                 if (fout)
311                                 {
312                                         vfwprintf(fout, format, arglist);
313                                         fclose(fout);
314                                 }
315                         }
316                         
317                         StringCchVPrintfW(str, 4096, format, arglist);
318                         wprintf(str);
319                         
320                         if (g_bDebugString)
321                         {                               
322                                 OutputDebugStringW(str);
323                         }
324
325                         va_end(arglist);
326                 }
327         }
328 }
329
330 /*
331  * This is a blocking function that merely sits on the request queue
332  * for our URI and processes them one at a time.  Once a request comes
333  * in, we check it for content-type, content-length, and verb.  As long
334  * as the initial validations are done, we pass the request to the 
335  * processRPCCall() function, which collects the body of the request
336  * and processes it.  If we get an error back other than network type,
337  * we are responsible for notifing the client.
338  */
339 DWORD
340 DoReceiveRequests(
341     IN HANDLE hReqQueue,
342         const xmlrpc_server_httpsys_parms * const parmsP
343     )
344 {
345     ULONG              result;
346     HTTP_REQUEST_ID    requestId;
347     DWORD              bytesRead;
348     PHTTP_REQUEST      pRequest;
349     PCHAR              pRequestBuffer;
350     ULONG              RequestBufferLength;
351         xmlrpc_env                      env;
352         char                            szHeaderBuf[255];
353         long                            lContentLength;
354
355     // Allocate a 2K buffer. Should be good for most requests, we'll grow 
356     // this if required. We also need space for a HTTP_REQUEST structure.
357     RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
358     pRequestBuffer      = (PCHAR) ALLOC_MEM( RequestBufferLength );
359     if (pRequestBuffer == NULL)
360     {
361         return ERROR_NOT_ENOUGH_MEMORY;
362     }
363
364     pRequest = (PHTTP_REQUEST)pRequestBuffer;
365
366     // Wait for a new request -- This is indicated by a NULL request ID.
367     HTTP_SET_NULL_ID( &requestId );
368     for(;;)
369     {
370         RtlZeroMemory(pRequest, RequestBufferLength);
371
372         result = HttpReceiveHttpRequest(
373                     hReqQueue,          // Req Queue
374                     requestId,          // Req ID
375                     0,                  // Flags
376                     pRequest,           // HTTP request buffer
377                     RequestBufferLength,// req buffer length
378                     &bytesRead,         // bytes received
379                     NULL                // LPOVERLAPPED
380                     );
381
382         if(NO_ERROR == result)
383         {
384             // Got a request with a filled buffer.
385             switch(pRequest->Verb)
386             {
387                 case HttpVerbPOST:
388
389                                         TraceW(L"Got a POST request for %ws \n",pRequest->CookedUrl.pFullUrl);                          
390                                         
391                                         //Check if we need use authorization.
392                                         if(parmsP->authfn)
393                                         {
394                                                 xmlrpc_env_init(&env);
395                                                 if(pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength<6)
396                                                 {
397                                                         xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, 
398                                                                 "Authorization header too short.");
399                                                 }
400                                                 else
401                                                 {
402                                                         //unencode the headers
403                                                         if(_strnicmp("basic ",pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue,6)!=0)
404                                                         {
405                                                                 xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, 
406                                                                         "Authorization header is not of type basic.");
407                                                         }
408                                                         else
409                                                         {
410                                                                 xmlrpc_mem_block * decoded;
411                                                                 
412                                                                 decoded = xmlrpc_base64_decode(&env,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue+6,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength-6);
413                                                                 if(!env.fault_occurred)
414                                                                 {
415                                                                         char *pDecodedStr;
416                                                                         char *pUser;
417                                                                         char *pPass;
418                                                                         char *pColon;
419
420                                                                         pDecodedStr = (char*)malloc(decoded->_size+1);
421                                                                         memcpy(pDecodedStr,decoded->_block,decoded->_size);
422                                                                         pDecodedStr[decoded->_size]='\0';
423                                                                         pUser = pPass = pDecodedStr;
424                                                                         pColon=strchr(pDecodedStr,':');
425                                                                         if(pColon)
426                                                                         {
427                                                                                 *pColon='\0';
428                                                                                 pPass=pColon+1;
429                                                                                 //The authfn should set env to fail if auth is denied.
430                                                                                 parmsP->authfn(&env,pUser,pPass);
431                                                                         }
432                                                                         else
433                                                                         {
434                                                                                 xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, 
435                                                                                         "Decoded auth not of the correct format.");
436                                                                         }
437                                                                         free(pDecodedStr);
438                                                                 }
439                                                                 if(decoded)
440                                                                         XMLRPC_MEMBLOCK_FREE(char, decoded);
441                                                         }
442                                                 }
443                                                 if(env.fault_occurred)
444                                                 {
445                                                         //request basic authorization, as the user did not provide it.
446                                                         xmlrpc_env_clean(&env);
447                                                         TraceW(L"POST request did not provide valid authorization header.");
448                                                         result = SendHttpResponseAuthRequired( hReqQueue, pRequest);
449                                                         break;
450                                                 }
451                                                 xmlrpc_env_clean(&env);
452                                         }
453                                         
454                                         //Check content type to make sure it is text/xml.
455                                         memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentType].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength);
456                                         szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength]='\0';
457                                         if (_stricmp(szHeaderBuf,"text/xml")!=0)
458                                         {
459                                                 //We only handle text/xml data.  Anything else is not valid.
460                                                 TraceW(L"POST request had an unsupported content-type: %s \n", szHeaderBuf);
461                                                 result = SendHttpResponse(
462                                                                         hReqQueue, 
463                                                                         pRequest,
464                                                                         400,
465                                                                         "Bad Request",
466                                                                         NULL
467                                                                         );
468                                                 break;
469                                         }
470
471                                         //Check content length to make sure it exists and is not too big.
472                                         memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength);
473                                         szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength]='\0';
474                                         lContentLength = atol(szHeaderBuf);
475                                         if (lContentLength<=0)
476                                         {
477                                                 //Make sure a content length was supplied.
478                                                 TraceW(L"POST request did not include a content-length \n", szHeaderBuf);
479                                                 result = SendHttpResponse(
480                                                                         hReqQueue, 
481                                                                         pRequest,
482                                                                         411,
483                                                                         "Length Required",
484                                                                         NULL
485                                                                         );
486                                                 break;
487                                         }                                               
488                                         if((size_t) lContentLength > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
489                                         {
490                                                 //Content-length is too big for us to handle
491                                                 TraceW(L"POST request content-length is too big for us to handle: %d bytes \n", lContentLength);
492                                                 result = SendHttpResponse(
493                                                                         hReqQueue, 
494                                                                         pRequest,
495                                                                         500,
496                                                                         "content-length too large",
497                                                                         NULL
498                                                                         );
499                                                 break;
500                                         }
501
502                                         //our initial validations of POST, content-type, and content-length
503                                         //all check out.  Collect and pass the complete buffer to the 
504                                         //XMLRPC-C library
505                                         
506                                         xmlrpc_env_init(&env);
507                     processRPCCall(&env,hReqQueue, pRequest);
508                                         if (env.fault_occurred) 
509                                         {
510                                                 //if we fail and it is anything other than a network error,
511                                                 //we should return a failure response to the client.
512                                                 if (env.fault_code != XMLRPC_NETWORK_ERROR)
513                                                 {
514                                                         if (env.fault_string)
515                                                                 result = SendHttpResponse(
516                                                                         hReqQueue, 
517                                                                         pRequest,
518                                                                         500,
519                                                                         env.fault_string,
520                                                                         NULL
521                                                                         );
522                                                         else
523                                                                 result = SendHttpResponse(
524                                                                         hReqQueue, 
525                                                                         pRequest,
526                                                                         500,
527                                                                         "Unknown Error",
528                                                                         NULL
529                                                                         );
530                                                 }
531                                         }
532                                         
533                                         xmlrpc_env_clean(&env);
534                     break;
535
536                 default:
537                                         //We only handle POST data.  Anything else is not valid.
538                     TraceW(L"Got an unsupported Verb request for URI %ws \n", pRequest->CookedUrl.pFullUrl);
539                         
540                     result = SendHttpResponse(
541                                 hReqQueue, 
542                                 pRequest,
543                                 405,
544                                 "Method Not Allowed",
545                                 NULL
546                                 );
547                     break;
548             }
549             if(result != NO_ERROR)
550             {
551                 break;
552             }
553
554             // Reset the Request ID so that we pick up the next request.
555             HTTP_SET_NULL_ID( &requestId );
556         }
557         else if(result == ERROR_MORE_DATA)
558         {
559             // The input buffer was too small to hold the request headers
560             // We have to allocate more buffer & call the API again. 
561             //
562             // When we call the API again, we want to pick up the request
563             // that just failed. This is done by passing a RequestID.
564             // This RequestID is picked from the old buffer.
565             requestId = pRequest->RequestId;
566
567             // Free the old buffer and allocate a new one.
568             RequestBufferLength = bytesRead;
569             FREE_MEM( pRequestBuffer );
570             pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );
571
572             if (pRequestBuffer == NULL)
573             {
574                 result = ERROR_NOT_ENOUGH_MEMORY;
575                 break;
576             }
577
578             pRequest = (PHTTP_REQUEST)pRequestBuffer;
579
580         }
581         else if(ERROR_CONNECTION_INVALID == result && 
582                 !HTTP_IS_NULL_ID(&requestId))
583         {
584             // The TCP connection got torn down by the peer when we were
585             // trying to pick up a request with more buffer. We'll just move
586             // onto the next request.            
587             HTTP_SET_NULL_ID( &requestId );
588         }
589         else
590         {
591             break;
592         }
593
594     } // for(;;)
595 Cleanup:
596
597     if(pRequestBuffer)
598     {
599         FREE_MEM( pRequestBuffer );
600     }
601
602     return result;
603 }
604
605 /*
606  * SendHttpResponse sends a text/html content type back with
607  * the user specified status code and reason.  Used for returning
608  * errors to clients.
609  */
610 DWORD
611 SendHttpResponse(
612     IN HANDLE        hReqQueue,
613     IN PHTTP_REQUEST pRequest,
614     IN USHORT        StatusCode,
615     IN PSTR          pReason,
616     IN PSTR          pEntityString
617     )
618 {
619     HTTP_RESPONSE   response;
620     HTTP_DATA_CHUNK dataChunk;
621     DWORD           result;
622     DWORD           bytesSent;
623         CHAR                    szServerHeader[20];
624
625     // Initialize the HTTP response structure.
626     INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
627
628     ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
629         
630         StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);                                    
631         ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
632    
633     if(pEntityString)
634     {
635         // Add an entity chunk
636         dataChunk.DataChunkType           = HttpDataChunkFromMemory;
637         dataChunk.FromMemory.pBuffer      = pEntityString;
638         dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString);
639
640         response.EntityChunkCount         = 1;
641         response.pEntityChunks            = &dataChunk;
642     }
643
644     // Since we are sending all the entity body in one call, we don't have 
645     // to specify the Content-Length.
646     result = HttpSendHttpResponse(
647                     hReqQueue,           // ReqQueueHandle
648                     pRequest->RequestId, // Request ID
649                     0,                   // Flags
650                     &response,           // HTTP response
651                     NULL,                // pReserved1
652                     &bytesSent,          // bytes sent   (OPTIONAL)
653                     NULL,                // pReserved2   (must be NULL)
654                     0,                   // Reserved3    (must be 0)
655                     NULL,                // LPOVERLAPPED (OPTIONAL)
656                     NULL                 // pReserved4   (must be NULL)
657                     );
658
659     if(result != NO_ERROR)
660     {
661                 TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
662     }
663
664     return result;
665 }
666
667 /*
668  * SendHttpResponseAuthRequired sends a 401 status code requesting authorization
669  */
670 DWORD
671 SendHttpResponseAuthRequired(
672     IN HANDLE        hReqQueue,
673     IN PHTTP_REQUEST pRequest
674     )
675 {
676     HTTP_RESPONSE   response;
677     DWORD           result;
678     DWORD           bytesSent;
679         CHAR                    szServerHeader[20];
680
681     // Initialize the HTTP response structure.
682     INITIALIZE_HTTP_RESPONSE(&response, 401, "Authentication Required");
683
684     // Add the WWW_Authenticate header.
685     ADD_KNOWN_HEADER(response, HttpHeaderWwwAuthenticate, "Basic realm=\"xmlrpc\"");
686         
687         StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);                                    
688         ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
689    
690     // Since we are sending all the entity body in one call, we don't have 
691     // to specify the Content-Length.
692     result = HttpSendHttpResponse(
693                     hReqQueue,           // ReqQueueHandle
694                     pRequest->RequestId, // Request ID
695                     0,                   // Flags
696                     &response,           // HTTP response
697                     NULL,                // pReserved1
698                     &bytesSent,          // bytes sent   (OPTIONAL)
699                     NULL,                // pReserved2   (must be NULL)
700                     0,                   // Reserved3    (must be 0)
701                     NULL,                // LPOVERLAPPED (OPTIONAL)
702                     NULL                 // pReserved4   (must be NULL)
703                     );
704
705     if(result != NO_ERROR)
706     {
707                 TraceW(L"SendHttpResponseAuthRequired failed with %lu \n", result);
708     }
709
710     return result;
711 }
712
713 /*
714  * processRPCCall() is called after some validations.  The assumption is that
715  * the request is an HTTP post of content-type text/xml with a content-length
716  * that is less than the maximum the library can handle.
717  *
718  * The caller should check the error status, and if the error was other than
719  * a network type, respond back to the client to let them know the call failed.
720  */
721 void
722 processRPCCall(
723     xmlrpc_env *     const envP,
724     IN HANDLE        hReqQueue,
725     IN PHTTP_REQUEST pRequest
726     )
727 {
728     HTTP_RESPONSE   response;
729     DWORD           result;
730     DWORD           bytesSent;
731     PUCHAR          pEntityBuffer;
732     ULONG           EntityBufferLength;
733     ULONG           BytesRead;
734 #define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
735     CHAR            szContentLength[MAX_ULONG_STR];
736         CHAR                    szServerHeader[20];
737     HTTP_DATA_CHUNK dataChunk;
738     ULONG           TotalBytesRead = 0;
739         xmlrpc_mem_block * body;
740         xmlrpc_mem_block * output;
741
742     BytesRead  = 0;
743         body       = NULL;
744         output     = NULL;
745
746     // Allocate some space for an entity buffer.
747     EntityBufferLength = 2048;  
748     pEntityBuffer      = (PUCHAR) ALLOC_MEM( EntityBufferLength );
749     if (pEntityBuffer == NULL)
750     {
751                 xmlrpc_env_set_fault_formatted(
752                                 envP, XMLRPC_INTERNAL_ERROR,
753                                 "Out of Memory");
754         goto Done;
755     }
756
757         // NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
758     //       flag with HttpReceiveHttpRequest(), the entity would have
759     //       been a part of HTTP_REQUEST (using the pEntityChunks field).
760     //       Since we have not passed that flag, we can be assured that 
761     //       there are no entity bodies in HTTP_REQUEST.
762     if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
763     {
764                 //Allocate some space for an XMLRPC memory block.
765                 body = xmlrpc_mem_block_new(envP, 0);
766                 if (envP->fault_occurred) 
767                         goto Done;
768
769         // The entity body can be sent over multiple calls. Let's collect all
770         // of these in a buffer and send the buffer to the xmlrpc-c library 
771         do
772         {
773             // Read the entity chunk from the request.
774             BytesRead = 0; 
775             result = HttpReceiveRequestEntityBody(
776                         hReqQueue,
777                         pRequest->RequestId,
778                         0,
779                         pEntityBuffer,
780                         EntityBufferLength,
781                         &BytesRead,
782                         NULL
783                         );
784             switch(result)
785             {
786                 case NO_ERROR:
787                     if(BytesRead != 0)
788                     {
789                                                 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body, 
790                                           pEntityBuffer, BytesRead);
791                                                 if(envP->fault_occurred)
792                                                         goto Done;                                              
793                     }
794                     break;
795
796                 case ERROR_HANDLE_EOF:
797                     // We have read the last request entity body. We can now 
798                     // process the suppossed XMLRPC data.
799                     if(BytesRead != 0)
800                     {
801                                                 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body, 
802                                           pEntityBuffer, BytesRead);
803                                                 if(envP->fault_occurred)
804                                                         goto Done;
805                     }
806
807                     // We will send the response over multiple calls. 
808                                         // This is achieved by passing the 
809                     // HTTP_SEND_RESPONSE_FLAG_MORE_DATA flag.
810                     
811                     // NOTE: Since we are accumulating the TotalBytesRead in 
812                     //       a ULONG, this will not work for entity bodies that
813                     //       are larger than 4 GB. For supporting large entity
814                     //       bodies, we would have to use a ULONGLONG.
815                                         TraceA("xmlrpc_server RPC2 handler processing RPC request.\n");
816                                                                                 
817                                         // Process the RPC.
818                                         output = xmlrpc_registry_process_call(
819                                                                                 envP, global_registryP, NULL, 
820                                                                                 XMLRPC_MEMBLOCK_CONTENTS(char, body),
821                                                                                 XMLRPC_MEMBLOCK_SIZE(char, body));
822                                         if (envP->fault_occurred) 
823                                                 goto Done;
824
825                                         // Initialize the HTTP response structure.
826                                         INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
827
828                                         //Add the content-length
829                                         StringCchPrintfA(szContentLength,MAX_ULONG_STR, "%lu",
830                                                                                 XMLRPC_MEMBLOCK_SIZE(char, output));
831                                         ADD_KNOWN_HEADER(
832                                                         response, 
833                                                         HttpHeaderContentLength, 
834                                                         szContentLength );
835
836                                         //Add the content-type
837                                         ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/xml");
838                                         
839                                         StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);                                    
840                                         ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
841
842                                         //send the response
843                                         result = HttpSendHttpResponse(
844                                                         hReqQueue,           // ReqQueueHandle
845                                                         pRequest->RequestId, // Request ID
846                                                         HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
847                                                         &response,           // HTTP response
848                                                         NULL,                // pReserved1
849                                                         &bytesSent,          // bytes sent (optional)
850                                                         NULL,                // pReserved2
851                                                         0,                   // Reserved3
852                                                         NULL,                // LPOVERLAPPED
853                                                         NULL                 // pReserved4
854                                                         );
855                                         if(result != NO_ERROR)
856                                         {
857                                                 TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
858                                                 xmlrpc_env_set_fault_formatted(
859                                                         envP, XMLRPC_NETWORK_ERROR,
860                                                         "HttpSendHttpResponse failed with %lu", result);
861                                                 goto Done;
862                                         }
863
864                                         // Send entity body from a memory chunk.
865                                         dataChunk.DataChunkType = HttpDataChunkFromMemory;
866                                         dataChunk.FromMemory.BufferLength = (ULONG)XMLRPC_MEMBLOCK_SIZE(char, output);
867                                         dataChunk.FromMemory.pBuffer = XMLRPC_MEMBLOCK_CONTENTS(char, output);
868
869                                         result = HttpSendResponseEntityBody(
870                                                                 hReqQueue,
871                                                                 pRequest->RequestId,
872                                                                 0,                    // This is the last send.
873                                                                 1,                    // Entity Chunk Count.
874                                                                 &dataChunk,
875                                                                 NULL,
876                                                                 NULL,
877                                                                 0,
878                                                                 NULL,
879                                                                 NULL
880                                                                 );
881                                         if(result != NO_ERROR)
882                                         {
883                                                 TraceW(L"HttpSendResponseEntityBody failed with %lu \n", result);
884                                                 xmlrpc_env_set_fault_formatted(
885                                                                 envP, XMLRPC_NETWORK_ERROR,
886                                                                 "HttpSendResponseEntityBody failed with %lu", result);
887                                                 goto Done;
888                                         }
889                                         goto Done;
890                     break;
891                 default:
892                                         TraceW(L"HttpReceiveRequestEntityBody failed with %lu \n", result);
893                                         xmlrpc_env_set_fault_formatted(
894                                                                 envP, XMLRPC_NETWORK_ERROR,
895                                                                 "HttpReceiveRequestEntityBody failed with %lu", result);
896                     goto Done;
897             }
898         } while(TRUE);
899     }
900     else
901     {
902                 // This request does not have an entity body. 
903                 TraceA("Received a bad request (no body in HTTP post).\n");
904                 xmlrpc_env_set_fault_formatted(
905                                 envP, XMLRPC_PARSE_ERROR,
906                                 "Bad POST request (no body)");
907         goto Done;
908     }
909
910 Done:
911
912     if(pEntityBuffer)
913         FREE_MEM(pEntityBuffer);
914
915         if(output)
916                 XMLRPC_MEMBLOCK_FREE(char, output);
917
918         if(body)
919                 XMLRPC_MEMBLOCK_FREE(char, body);
920
921     return;
922 }
923
924 #endif /* #if MUST_BUILD_HTTP_SYS_SERVER <> 0 */