]> granicus.if.org Git - apache/blob - modules/arch/win32/mod_isapi.c
d0d482fc3aedd0cc635cb13f71a3d0cb495bc3e1
[apache] / modules / arch / win32 / mod_isapi.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * mod_isapi.c - Internet Server Application (ISA) module for Apache
61  * by Alexei Kosut <akosut@apache.org>
62  *
63  * This module implements Microsoft's ISAPI, allowing Apache (when running
64  * under Windows) to load Internet Server Applications (ISAPI extensions).
65  * It implements all of the ISAPI 2.0 specification, except for the 
66  * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
67  * extensions that use only synchronous I/O and are compatible with the
68  * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
69  * function as well).
70  *
71  * To load, simply place the ISA in a location in the document tree.
72  * Then add an "AddHandler isapi-isa dll" into your config file.
73  * You should now be able to load ISAPI DLLs just be reffering to their
74  * URLs. Make sure the ExecCGI option is active in the directory
75  * the ISA is in.
76  */
77
78 #include "ap_config.h"
79 #include "httpd.h"
80 #include "http_config.h"
81 #include "http_core.h"
82 #include "http_protocol.h"
83 #include "http_request.h"
84 #include "http_log.h"
85 #include "util_script.h"
86 #include "apr_portable.h"
87 #include "apr_strings.h"
88
89
90 /* We use the exact same header file as the original */
91 #include <HttpExt.h>
92
93 /* TODO: Unknown errors that must be researched for correct codes */
94
95 #define TODO_ERROR 1
96
97 /* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
98    define this to conform */
99 #define RELAX_HEADER_RULE
100
101 module isapi_module;
102
103 /* Declare the ISAPI functions */
104
105 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
106                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
107 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
108                          DWORD dwReserved);
109 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
110 BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
111                                    LPVOID lpvBuffer, LPDWORD lpdwSize,
112                                    LPDWORD lpdwDataType);
113
114 /*
115     The optimiser blows it totally here. What happens is that autos are addressed relative to the
116     stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
117     between setting HttpExtensionProc's address and calling through it. We work around the problem by 
118     forcing it to use frame pointers.
119
120     The revisions below may eliminate this artifact.
121 */
122 #pragma optimize("y",off)
123
124 /* Our loaded isapi module description structure */
125
126 typedef struct {
127     HINSTANCE handle;
128     HSE_VERSION_INFO *pVer;
129     PFN_GETEXTENSIONVERSION GetExtensionVersion;
130     PFN_HTTPEXTENSIONPROC   HttpExtensionProc;
131     PFN_TERMINATEEXTENSION  TerminateExtension;
132     int   refcount;
133     DWORD timeout;
134     BOOL  fakeasync;
135     DWORD reportversion;
136 } isapi_loaded;
137
138 /* Our "Connection ID" structure */
139
140 typedef struct {
141     LPEXTENSION_CONTROL_BLOCK ecb;
142     isapi_loaded *isa;
143     request_rec  *r;
144     PFN_HSE_IO_COMPLETION completion;
145     PVOID  completion_arg;
146     HANDLE complete;
147     apr_status_t retval;
148 } isapi_cid;
149
150 apr_status_t isapi_handler (request_rec *r)
151 {
152     apr_table_t *e = r->subprocess_env;
153     isapi_loaded *isa;
154     isapi_cid *cid;
155
156     /* Use similar restrictions as CGIs
157      *
158      * If this fails, it's pointless to load the isapi dll.
159      */
160     if (!(ap_allow_options(r) & OPT_EXECCGI))
161         return HTTP_FORBIDDEN;
162
163     if (r->finfo.protection == 0)
164         return HTTP_NOT_FOUND;
165
166     if (r->finfo.filetype == APR_DIR)
167         return HTTP_FORBIDDEN;
168
169     /* Load the module 
170      *
171      * TODO: Critical section
172      *
173      * Warning: cid should not be allocated from pool if we 
174      * cache the isapi process in-memory.
175      *
176      * This code could use cacheing... everything that follows
177      * should only be performed on the first isapi dll invocation, 
178      * not with every HttpExtensionProc()
179      */
180     isa = apr_pcalloc(r->pool, sizeof(isapi_module));
181     isa->pVer = apr_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
182     isa->refcount = 0;
183
184     /* TODO: These may need to become overrideable, so that we
185      * assure a given isapi can be fooled into behaving well.
186      */
187     isa->timeout = INFINITE; /* microsecs */
188     isa->fakeasync = TRUE;
189     isa->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
190     
191     if (!(isa->handle = LoadLibraryEx(r->filename, NULL,
192                                       LOAD_WITH_ALTERED_SEARCH_PATH))) {
193         ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
194                       "ISAPI %s failed to load", r->filename);
195         return HTTP_INTERNAL_SERVER_ERROR;
196     }
197
198     if (!(isa->GetExtensionVersion =
199           (void *)(GetProcAddress(isa->handle, "GetExtensionVersion")))) {
200         ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
201                       "ISAPI %s is missing GetExtensionVersion()",
202                       r->filename);
203         FreeLibrary(isa->handle);
204         return HTTP_INTERNAL_SERVER_ERROR;
205     }
206
207     if (!(isa->HttpExtensionProc =
208           (void *)(GetProcAddress(isa->handle, "HttpExtensionProc")))) {
209         ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
210                       "ISAPI %s is missing HttpExtensionProc()",
211                       r->filename);
212         FreeLibrary(isa->handle);
213         return HTTP_INTERNAL_SERVER_ERROR;
214     }
215
216     /* TerminateExtension() is an optional interface */
217
218     isa->TerminateExtension = (void *)(GetProcAddress(isa->handle, "TerminateExtension"));
219
220     /* Run GetExtensionVersion() */
221
222     if (!(*isa->GetExtensionVersion)(isa->pVer)) {
223         /* ### euh... we're passing the wrong type of error code here */
224         ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
225                       "ISAPI %s call GetExtensionVersion() failed", 
226                       r->filename);
227         FreeLibrary(isa->handle);
228         return HTTP_INTERNAL_SERVER_ERROR;
229     }
230
231     /* Load of this module completed, this is the point at which *isa
232      * could be cached for later invocation.
233      *
234      * on to invoking this request... 
235      */
236     
237     /* Set up variables */
238     ap_add_common_vars(r);
239     ap_add_cgi_vars(r);
240
241     /* Set up connection structure and ecb */
242     cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
243     cid->ecb = apr_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
244     cid->ecb->ConnID = (HCONN)cid;
245     /* TODO: Critical section */
246     ++isa->refcount;
247     cid->isa = isa;
248     cid->r = r;
249     cid->r->status = 0;
250     cid->complete = NULL;
251     cid->completion = NULL;
252     cid->retval = APR_SUCCESS;
253
254     cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
255     cid->ecb->dwVersion = isa->reportversion;
256     cid->ecb->dwHttpStatusCode = 0;
257     strcpy(cid->ecb->lpszLogData, "");
258     // TODO: are copies really needed here?
259     cid->ecb->lpszMethod = apr_pstrdup(r->pool, (char*) r->method);
260     cid->ecb->lpszQueryString = apr_pstrdup(r->pool, 
261                                 (char*) apr_table_get(e, "QUERY_STRING"));
262     cid->ecb->lpszPathInfo = apr_pstrdup(r->pool, 
263                              (char*) apr_table_get(e, "PATH_INFO"));
264     cid->ecb->lpszPathTranslated = apr_pstrdup(r->pool, 
265                                    (char*) apr_table_get(e, "PATH_TRANSLATED"));
266     cid->ecb->lpszContentType = apr_pstrdup(r->pool, 
267                                 (char*) apr_table_get(e, "CONTENT_TYPE"));
268     /* Set up the callbacks */
269     cid->ecb->GetServerVariable = &GetServerVariable;
270     cid->ecb->WriteClient = &WriteClient;
271     cid->ecb->ReadClient = &ReadClient;
272     cid->ecb->ServerSupportFunction = &ServerSupportFunction;
273
274     
275     /* Set up client input */
276     cid->retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
277     if (cid->retval) {
278         if (isa->TerminateExtension) {
279             (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
280         }
281         FreeLibrary(isa->handle);
282         return cid->retval;
283     }
284
285     if (ap_should_client_block(r)) {
286         /* Unlike IIS, which limits this to 48k, we read the whole
287          * sucker in. I suppose this could be bad for memory if someone
288          * uploaded the complete works of Shakespeare. Well, WebSite
289          * does the same thing.
290          *
291          * But we can be smarter and read up to our 48k and then allow
292          * the ISAPI app to read further blocks as desired.
293          */
294         long to_read = atol(apr_table_get(e, "CONTENT_LENGTH"));
295         long read;
296
297         /* Actually, let's cap it at 48k, until we figure out what
298          * to do with this... we don't want a Content-Length: 1000000000
299          * taking out the machine.
300          */
301
302         if (to_read > 49152) {
303             if (isa->TerminateExtension) 
304                 (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
305             FreeLibrary(isa->handle);
306             return HTTP_REQUEST_ENTITY_TOO_LARGE;
307         }
308
309         cid->ecb->lpbData = apr_pcalloc(r->pool, 1 + to_read);
310
311         if ((read = ap_get_client_block(r, cid->ecb->lpbData, to_read)) < 0) {
312             if (isa->TerminateExtension) 
313                 (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
314             FreeLibrary(isa->handle);
315             return HTTP_INTERNAL_SERVER_ERROR;
316         }
317
318         /* Although its not to spec, IIS seems to null-terminate
319          * its lpdData string. So we will too. To make sure
320          * cbAvailable matches cbTotalBytes, we'll up the latter
321          * and equalize them.
322          */
323         cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read + 1;
324         cid->ecb->lpbData[read] = '\0';
325     }
326     else {
327         cid->ecb->cbTotalBytes = 0;
328         cid->ecb->cbAvailable = 0;
329         cid->ecb->lpbData = NULL;
330     }
331
332     /* All right... try and run the sucker */
333     cid->retval = (*isa->HttpExtensionProc)(cid->ecb);
334
335     /* Set the status (for logging) */
336     if (cid->ecb->dwHttpStatusCode) {
337         cid->r->status = cid->ecb->dwHttpStatusCode;
338     }
339
340     /* Check for a log message - and log it */
341     if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
342         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
343                       "ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
344
345     switch(cid->retval) {
346         case HSE_STATUS_SUCCESS:
347         case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
348             /* Ignore the keepalive stuff; Apache handles it just fine without
349              * the ISA's "advice".
350              * Per Microsoft: "In IIS versions 4.0 and later, the return
351              * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
352              * are functionally identical: Keep-Alive connections are
353              * maintained, if supported by the client."
354              * ... so we were pat all this time
355              */
356             break;
357             
358         case HSE_STATUS_PENDING:    
359             /* emulating async behavior...
360              *
361              * Create a cid->completed event and wait on it for some timeout
362              * so that the app thinks is it running async.
363              *
364              * All async ServerSupportFunction calls will be handled through
365              * the registered IO_COMPLETION hook.
366              */
367             
368             if (!isa->fakeasync) {
369                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
370                               "ISAPI %s asynch I/O request refused", 
371                               r->filename);
372                 cid->retval = APR_ENOTIMPL;
373             }
374             else {
375                 cid->complete = CreateEvent(NULL, FALSE, FALSE, NULL);
376                 if (WaitForSingleObject(cid->complete, isa->timeout)
377                         == WAIT_TIMEOUT) {
378                     /* TODO: Now what... if this hung, then do we kill our own
379                      * thread to force it's death?  For now leave timeout = -1
380                      */
381                 }
382             }
383             break;
384
385         case HSE_STATUS_ERROR:    
386             /* end response if we have yet to do so.
387              */
388             cid->retval = HTTP_INTERNAL_SERVER_ERROR;
389             break;
390
391         default:
392             /* TODO: log unrecognized retval for debugging 
393              */
394             cid->retval = HTTP_INTERNAL_SERVER_ERROR;
395             break;
396     }
397
398     /* All done with the DLL... get rid of it...
399      *
400      * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
401      * and if it returns TRUE, unload, otherwise, cache it.
402      */
403     if (isa->TerminateExtension) {
404         (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
405     }
406     FreeLibrary(isa->handle);
407     /* TODO: Crit section */
408     cid->isa = NULL;
409     --isa->refcount;
410     isa->handle = NULL;
411     
412     return cid->retval;
413 }
414 #pragma optimize("",on)
415
416 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
417                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
418 {
419     request_rec *r = ((isapi_cid *)hConn)->r;
420     apr_table_t *e = r->subprocess_env;
421     const char *result;
422
423     /* Mostly, we just grab it from the environment, but there are
424      * a couple of special cases
425      */
426
427     if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
428         /* We don't support NT users, so this is always the same as
429          * REMOTE_USER
430          */
431         result = apr_table_get(e, "REMOTE_USER");
432     }
433     else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
434         /* Apache doesn't support secure requests inherently, so
435          * we have no way of knowing. We'll be conservative, and say
436          * all requests are insecure.
437          */
438         result = "0";
439     }
440     else if (!strcasecmp(lpszVariableName, "URL")) {
441         result = r->uri;
442     }
443     else {
444         result = apr_table_get(e, lpszVariableName);
445     }
446
447     if (result) {
448         if (strlen(result) > *lpdwSizeofBuffer) {
449             *lpdwSizeofBuffer = strlen(result);
450             SetLastError(ERROR_INSUFFICIENT_BUFFER);
451             return FALSE;
452         }
453         strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
454         return TRUE;
455     }
456
457     /* Didn't find it */
458     SetLastError(ERROR_INVALID_INDEX);
459     return FALSE;
460 }
461
462 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
463                          DWORD dwReserved)
464 {
465     request_rec *r = ((isapi_cid *)ConnID)->r;
466     int writ;   /* written, actually, but why shouldn't I make up words? */
467
468     /* We only support synchronous writing */
469     if (dwReserved && dwReserved != HSE_IO_SYNC) {
470         ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
471                       "ISAPI %s asynch write", r->filename);
472         SetLastError(ERROR_INVALID_PARAMETER);
473         return FALSE;
474     }
475
476     if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
477         SetLastError(WSAEDISCON); /* TODO: Find the right error code */
478         return FALSE;
479     }
480
481     *lpwdwBytes = writ;
482     return TRUE;
483 }
484
485 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
486 {
487     /* TODO: If the request was a huge transmit or chunked, continue piping the
488      * request here, but if it's of a sane size, continue to ...
489      */
490     return TRUE;
491 }
492
493 static char* ComposeHeaders(request_rec *r, char* data)
494 {
495     /* We *should* break before this while loop ends */
496     while (*data) 
497     {
498         char *value, *lf = strchr(data, '\n');
499         int p;
500
501 #ifdef RELAX_HEADER_RULE
502         if (lf)
503             *lf = '\0';
504 #else
505         if (!lf) { /* Huh? Invalid data, I think */
506             ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
507                           "ISAPI %s sent invalid headers", r->filename);
508             SetLastError(TODO_ERROR);
509             return FALSE;
510         }
511
512         /* Get rid of \n and \r */
513         *lf = '\0';
514 #endif
515         p = strlen(data);
516         if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
517
518         /* End of headers */
519         if (*data == '\0') {
520 #ifdef RELAX_HEADER_RULE
521             if (lf)
522 #endif
523                 data = lf + 1;  /* Reset data */
524             break;
525         }
526
527         if (!(value = strchr(data, ':'))) {
528             SetLastError(TODO_ERROR);
529             /* ### euh... we're passing the wrong type of error
530                ### code here */
531             ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_INTERNAL_SERVER_ERROR, r,
532                           "ISAPI %s sent invalid headers", r->filename);
533             return FALSE;
534         }
535
536         *value++ = '\0';
537         while (*value && apr_isspace(*value)) ++value;
538
539         /* Check all the special-case headers. Similar to what
540          * ap_scan_script_header_err() does (see that function for
541          * more detail)
542          */
543
544         if (!strcasecmp(data, "Content-Type")) 
545         {
546             /* Nuke trailing whitespace */    
547             char *tmp;
548             char *endp = value + strlen(value) - 1;
549             while (endp > value && apr_isspace(*endp)) 
550                 *endp-- = '\0';
551
552             tmp = apr_pstrdup (r->pool, value);
553             ap_str_tolower(tmp);
554             r->content_type = tmp;
555         }
556         else if (!strcasecmp(data, "Content-Length")) {
557             apr_table_set(r->headers_out, data, value);
558         }
559         else if (!strcasecmp(data, "Transfer-Encoding")) {
560             apr_table_set(r->headers_out, data, value);
561         }
562         else if (!strcasecmp(data, "Set-Cookie")) {
563             apr_table_add(r->err_headers_out, data, value);
564         }
565         else {
566             apr_table_merge(r->err_headers_out, data, value);
567         }
568
569         /* Reset data */
570 #ifdef RELAX_HEADER_RULE
571         if (!lf) {
572             data += p;
573             break;
574         }
575 #endif
576         data = lf + 1;
577     }
578     return data;
579 }
580
581
582 /* XXX: There is an O(n^2) attack possible here. */
583 BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
584                                    LPVOID lpvBuffer, LPDWORD lpdwSize,
585                                    LPDWORD lpdwDataType)
586 {
587     isapi_cid *cid = (isapi_cid *)hConn;
588     request_rec *r = cid->r;
589     request_rec *subreq;
590     char *data;
591
592     switch (dwHSERequest) {
593         case HSE_REQ_SEND_URL_REDIRECT_RESP:
594             /* Set the status to be returned when the HttpExtensionProc()
595              * is done.
596              */
597             apr_table_set (r->headers_out, "Location", lpvBuffer);
598             cid->r->status = cid->ecb->dwHttpStatusCode 
599                                                    = HTTP_MOVED_TEMPORARILY;
600             return TRUE;
601
602         case HSE_REQ_SEND_URL:
603             /* Read any additional input */
604
605             if (r->remaining > 0) {
606                 char argsbuffer[HUGE_STRING_LEN];
607
608                 while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
609             }
610
611             /* Reset the method to GET */
612             r->method = apr_pstrdup(r->pool, "GET");
613             r->method_number = M_GET;
614
615             /* Don't let anyone think there's still data */
616             apr_table_unset(r->headers_in, "Content-Length");
617
618             ap_internal_redirect((char *)lpvBuffer, r);
619             return TRUE;
620
621         case HSE_REQ_SEND_RESPONSE_HEADER:
622             r->status_line = lpvBuffer ? lpvBuffer : apr_pstrdup(r->pool, "200 OK");
623             sscanf(r->status_line, "%d", &r->status);
624             cid->ecb->dwHttpStatusCode = r->status;
625
626             /* Now fill in the HTTP headers, and the rest of it. Ick.
627              * lpdwDataType contains a string that has headers (in MIME
628              * format), a blank like, then (possibly) data. We need
629              * to parse it.
630              *
631              * Easy case first:
632              */
633             if (!lpdwDataType) {
634                 ap_send_http_header(r);
635                 return TRUE;
636             }
637                         
638             /* Make a copy - don't disturb the original */
639             data = apr_pstrdup(r->pool, (char *)lpdwDataType);
640             
641             /* Parse them out, or die trying */
642             data = ComposeHeaders(r, data);
643             if (!data)
644                 return FALSE;
645
646             /* All the headers should be set now */
647             ap_send_http_header(r);
648
649             /* Any data left should now be sent directly */
650             if (*data)
651                 ap_rputs(data, r);
652
653             return TRUE;
654
655         case HSE_REQ_DONE_WITH_SESSION:
656             /* Signal to resume the thread completing this request
657              */
658             if (cid->complete)
659                 SetEvent(cid->complete);
660             return TRUE;
661
662         case HSE_REQ_MAP_URL_TO_PATH:
663             /* Map a URL to a filename */
664             subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, (char *)lpvBuffer,
665                                            *lpdwSize), r);
666
667             GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
668
669             /* IIS puts a trailing slash on directories, Apache doesn't */
670
671             if (subreq->finfo.filetype == APR_DIR) {
672                 int l = strlen((char *)lpvBuffer);
673
674                 ((char *)lpvBuffer)[l] = '\\';
675                 ((char *)lpvBuffer)[l + 1] = '\0';
676             }
677
678             return TRUE;
679
680         case HSE_REQ_GET_SSPI_INFO:
681             SetLastError(ERROR_INVALID_PARAMETER);
682             return FALSE;
683         
684         case HSE_APPEND_LOG_PARAMETER:
685             /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field 
686              * This code will do for now...
687              */
688             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
689                       "ISAPI %s: %s", cid->r->filename, 
690                       (char*) lpvBuffer);
691             return TRUE;
692         
693         case HSE_REQ_IO_COMPLETION:
694             /* TODO: Emulate a completion port, if we can...
695              * Record the callback address and user defined argument...
696              * we will call this after any async request (e.g. transmitfile)
697              * as if the request had completed async execution.
698              * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
699              * to HSE_REQ_IO_COMPLETION, and lpvBuffer may be set to NULL.
700              */
701             if (!cid->isa->fakeasync)
702                 return FALSE;
703             cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
704             cid->completion_arg = (PVOID) lpdwDataType;
705             return TRUE;
706
707         case HSE_REQ_TRANSMIT_FILE:
708             /* Use TransmitFile... nothing wrong with that :)
709              */
710
711             /* ### euh... we're passing the wrong type of error code here */
712             ap_log_rerror(APLOG_MARK, APLOG_WARNING,
713                           HTTP_INTERNAL_SERVER_ERROR, r,
714                           "ISAPI asynchronous I/O not supported: %s",
715                           r->filename);
716             return FALSE;
717             
718         case HSE_REQ_REFRESH_ISAPI_ACL:
719             SetLastError(ERROR_INVALID_PARAMETER);
720             return FALSE;
721
722         case HSE_REQ_IS_KEEP_CONN:
723             SetLastError(ERROR_INVALID_PARAMETER);
724             return FALSE;
725         
726         case HSE_REQ_ASYNC_READ_CLIENT:
727             SetLastError(ERROR_INVALID_PARAMETER);
728             return FALSE;
729         
730         case HSE_REQ_GET_IMPERSONATION_TOKEN:  /* Added in ISAPI 4.0 */
731             SetLastError(ERROR_INVALID_PARAMETER);
732             return FALSE;
733
734         case HSE_REQ_MAP_URL_TO_PATH_EX:
735             SetLastError(ERROR_INVALID_PARAMETER);
736             return FALSE;
737
738             /* TODO: Not quite ready for prime time yet */
739
740             /* Map a URL to a filename */
741             subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, (char *)lpvBuffer,
742                                            *lpdwSize), r);
743
744             GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
745
746             /* IIS puts a trailing slash on directories, Apache doesn't */
747
748             if (subreq->finfo.filetype == APR_DIR) {
749                 int l = strlen((char *)lpvBuffer);
750
751                 ((char *)lpvBuffer)[l] = '\\';
752                 ((char *)lpvBuffer)[l + 1] = '\0';
753             }
754
755             lpdwDataType = (LPDWORD) apr_palloc(r->pool, sizeof(HSE_URL_MAPEX_INFO));
756             strncpy(((LPHSE_URL_MAPEX_INFO)lpdwDataType)->lpszPath,
757                     (char *) lpvBuffer, MAX_PATH);
758             ((LPHSE_URL_MAPEX_INFO)lpdwDataType)->dwFlags = 0;
759             /* is a combination of:
760              * HSE_URL_FLAGS_READ       Allow for read. 
761              * HSE_URL_FLAGS_WRITE      Allow for write. 
762              * HSE_URL_FLAGS_EXECUTE    Allow for execute. 
763              * HSE_URL_FLAGS_SSL        Require SSL. 
764              * HSE_URL_FLAGS_DONT_CACHE Don't cache (virtual root only). 
765              * HSE_URL_FLAGS_NEGO_CERT  Allow client SSL certifications. 
766              * HSE_URL_FLAGS_REQUIRE_CERT Require client SSL certifications. 
767              * HSE_URL_FLAGS_MAP_CERT   Map SSL certification to a Windows account. 
768              * HSE_URL_FLAGS_SSL128     Requires a 128-bit SSL. 
769              * HSE_URL_FLAGS_SCRIPT     Allows for script execution. 
770              */
771             /* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingPath
772              * (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingURL
773              */
774
775             return TRUE;
776         
777         case HSE_REQ_ABORTIVE_CLOSE:
778             SetLastError(ERROR_INVALID_PARAMETER);
779             return FALSE;
780                 
781         case HSE_REQ_GET_CERT_INFO_EX:  /* Added in ISAPI 4.0 */
782             SetLastError(ERROR_INVALID_PARAMETER);
783             return FALSE;
784
785         case HSE_REQ_SEND_RESPONSE_HEADER_EX:  /* Added in ISAPI 4.0 */
786             SetLastError(ERROR_INVALID_PARAMETER);
787             return FALSE;
788
789             /* TODO: Not quite ready for prime time */
790
791             if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
792                 && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus) {
793                 r->status_line = apr_pstrndup(r->pool, 
794                            ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
795                            ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
796             }
797             else {
798                 r->status_line = apr_pstrdup(r->pool, "200 OK");
799             }
800             sscanf(r->status_line, "%d", &r->status);
801             cid->ecb->dwHttpStatusCode = r->status;
802
803             if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader
804                 && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader)
805             {
806                 /* Make a copy - don't disturb the original */
807                 data = apr_pstrndup(r->pool, 
808                            ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader,
809                            ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader);
810                 
811                 /* Parse them out, or die trying */
812                 data = ComposeHeaders(r, data);
813                 if (!data)
814                     return FALSE;
815
816             }
817             else {
818                 data = "\0";
819             }
820             
821             /* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn; 
822              *
823              * Now how are we about to start listening to an ISAPI's
824              * idea of keeping or closing a connection?  Seriously :)
825              */
826
827             /* All the headers should be set now */
828             ap_send_http_header(r);
829
830             /* Any data left should now be sent directly */
831             if (*data)
832                 ap_rputs(data, r);
833
834             return TRUE;
835 #if 0
836 /* HSE_REQ_CLOSE_CONNACTION and HSE_REQ_IS_CONNECTED are not defined on
837  * my system. wgs */
838         case HSE_REQ_CLOSE_CONNECTION:  /* Added after ISAPI 4.0 */
839             SetLastError(ERROR_INVALID_PARAMETER);
840             return FALSE;
841
842         case HSE_REQ_IS_CONNECTED:  /* Added after ISAPI 4.0 */
843             /* Returns True if client is connected c.f. Q188346*/
844             return TRUE;
845 #endif
846
847      /* case HSE_REQ_EXTENSION_TRIGGER:  
848       *     Added after ISAPI 4.0? 
849       *      Undocumented - from the Microsoft Jan '00 Platform SDK
850       */
851         default:
852             /* TODO: log unrecognized ServerSupportCommand for debugging 
853              */
854             SetLastError(ERROR_INVALID_PARAMETER);
855             return FALSE;
856     }
857 }
858
859 handler_rec isapi_handlers[] = {
860     { "isapi-isa", isapi_handler },
861     { NULL}
862 };
863
864 module isapi_module = {
865    STANDARD20_MODULE_STUFF,
866    NULL,                        /* create per-dir config */
867    NULL,                        /* merge per-dir config */
868    NULL,                        /* server config */
869    NULL,                        /* merge server config */
870    NULL,                        /* command apr_table_t */
871    isapi_handlers,              /* handlers */
872    NULL                         /* register hooks */
873 };