]> granicus.if.org Git - apache/blob - os/win32/mod_isapi.c
Missing apr_strings.h - hope this wasn't destined for some global header.
[apache] / os / 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 /* Our "Connection ID" structure */
104
105 typedef struct {
106     LPEXTENSION_CONTROL_BLOCK ecb;
107     request_rec *r;
108     int status;
109 } isapi_cid;
110
111 /* Declare the ISAPI functions */
112
113 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
114                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
115 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
116                          DWORD dwReserved);
117 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
118 BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
119                                    LPVOID lpvBuffer, LPDWORD lpdwSize,
120                                    LPDWORD lpdwDataType);
121
122 /*
123     The optimiser blows it totally here. What happens is that autos are addressed relative to the
124     stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
125     between setting isapi_entry and calling through it. We work around the problem by forcing it to
126     use frame pointers.
127 */
128 #pragma optimize("y",off)
129
130 int isapi_handler (request_rec *r)
131 {
132     ap_status_t rv;
133
134     LPEXTENSION_CONTROL_BLOCK ecb =
135         ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
136     HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
137
138     HINSTANCE isapi_handle;
139     BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
140     DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
141     BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
142
143     isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
144     ap_table_t *e = r->subprocess_env;
145     int retval;
146
147     /* Use similar restrictions as CGIs */
148
149     if (!(ap_allow_options(r) & OPT_EXECCGI))
150         return HTTP_FORBIDDEN;
151
152     if (r->finfo.protection == 0)
153             return HTTP_NOT_FOUND;
154
155     if (r->finfo.filetype == APR_DIR)
156             return HTTP_FORBIDDEN;
157
158     /* Load the module */
159
160     if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
161                                        LOAD_WITH_ALTERED_SEARCH_PATH))) {
162             rv = GetLastError();
163         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
164                               "Could not load DLL: %s", r->filename);
165             return HTTP_INTERNAL_SERVER_ERROR;
166     }
167
168     if (!(isapi_version =
169           (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
170             rv = GetLastError();
171         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
172                               "Could not load DLL %s symbol GetExtensionVersion()",
173                       r->filename);
174             FreeLibrary(isapi_handle);
175             return HTTP_INTERNAL_SERVER_ERROR;
176     }
177
178     if (!(isapi_entry =
179           (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
180             rv = GetLastError();
181         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
182                               "Could not load DLL %s symbol HttpExtensionProc()",
183                       r->filename);
184             FreeLibrary(isapi_handle);
185             return HTTP_INTERNAL_SERVER_ERROR;
186     }
187
188     /* TerminateExtension() is an optional interface */
189
190     isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
191
192     /* Run GetExtensionVersion() */
193
194     if (!(*isapi_version)(pVer)) {
195         /* ### euh... we're passing the wrong type of error code here */
196         ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
197                     "ISAPI %s GetExtensionVersion() call failed", r->filename);
198             FreeLibrary(isapi_handle);
199             return HTTP_INTERNAL_SERVER_ERROR;
200     }
201
202     /* Set up variables */
203     ap_add_common_vars(r);
204     ap_add_cgi_vars(r);
205
206     /* Set up connection ID */
207     ecb->ConnID = (HCONN)cid;
208     cid->ecb = ecb;
209     cid->r = r;
210     cid->status = 0;
211
212     ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
213     ecb->dwVersion = MAKELONG(0, 2);
214     ecb->dwHttpStatusCode = 0;
215     strcpy(ecb->lpszLogData, "");
216     // TODO: is a copy needed here?
217     ecb->lpszMethod = (char*) r->method;
218     // TODO: is a copy needed here?
219     ecb->lpszQueryString = (char*) ap_table_get(e, "QUERY_STRING");
220     // TODO: is a copy needed here?
221     ecb->lpszPathInfo = (char*) ap_table_get(e, "PATH_INFO");
222     // TODO: is a copy needed here?
223     ecb->lpszPathTranslated = (char*) ap_table_get(e, "PATH_TRANSLATED");
224     // TODO: is a copy needed here?
225     ecb->lpszContentType = (char*) ap_table_get(e, "CONTENT_TYPE");
226
227     /* Set up client input */
228     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
229         if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
230             FreeLibrary(isapi_handle);
231             return retval;
232     }
233
234     if (ap_should_client_block(r)) {
235         /* Unlike IIS, which limits this to 48k, we read the whole
236          * sucker in. I suppose this could be bad for memory if someone
237          * uploaded the complete works of Shakespeare. Well, WebSite
238          * does the same thing.
239          */
240             long to_read = atol(ap_table_get(e, "CONTENT_LENGTH"));
241             long read;
242
243             /* Actually, let's cap it at 48k, until we figure out what
244              * to do with this... we don't want a Content-Length: 1000000000
245              * taking out the machine.
246              */
247
248             if (to_read > 49152) {
249                 if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
250                 FreeLibrary(isapi_handle);
251                 return HTTP_REQUEST_ENTITY_TOO_LARGE;
252             }
253
254             ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
255
256             if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
257                 if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
258                 FreeLibrary(isapi_handle);
259                 return HTTP_INTERNAL_SERVER_ERROR;
260             }
261
262             /* Although its not to spec, IIS seems to null-terminate
263              * its lpdData string. So we will too. To make sure
264              * cbAvailable matches cbTotalBytes, we'll up the latter
265              * and equalize them.
266              */
267             ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
268             ecb->lpbData[read] = '\0';
269         }
270     else {
271             ecb->cbTotalBytes = 0;
272             ecb->cbAvailable = 0;
273             ecb->lpbData = NULL;
274     }
275
276     /* Set up the callbacks */
277
278     ecb->GetServerVariable = &GetServerVariable;
279     ecb->WriteClient = &WriteClient;
280     ecb->ReadClient = &ReadClient;
281     ecb->ServerSupportFunction = &ServerSupportFunction;
282
283     /* All right... try and load the sucker */
284     retval = (*isapi_entry)(ecb);
285
286     /* Set the status (for logging) */
287     if (ecb->dwHttpStatusCode)
288         r->status = ecb->dwHttpStatusCode;
289
290     /* Check for a log message - and log it */
291     if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
292             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
293                       "ISAPI %s: %s", r->filename, ecb->lpszLogData);
294
295     /* All done with the DLL... get rid of it */
296     if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
297         FreeLibrary(isapi_handle);
298
299     switch(retval) {
300         case HSE_STATUS_SUCCESS:
301             /* TODO: If content length was missing or incorrect, and the response
302              * was not chunked, we need to close the connection here.
303              * If the response was chunked, and no closing chunk was sent, we aught
304              * to transmit one here
305              */
306
307             /* fall through... */
308         case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
309             /* Ignore the keepalive stuff; Apache handles it just fine without
310              * the ISA's "advice".
311              */
312
313             if (cid->status) /* We have a special status to return */
314                 return cid->status;
315
316             return OK;
317
318         case HSE_STATUS_PENDING:    
319             /* We don't support this, but we need to... we should simply create a
320              * wait event and die on timeout or resume with the callback to our
321              * ServerSupportFunction with HSE_REQ_DONE_WITH_SESSION to emulate
322              * async behavior.
323              */
324             ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
325                           "ISAPI asynchronous I/O not supported: %s", r->filename);
326
327         case HSE_STATUS_ERROR:    
328             /* end response if we have yet to do so.
329              */
330             return HTTP_INTERNAL_SERVER_ERROR;
331
332         default:
333             /* TODO: log unrecognized retval for debugging 
334              */
335             return HTTP_INTERNAL_SERVER_ERROR;
336     }
337
338 }
339 #pragma optimize("",on)
340
341 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
342                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
343 {
344     request_rec *r = ((isapi_cid *)hConn)->r;
345     ap_table_t *e = r->subprocess_env;
346     const char *result;
347
348     /* Mostly, we just grab it from the environment, but there are
349      * a couple of special cases
350      */
351
352     if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
353             /* We don't support NT users, so this is always the same as
354              * REMOTE_USER
355              */
356             result = ap_table_get(e, "REMOTE_USER");
357     }
358     else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
359             /* Apache doesn't support secure requests inherently, so
360              * we have no way of knowing. We'll be conservative, and say
361              * all requests are insecure.
362              */
363             result = "0";
364     }
365     else if (!strcasecmp(lpszVariableName, "URL")) {
366         result = r->uri;
367     }
368     else {
369             result = ap_table_get(e, lpszVariableName);
370     }
371
372     if (result) {
373             if (strlen(result) > *lpdwSizeofBuffer) {
374                 *lpdwSizeofBuffer = strlen(result);
375                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
376                 return FALSE;
377             }
378             strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
379             return TRUE;
380     }
381
382     /* Didn't find it */
383     SetLastError(ERROR_INVALID_INDEX);
384     return FALSE;
385 }
386
387 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
388                                  DWORD dwReserved)
389 {
390     request_rec *r = ((isapi_cid *)ConnID)->r;
391     int writ;   /* written, actually, but why shouldn't I make up words? */
392
393     /* We only support synchronous writing */
394     if (dwReserved && dwReserved != HSE_IO_SYNC) {
395             ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
396                       "ISAPI asynchronous I/O not supported: %s", r->filename);
397             SetLastError(ERROR_INVALID_PARAMETER);
398             return FALSE;
399     }
400
401     if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
402             SetLastError(WSAEDISCON); /* TODO: Find the right error code */
403             return FALSE;
404     }
405
406     *lpwdwBytes = writ;
407     return TRUE;
408 }
409
410 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
411 {
412     /* If the request was a huge transmit or chunked, continue piping the
413      * request here, but if it's of a sane size, continue to ...
414      */
415     return TRUE;
416 }
417
418 /* XXX: There is an O(n^2) attack possible here. */
419 BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
420                                                    LPVOID lpvBuffer, LPDWORD lpdwSize,
421                                                    LPDWORD lpdwDataType)
422 {
423     isapi_cid *cid = (isapi_cid *)hConn;
424     request_rec *subreq, *r = cid->r;
425     char *data;
426
427     switch (dwHSERequest) {
428         case HSE_REQ_SEND_URL_REDIRECT_RESP:
429             /* Set the status to be returned when the HttpExtensionProc()
430              * is done.
431              */
432             ap_table_set (r->headers_out, "Location", lpvBuffer);
433             cid->status = cid->r->status = cid->ecb->dwHttpStatusCode =
434                 HTTP_MOVED_TEMPORARILY;
435             return TRUE;
436
437         case HSE_REQ_SEND_URL:
438             /* Read any additional input */
439
440             if (r->remaining > 0) {
441                 char argsbuffer[HUGE_STRING_LEN];
442
443                 while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
444             }
445
446             /* Reset the method to GET */
447             r->method = ap_pstrdup(r->pool, "GET");
448             r->method_number = M_GET;
449
450             /* Don't let anyone think there's still data */
451             ap_table_unset(r->headers_in, "Content-Length");
452
453             ap_internal_redirect((char *)lpvBuffer, r);
454             return TRUE;
455
456         case HSE_REQ_SEND_RESPONSE_HEADER_EX:
457             if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
458                 && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus)
459                 r->status_line = ap_pstrndup(r->pool, 
460                                              ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
461                                              ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
462             else
463                 r->status_line = ap_pstrdup(r->pool, "200 OK");
464             sscanf(r->status_line, "%d", &r->status);
465             cid->ecb->dwHttpStatusCode = r->status;
466
467           ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader; // HTTP header
468           ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader; // HTTP header len
469           
470           ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn; // Keep alive? (bool)
471
472         case HSE_REQ_SEND_RESPONSE_HEADER:
473             r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
474             sscanf(r->status_line, "%d", &r->status);
475             cid->ecb->dwHttpStatusCode = r->status;
476
477             /* Now fill in the HTTP headers, and the rest of it. Ick.
478              * lpdwDataType contains a string that has headers (in MIME
479              * format), a blank like, then (possibly) data. We need
480              * to parse it.
481              *
482              * Easy case first:
483              */
484             if (!lpdwDataType) {
485                 ap_send_http_header(r);
486                 return TRUE;
487             }
488
489             /* Make a copy - don't disturb the original */
490             data = ap_pstrdup(r->pool, (char *)lpdwDataType);
491
492             /* We *should* break before this while loop ends */
493             while (*data) {
494                 char *value, *lf = strchr(data, '\n');
495                 int p;
496
497 #ifdef RELAX_HEADER_RULE
498                 if (lf)
499                     *lf = '\0';
500 #else
501                 if (!lf) { /* Huh? Invalid data, I think */
502                         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
503                                     "ISA sent invalid headers: %s", r->filename);
504                         SetLastError(TODO_ERROR);
505                         return FALSE;
506                 }
507
508                 /* Get rid of \n and \r */
509                 *lf = '\0';
510 #endif
511                 p = strlen(data);
512                 if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
513         
514                 /* End of headers */
515                 if (*data == '\0') {
516 #ifdef RELAX_HEADER_RULE
517                     if (lf)
518 #endif
519                     data = lf + 1;  /* Reset data */
520                     break;
521                 }
522
523                 if (!(value = strchr(data, ':'))) {
524                     SetLastError(TODO_ERROR);
525                     /* ### euh... we're passing the wrong type of error
526                        ### code here */
527                     ap_log_rerror(APLOG_MARK, APLOG_ERR,
528                                   HTTP_INTERNAL_SERVER_ERROR, r,
529                                   "ISA sent invalid headers", r->filename);
530                     return FALSE;
531                 }
532
533                 *value++ = '\0';
534                 while (*value && ap_isspace(*value)) ++value;
535
536                 /* Check all the special-case headers. Similar to what
537                  * ap_scan_script_header_err() does (see that function for
538                  * more detail)
539                  */
540
541                 if (!strcasecmp(data, "Content-Type")) {
542                     char *tmp;
543                     /* Nuke trailing whitespace */
544             
545                     char *endp = value + strlen(value) - 1;
546                     while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
547
548                     tmp = ap_pstrdup (r->pool, value);
549                     ap_str_tolower(tmp);
550                     r->content_type = tmp;
551                 }
552                 else if (!strcasecmp(data, "Content-Length")) {
553                     ap_table_set(r->headers_out, data, value);
554                 }
555                 else if (!strcasecmp(data, "Transfer-Encoding")) {
556                     ap_table_set(r->headers_out, data, value);
557                 }
558                 else if (!strcasecmp(data, "Set-Cookie")) {
559                     ap_table_add(r->err_headers_out, data, value);
560                 }
561                 else {
562                     ap_table_merge(r->err_headers_out, data, value);
563                 }
564
565                 /* Reset data */
566 #ifdef RELAX_HEADER_RULE
567                 if (!lf) {
568                     data += p;
569                     break;
570                 }
571 #endif
572                 data = lf + 1;
573             }
574
575             /* All the headers should be set now */
576
577             ap_send_http_header(r);
578
579             /* Any data left should now be sent directly */
580             ap_rputs(data, r);
581
582             return TRUE;
583
584         case HSE_REQ_MAP_URL_TO_PATH:
585             /* Map a URL to a filename */
586             subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
587                                            *lpdwSize), r);
588
589             GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
590
591             /* IIS puts a trailing slash on directories, Apache doesn't */
592
593             if (subreq->finfo.filetype == APR_DIR) {
594                 int l = strlen((char *)lpvBuffer);
595
596                 ((char *)lpvBuffer)[l] = '\\';
597                 ((char *)lpvBuffer)[l + 1] = '\0';
598             }
599
600             return TRUE;
601
602         case HSE_REQ_DONE_WITH_SESSION:
603             /* TODO: Signal the main request with the event to complete the session
604              */
605             return TRUE;
606
607         /* We don't support all this async I/O, Microsoft-specific stuff */
608         case HSE_REQ_IO_COMPLETION:
609             /* TODO: Emulate a completion port, if we can...
610              * Record the callback address and user defined argument...
611              * we will call this after any async request (including transmitfile)
612              * as if the request had been async.
613              */
614
615         case HSE_REQ_TRANSMIT_FILE:
616             /* Use TransmitFile (in leiu of WriteClient)... nothing wrong with that
617              */
618
619             /* ### euh... we're passing the wrong type of error code here */
620             ap_log_rerror(APLOG_MARK, APLOG_WARNING,
621                           HTTP_INTERNAL_SERVER_ERROR, r,
622                           "ISAPI asynchronous I/O not supported: %s",
623                           r->filename);
624
625         case HSE_APPEND_LOG_PARAMETER:
626             /* Log lpvBuffer, of lpdwSize bytes */
627             return TRUE;
628
629         case HSE_REQ_ABORTIVE_CLOSE:
630         case HSE_REQ_ASYNC_READ_CLIENT:
631         case HSE_REQ_CLOSE_CONNECTION:
632         case HSE_REQ_GET_CERT_INFO_EX:
633         case HSE_REQ_GET_IMPERSONATION_TOKEN:
634         case HSE_REQ_GET_SSPI_INFO:
635         case HSE_REQ_IS_KEEP_CONN:
636         case HSE_REQ_MAP_URL_TO_PATH_EX:
637         case HSE_REQ_REFRESH_ISAPI_ACL:
638
639         default:
640             SetLastError(ERROR_INVALID_PARAMETER);
641             return FALSE;
642     }
643 }
644
645 handler_rec isapi_handlers[] = {
646     { "isapi-isa", isapi_handler },
647     { NULL}
648 };
649
650 module isapi_module = {
651    STANDARD20_MODULE_STUFF,
652    NULL,                        /* create per-dir config */
653    NULL,                        /* merge per-dir config */
654    NULL,                        /* server config */
655    NULL,                        /* merge server config */
656    NULL,                        /* command ap_table_t */
657    isapi_handlers,              /* handlers */
658    NULL                         /* register hooks */
659 };