]> granicus.if.org Git - apache/blob - modules/arch/win32/mod_isapi.c
Update our copyright for this year.
[apache] / modules / arch / win32 / mod_isapi.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 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 "apr_strings.h"
79 #include "apr_portable.h"
80 #include "apr_buckets.h"
81 #include "ap_config.h"
82 #include "httpd.h"
83 #include "http_config.h"
84 #include "http_core.h"
85 #include "http_protocol.h"
86 #include "http_request.h"
87 #include "http_log.h"
88 #include "util_script.h"
89 #include "mod_core.h"
90
91 /* We use the exact same header file as the original */
92 #include <HttpExt.h>
93
94 #if !defined(HSE_REQ_MAP_URL_TO_PATH_EX) \
95  || !defined(HSE_REQ_SEND_RESPONSE_HEADER_EX)
96 #pragma message("WARNING: This build of Apache is missing the recent changes")
97 #pragma message("in the Microsoft Win32 Platform SDK; some mod_isapi features")
98 #pragma message("will be disabled.  To obtain the latest Platform SDK files,")
99 #pragma message("please refer to:")
100 #pragma message("http://msdn.microsoft.com/downloads/sdks/platform/platform.asp")
101 #endif
102
103 /* TODO: Unknown errors that must be researched for correct codes */
104
105 #define TODO_ERROR 1
106
107 /* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
108    define this to conform */
109 #define RELAX_HEADER_RULE
110
111 module AP_MODULE_DECLARE_DATA isapi_module;
112
113 /* Declare the ISAPI functions */
114
115 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
116                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
117 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes,
118                          DWORD dwReserved);
119 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
120 BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
121                                    LPVOID lpvBuffer, LPDWORD lpdwSize,
122                                    LPDWORD lpdwDataType);
123
124 /*
125     The optimiser blows it totally here. What happens is that autos are addressed relative to the
126     stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
127     between setting HttpExtensionProc's address and calling through it. We work around the problem by 
128     forcing it to use frame pointers.
129
130     The revisions below may eliminate this artifact.
131 */
132 #pragma optimize("y",off)
133
134 /* Our isapi server config structure */
135
136 typedef struct {
137     apr_array_header_t *loaded;
138     DWORD ReadAheadBuffer;
139     int LogNotSupported;
140     int AppendLogToErrors;
141     int AppendLogToQuery;
142 } isapi_server_conf;
143
144 /* Our loaded isapi module description structure */
145
146 typedef struct {
147     const char *filename;
148     apr_dso_handle_t *handle;
149     HSE_VERSION_INFO *pVer;
150     PFN_GETEXTENSIONVERSION GetExtensionVersion;
151     PFN_HTTPEXTENSIONPROC   HttpExtensionProc;
152     PFN_TERMINATEEXTENSION  TerminateExtension;
153     int   refcount;
154     DWORD timeout;
155     BOOL  fakeasync;
156     DWORD reportversion;
157 } isapi_loaded;
158
159 /* Our "Connection ID" structure */
160
161 typedef struct {
162     LPEXTENSION_CONTROL_BLOCK ecb;
163     isapi_server_conf *sconf;
164     isapi_loaded *isa;
165     request_rec  *r;
166     PFN_HSE_IO_COMPLETION completion;
167     PVOID  completion_arg;
168     HANDLE complete;
169 } isapi_cid;
170
171 static void *create_isapi_server_config(apr_pool_t *p, server_rec *s)
172 {
173     isapi_server_conf *sconf = apr_palloc(p, sizeof(isapi_server_conf));
174     sconf->loaded = apr_array_make(p, 20, sizeof(isapi_loaded*));
175     
176     sconf->ReadAheadBuffer = 49152;
177     sconf->LogNotSupported    = -1;
178     sconf->AppendLogToErrors   = 0;
179     sconf->AppendLogToQuery    = 0;
180
181     return sconf;
182 }
183
184 static int compare_loaded(const void *av, const void *bv)
185 {
186     const isapi_loaded **a = av;
187     const isapi_loaded **b = bv;
188
189     return strcmp((*a)->filename, (*b)->filename);
190 }
191
192 static int isapi_post_config(apr_pool_t *p, apr_pool_t *plog,
193                              apr_pool_t *ptemp, server_rec *s)
194 {
195     isapi_server_conf *sconf = ap_get_module_config(s->module_config, 
196                                                     &isapi_module);
197     isapi_loaded **elts = (isapi_loaded **)sconf->loaded->elts;
198     int nelts = sconf->loaded->nelts;
199
200     /* sort the elements of the main_server, by filename */
201     qsort(elts, nelts, sizeof(isapi_loaded*), compare_loaded);
202
203     /* and make all virtualhosts share the same */
204     for (s = s->next; s; s = s->next) {
205         ap_set_module_config(s->module_config, &isapi_module, sconf);
206     }
207     return OK;
208 }
209
210 static apr_status_t isapi_unload(isapi_loaded* isa, int force);
211
212 static apr_status_t cleanup_isapi(void *isa)
213 {
214     return isapi_unload((isapi_loaded*) isa, TRUE);
215 }
216
217 static apr_status_t isapi_load(apr_pool_t *p, isapi_server_conf *sconf, 
218                                request_rec *r, const char *fpath, 
219                                isapi_loaded** isa)
220 {
221     isapi_loaded **found = (isapi_loaded **)sconf->loaded->elts;
222     apr_status_t rv;
223     int n;
224
225     for (n = 0; n < sconf->loaded->nelts; ++n) {
226         if (strcasecmp(fpath, (*found)->filename) == 0) {
227             break;
228         }
229         ++found;
230     }
231     
232     if (n < sconf->loaded->nelts) 
233     {
234         *isa = *found;
235         if ((*isa)->handle) 
236         {
237             ++(*isa)->refcount;
238             return APR_SUCCESS;
239         }
240         /* Otherwise we fall through and have to reload the resource
241          * into this existing mod_isapi cache bucket.
242          */
243     }
244     else
245     {
246         *isa = apr_pcalloc(p, sizeof(isapi_module));
247         (*isa)->filename = fpath;
248         (*isa)->pVer = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
249     
250         /* TODO: These need to become overrideable, so that we
251          * assure a given isapi can be fooled into behaving well.
252          */
253         (*isa)->timeout = INFINITE; /* microsecs */
254         (*isa)->fakeasync = TRUE;
255         (*isa)->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
256     }
257     
258     rv = apr_dso_load(&(*isa)->handle, fpath, p);
259     if (rv)
260     {
261         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
262                       "ISAPI %s failed to load", fpath);
263         (*isa)->handle = NULL;
264         return rv;
265     }
266
267     rv = apr_dso_sym((void**)&(*isa)->GetExtensionVersion, (*isa)->handle,
268                      "GetExtensionVersion");
269     if (rv)
270     {
271         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
272                       "ISAPI %s is missing GetExtensionVersion()",
273                       fpath);
274         apr_dso_unload((*isa)->handle);
275         (*isa)->handle = NULL;
276         return rv;
277     }
278
279     rv = apr_dso_sym((void**)&(*isa)->HttpExtensionProc, (*isa)->handle,
280                      "HttpExtensionProc");
281     if (rv)
282     {
283         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
284                       "ISAPI %s is missing HttpExtensionProc()",
285                       fpath);
286         apr_dso_unload((*isa)->handle);
287         (*isa)->handle = NULL;
288         return rv;
289     }
290
291     /* TerminateExtension() is an optional interface */
292     rv = apr_dso_sym((void**)&(*isa)->TerminateExtension, (*isa)->handle,
293                      "TerminateExtension");
294     SetLastError(0);
295
296     /* Run GetExtensionVersion() */
297     if (!((*isa)->GetExtensionVersion)((*isa)->pVer)) {
298         apr_status_t rv = apr_get_os_error();
299         ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
300                       "ISAPI %s call GetExtensionVersion() failed", 
301                       fpath);
302         apr_dso_unload((*isa)->handle);
303         (*isa)->handle = NULL;
304         return rv;
305     }
306
307     ++(*isa)->refcount;
308
309     apr_pool_cleanup_register(p, *isa, cleanup_isapi, 
310                                    apr_pool_cleanup_null);
311
312     return APR_SUCCESS;
313 }
314
315 static apr_status_t isapi_unload(isapi_loaded* isa, int force)
316 {
317     /* All done with the DLL... get rid of it...
318      *
319      * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
320      * and if it returns TRUE, unload, otherwise, cache it.
321      */
322     if (((--isa->refcount > 0) && !force) || !isa->handle)
323         return APR_SUCCESS;
324     if (isa->TerminateExtension) {
325         if (force)
326             (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
327         else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD))
328             return APR_EGENERAL;
329     }
330     apr_dso_unload(isa->handle);
331     isa->handle = NULL;
332     return APR_SUCCESS;
333 }
334
335 apr_status_t isapi_handler (request_rec *r)
336 {
337     isapi_server_conf * sconf;
338     apr_table_t *e;
339     apr_status_t rv;
340     isapi_loaded *isa;
341     isapi_cid *cid;
342     const char *val;
343     DWORD read;
344     int res;
345     
346     if(strcmp(r->handler, "isapi-isa"))
347         return DECLINED;    
348
349     sconf = ap_get_module_config(r->server->module_config, &isapi_module);
350     e = r->subprocess_env;
351
352     /* Use similar restrictions as CGIs
353      *
354      * If this fails, it's pointless to load the isapi dll.
355      */
356     if (!(ap_allow_options(r) & OPT_EXECCGI))
357         return HTTP_FORBIDDEN;
358
359     if (r->finfo.filetype == APR_NOFILE)
360         return HTTP_NOT_FOUND;
361
362     if (r->finfo.filetype != APR_REG)
363         return HTTP_FORBIDDEN;
364
365     if (r->path_info && *r->path_info && !r->used_path_info)
366         return HTTP_NOT_FOUND;
367
368     /* Load the isapi extention without caching (sconf == NULL) 
369      * but note that we will recover an existing cached module.
370      */
371     if (isapi_load(r->pool, sconf, r, r->filename, &isa) != APR_SUCCESS)
372         return HTTP_INTERNAL_SERVER_ERROR;
373         
374     /* Set up variables */
375     ap_add_common_vars(r);
376     ap_add_cgi_vars(r);
377     apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
378     if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on"))
379         apr_table_setn(e, "SERVER_PORT_SECURE", "1");
380     else
381         apr_table_setn(e, "SERVER_PORT_SECURE", "0");
382     apr_table_setn(e, "URL", r->uri);
383
384     /* Set up connection structure and ecb */
385     cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
386     cid->sconf = ap_get_module_config(r->server->module_config, &isapi_module);
387
388     cid->ecb = apr_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
389     cid->ecb->ConnID = (HCONN)cid;
390     cid->isa = isa;
391     cid->r = r;
392     cid->r->status = 0;
393     cid->complete = NULL;
394     cid->completion = NULL;
395     
396     cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
397     cid->ecb->dwVersion = isa->reportversion;
398     cid->ecb->dwHttpStatusCode = 0;
399     strcpy(cid->ecb->lpszLogData, "");
400     // TODO: are copies really needed here?
401     cid->ecb->lpszMethod = apr_pstrdup(r->pool, (char*) r->method);
402     cid->ecb->lpszQueryString = apr_pstrdup(r->pool, 
403                                 (char*) apr_table_get(e, "QUERY_STRING"));
404     cid->ecb->lpszPathInfo = apr_pstrdup(r->pool, 
405                              (char*) apr_table_get(e, "PATH_INFO"));
406     cid->ecb->lpszPathTranslated = apr_pstrdup(r->pool, 
407                                    (char*) apr_table_get(e, "PATH_TRANSLATED"));
408     cid->ecb->lpszContentType = apr_pstrdup(r->pool, 
409                                 (char*) apr_table_get(e, "CONTENT_TYPE"));
410     /* Set up the callbacks */
411     cid->ecb->GetServerVariable = GetServerVariable;
412     cid->ecb->WriteClient = WriteClient;
413     cid->ecb->ReadClient = ReadClient;
414     cid->ecb->ServerSupportFunction = ServerSupportFunction;
415
416     
417     /* Set up client input */
418     res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
419     if (res) {
420         isapi_unload(isa, FALSE);
421         return res;
422     }
423
424     if (ap_should_client_block(r)) {
425         /* Time to start reading the appropriate amount of data,
426          * and allow the administrator to tweak the number
427          * TODO: add the httpd.conf option for ReadAheadBuffer.
428          */
429         if (r->remaining) {
430             cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
431             if (cid->ecb->cbTotalBytes > cid->sconf->ReadAheadBuffer)
432                 cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
433             else
434                 cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
435         }
436         else
437         {
438             cid->ecb->cbTotalBytes = 0xffffffff;
439             cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
440         }
441
442         cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
443
444         read = 0;
445         while (read < cid->ecb->cbAvailable &&
446                ((res = ap_get_client_block(r, cid->ecb->lpbData + read,
447                                         cid->ecb->cbAvailable - read)) > 0)) {
448             read += res;
449         }
450
451         if (res < 0) {
452             isapi_unload(isa, FALSE);
453             return HTTP_INTERNAL_SERVER_ERROR;
454         }
455
456         /* Although it's not to spec, IIS seems to null-terminate
457          * its lpdData string. So we will too.
458          */
459         if (res == 0)
460             cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
461         else
462             cid->ecb->cbAvailable = read;
463         cid->ecb->lpbData[read] = '\0';
464     }
465     else {
466         cid->ecb->cbTotalBytes = 0;
467         cid->ecb->cbAvailable = 0;
468         cid->ecb->lpbData = NULL;
469     }
470
471     /* All right... try and run the sucker */
472     rv = (*isa->HttpExtensionProc)(cid->ecb);
473
474     /* Check for a log message - and log it */
475     if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
476         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
477                       "ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
478
479     switch(rv) {
480         case 0:  /* Strange, but MS isapi accepts this as success */
481         case HSE_STATUS_SUCCESS:
482         case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
483             /* Ignore the keepalive stuff; Apache handles it just fine without
484              * the ISA's "advice".
485              * Per Microsoft: "In IIS versions 4.0 and later, the return
486              * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
487              * are functionally identical: Keep-Alive connections are
488              * maintained, if supported by the client."
489              * ... so we were pat all this time
490              */
491             break;
492
493         case HSE_STATUS_PENDING:    
494             /* emulating async behavior...
495              *
496              * Create a cid->completed event and wait on it for some timeout
497              * so that the app thinks is it running async.
498              *
499              * All async ServerSupportFunction calls will be handled through
500              * the registered IO_COMPLETION hook.
501              */
502             
503             if (!isa->fakeasync) {
504                 if (cid->sconf->LogNotSupported)
505                 {
506                      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
507                                    "ISAPI %s asynch I/O request refused", 
508                                    r->filename);
509                      cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
510                 }
511             }
512             else {
513                 cid->complete = CreateEvent(NULL, FALSE, FALSE, NULL);
514                 if (WaitForSingleObject(cid->complete, isa->timeout)
515                         == WAIT_TIMEOUT) {
516                     /* TODO: Now what... if this hung, then do we kill our own
517                      * thread to force its death?  For now leave timeout = -1
518                      */
519                 }
520             }
521             break;
522
523         case HSE_STATUS_ERROR:    
524             /* end response if we have yet to do so.
525              */
526             cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
527             break;
528
529         default:
530             /* TODO: log unrecognized retval for debugging 
531              */
532             cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
533             break;
534     }
535
536     /* Set the status (for logging) */
537     if (cid->ecb->dwHttpStatusCode) {
538         cid->r->status = cid->ecb->dwHttpStatusCode;
539     }
540
541     /* All done with the DLL... get rid of it... */
542     isapi_unload(isa, FALSE);
543     
544     return OK;          /* NOT r->status, even if it has changed. */
545 }
546 #pragma optimize("",on)
547
548 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
549                                LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
550 {
551     request_rec *r = ((isapi_cid *)hConn)->r;
552     const char *result;
553     DWORD len;
554
555     if (!strcmp(lpszVariableName, "ALL_HTTP")) 
556     {
557         /* lf delimited, colon split, comma seperated and 
558          * null terminated list of HTTP_ vars 
559          */
560         const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
561         const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
562         int i;
563
564         for (len = 0, i = 0; i < arr->nelts; i++) {
565             if (!strncmp(elts[i].key, "HTTP_", 5)) {
566                 len += strlen(elts[i].key) + strlen(elts[i].val) + 2;
567             }
568         }
569   
570         if (*lpdwSizeofBuffer < len + 1) {
571             *lpdwSizeofBuffer = len + 1;
572             SetLastError(ERROR_INSUFFICIENT_BUFFER);
573             return FALSE;
574         }
575     
576         for (i = 0; i < arr->nelts; i++) {
577             if (!strncmp(elts[i].key, "HTTP_", 5)) {
578                 strcpy(lpvBuffer, elts[i].key);
579                 ((char*)lpvBuffer) += strlen(elts[i].key);
580                 *(((char*)lpvBuffer)++) = ':';
581                 strcpy(lpvBuffer, elts[i].val);
582                 ((char*)lpvBuffer) += strlen(elts[i].val);
583                 *(((char*)lpvBuffer)++) = '\n';
584             }
585         }
586
587         *(((char*)lpvBuffer)++) = '\0';
588         *lpdwSizeofBuffer = len;
589         return TRUE;
590     }
591     
592     if (!strcmp(lpszVariableName, "ALL_RAW")) 
593     {
594         /* lf delimited, colon split, comma seperated and 
595          * null terminated list of the raw request header
596          */
597         const apr_array_header_t *arr = apr_table_elts(r->headers_in);
598         const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
599         int i;
600
601         for (len = 0, i = 0; i < arr->nelts; i++) {
602             len += strlen(elts[i].key) + strlen(elts[i].val) + 2;
603         }
604   
605         if (*lpdwSizeofBuffer < len + 1) {
606             *lpdwSizeofBuffer = len + 1;
607             SetLastError(ERROR_INSUFFICIENT_BUFFER);
608             return FALSE;
609         }
610     
611         for (i = 0; i < arr->nelts; i++) {
612             strcpy(lpvBuffer, elts[i].key);
613             ((char*)lpvBuffer) += strlen(elts[i].key);
614             *(((char*)lpvBuffer)++) = ':';
615             *(((char*)lpvBuffer)++) = ' ';
616             strcpy(lpvBuffer, elts[i].val);
617             ((char*)lpvBuffer) += strlen(elts[i].val);
618             *(((char*)lpvBuffer)++) = '\n';
619         }
620         *(((char*)lpvBuffer)++) = '\0';
621         *lpdwSizeofBuffer = len;
622         return TRUE;
623     }
624     
625     /* Not a special case */
626     result = apr_table_get(r->subprocess_env, lpszVariableName);
627
628     if (result) {
629         len = strlen(result);
630         if (*lpdwSizeofBuffer < len + 1) {
631             *lpdwSizeofBuffer = len + 1;
632             SetLastError(ERROR_INSUFFICIENT_BUFFER);
633             return FALSE;
634         }
635         strcpy(lpvBuffer, result);
636         *lpdwSizeofBuffer = len;
637         return TRUE;
638     }
639
640     /* Not Found */
641     SetLastError(ERROR_INVALID_INDEX);
642     return FALSE;
643 }
644
645 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes,
646                          DWORD dwReserved)
647 {
648     request_rec *r = ((isapi_cid *)ConnID)->r;
649     apr_bucket_brigade *bb;
650     apr_bucket *b;
651
652     if (dwReserved == HSE_IO_SYNC)
653         ; /* XXX: Fake it */
654
655     bb = apr_brigade_create(r->pool);
656     b = apr_bucket_transient_create(Buffer, *lpdwBytes);
657     APR_BRIGADE_INSERT_TAIL(bb, b);
658     b = apr_bucket_flush_create();
659     APR_BRIGADE_INSERT_TAIL(bb, b);
660     ap_pass_brigade(r->output_filters, bb);
661
662     return TRUE;
663 }
664
665 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
666 {
667     request_rec *r = ((isapi_cid *)ConnID)->r;
668     DWORD read = 0;
669     int res;
670
671     if (r->remaining < *lpdwSize) {
672         *lpdwSize = (apr_size_t)r->remaining;
673     }
674
675     while (read < *lpdwSize &&
676            ((res = ap_get_client_block(r, (char*)lpvBuffer + read,
677                                        *lpdwSize - read)) > 0)) {
678         read += res;
679     }
680
681     *lpdwSize = read;
682     return TRUE;
683 }
684
685 static apr_ssize_t SendResponseHeaderEx(isapi_cid *cid, const char *stat,
686                                         const char *head, apr_size_t statlen,
687                                         apr_size_t headlen)
688 {
689     int termarg;
690     char *termch;
691
692     if (!stat || statlen == 0 || !*stat) {
693         stat = "Status: 200 OK";
694     }
695     else {
696         char *newstat;
697         newstat = apr_palloc(cid->r->pool, statlen + 9);
698         strcpy(newstat, "Status: ");
699         apr_cpystrn(newstat + 8, stat, statlen + 1);
700         stat = newstat;
701     }
702
703     if (!head || headlen == 0 || !*head) {
704         head = "\r\n";
705     }
706     else
707     {
708         if (head[headlen]) {
709             /* Whoops... not NULL terminated */
710             head = apr_pstrndup(cid->r->pool, head, headlen);
711         }
712     }
713  
714     /* Parse them out, or die trying */
715     cid->r->status= ap_scan_script_header_err_strs(cid->r, NULL, &termch,
716                                                   &termarg, stat, head, NULL);
717     cid->ecb->dwHttpStatusCode = cid->r->status;
718     if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR)
719         return -1;
720     
721     /* Headers will actually go when they are good and ready */
722
723     /* If all went well, tell the caller we consumed the headers complete */
724     if (!termch)
725         return(headlen);
726
727     /* Any data left is sent directly by the caller, all we
728      * give back is the size of the headers we consumed
729      */
730     if (termch && (termarg == 1) && head + headlen > termch) {
731         return termch - head;
732     }
733     return 0;
734 }
735
736 BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
737                                   LPVOID lpvBuffer, LPDWORD lpdwSize,
738                                   LPDWORD lpdwDataType)
739 {
740     isapi_cid *cid = (isapi_cid *)hConn;
741     request_rec *r = cid->r;
742     request_rec *subreq;
743
744     switch (dwHSERequest) {
745     case 1: /* HSE_REQ_SEND_URL_REDIRECT_RESP */
746         /* Set the status to be returned when the HttpExtensionProc()
747          * is done.
748          * WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP
749          *          and HSE_REQ_SEND_URL as equivalant per the Jan 2000 SDK.
750          *          They most definately are not, even in their own samples.
751          */
752         apr_table_set (r->headers_out, "Location", lpvBuffer);
753         cid->r->status = cid->ecb->dwHttpStatusCode 
754                                                = HTTP_MOVED_TEMPORARILY;
755         return TRUE;
756
757     case 2: /* HSE_REQ_SEND_URL */
758         /* Soak up remaining input */
759         if (r->remaining > 0) {
760             char argsbuffer[HUGE_STRING_LEN];
761             while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
762         }
763
764         /* Reset the method to GET */
765         r->method = apr_pstrdup(r->pool, "GET");
766         r->method_number = M_GET;
767
768         /* Don't let anyone think there's still data */
769         apr_table_unset(r->headers_in, "Content-Length");
770
771         /* AV fault per PR3598 - redirected path is lost! */
772         (char*)lpvBuffer = apr_pstrdup(r->pool, (char*)lpvBuffer);
773         ap_internal_redirect((char*)lpvBuffer, r);
774         return TRUE;
775
776     case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */
777     {
778         /* Parse them out, or die trying */
779         apr_size_t statlen = 0, headlen = 0;
780         apr_ssize_t ate;
781         if (lpvBuffer)
782             statlen = strlen((char*) lpvBuffer);
783         if (lpdwDataType)
784             headlen = strlen((char*) lpdwDataType);
785         ate = SendResponseHeaderEx(cid, (char*) lpvBuffer,
786                                    (char*) lpdwDataType,
787                                    statlen, headlen);
788         if (ate < 0) {
789             SetLastError(TODO_ERROR);
790             return FALSE;
791         }
792         else if ((apr_size_t)ate < headlen) {
793             apr_bucket_brigade *bb;
794             apr_bucket *b;
795             bb = apr_brigade_create(cid->r->pool);
796             b = apr_bucket_transient_create((char*) lpdwDataType + ate, 
797                                            headlen - ate);
798             APR_BRIGADE_INSERT_TAIL(bb, b);
799             b = apr_bucket_flush_create();
800             APR_BRIGADE_INSERT_TAIL(bb, b);
801             ap_pass_brigade(cid->r->output_filters, bb);
802         }
803         return TRUE;
804     }
805
806     case 4: /* HSE_REQ_DONE_WITH_SESSION */
807         /* Signal to resume the thread completing this request
808          */
809         if (cid->complete)
810             SetEvent(cid->complete);            
811         return TRUE;
812
813     case 1001: /* HSE_REQ_MAP_URL_TO_PATH */
814     {
815         /* Map a URL to a filename */
816         char *file = (char *)lpvBuffer;
817         DWORD len;
818         subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *lpdwSize),
819                                        r, NULL);
820
821         len = apr_cpystrn(file, subreq->filename, *lpdwSize) - file;
822
823
824         /* IIS puts a trailing slash on directories, Apache doesn't */
825         if (subreq->finfo.filetype == APR_DIR) {
826             if (len < *lpdwSize - 1) {
827                 file[len++] = '\\';
828                 file[len] = '\0';
829             }
830         }
831         *lpdwSize = len;
832         return TRUE;
833     }
834
835     case 1002: /* HSE_REQ_GET_SSPI_INFO */
836         if (cid->sconf->LogNotSupported)
837             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
838                            "ISAPI ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
839                            "is not supported: %s", r->filename);
840         SetLastError(ERROR_INVALID_PARAMETER);
841         return FALSE;
842         
843     case 1003: /* HSE_APPEND_LOG_PARAMETER */
844         /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
845          */
846         apr_table_set(r->notes, "isapi-parameter", (char*) lpvBuffer);
847         if (cid->sconf->AppendLogToQuery) {
848             if (r->args)
849                 r->args = apr_pstrcat(r->pool, r->args, (char*) lpvBuffer, NULL);
850             else
851                 r->args = apr_pstrdup(r->pool, (char*) lpvBuffer);
852         }
853         if (cid->sconf->AppendLogToErrors)
854             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
855                           "ISAPI %s: %s", cid->r->filename,
856                           (char*) lpvBuffer);
857         return TRUE;
858         
859     case 1005: /* HSE_REQ_IO_COMPLETION */
860         /* Emulates a completion port...  Record callback address and 
861          * user defined arg, we will call this after any async request 
862          * (e.g. transmitfile) as if the request executed async.
863          * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
864          * to HSE_REQ_IO_COMPLETION, and lpvBuffer may be set to NULL.
865          */
866         if (!cid->isa->fakeasync) {
867             if (cid->sconf->LogNotSupported)
868                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
869                           "ISAPI ServerSupportFunction HSE_REQ_IO_COMPLETION "
870                           "is not supported: %s", r->filename);
871             SetLastError(ERROR_INVALID_PARAMETER);
872             return FALSE;
873         }
874         cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
875         cid->completion_arg = (PVOID) lpdwDataType;
876         return TRUE;
877
878     case 1006: /* HSE_REQ_TRANSMIT_FILE */
879     {
880         HSE_TF_INFO *tf = (HSE_TF_INFO*)lpvBuffer;
881         apr_status_t rv;
882         apr_bucket_brigade *bb;
883         apr_bucket *b;
884         apr_file_t *fd;
885
886         if (!cid->isa->fakeasync && (tf->dwFlags & HSE_IO_ASYNC)) {
887             if (cid->sconf->LogNotSupported)
888                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
889                          "ISAPI ServerSupportFunction HSE_REQ_TRANSMIT_FILE "
890                          "as HSE_IO_ASYNC is not supported: %s", r->filename);
891             SetLastError(ERROR_INVALID_PARAMETER);
892             return FALSE;
893         }
894         
895         if ((rv = apr_os_file_put(&fd, tf->hFile, 0, r->pool)) != APR_SUCCESS) {
896             return FALSE;
897         }
898         
899         /* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */
900         bb = apr_brigade_create(r->pool);
901
902         if (tf->dwFlags & HSE_IO_SEND_HEADERS) 
903         {
904             /* According to MS: if calling HSE_REQ_TRANSMIT_FILE with the
905              * HSE_IO_SEND_HEADERS flag, then you can't otherwise call any
906              * HSE_SEND_RESPONSE_HEADERS* fn, but if you don't use the flag,
907              * you must have done so.  They document that the pHead headers
908              * option is valid only for HSE_IO_SEND_HEADERS - we are a bit
909              * more flexible and assume with the flag, pHead are the
910              * response headers, and without, pHead simply contains text
911              * (handled after this case).
912              */
913             apr_ssize_t ate = SendResponseHeaderEx(cid, tf->pszStatusCode, 
914                                                    (char*)tf->pHead,
915                                                    strlen(tf->pszStatusCode), 
916                                                    tf->HeadLength);
917             if (ate < 0)
918             {
919                 apr_brigade_destroy(bb);
920                 SetLastError(TODO_ERROR);
921                 return FALSE;
922             }
923             if ((apr_size_t)ate < tf->HeadLength)
924             {
925                 b = apr_bucket_transient_create((char*)tf->pHead + ate, 
926                                                 tf->HeadLength - ate);
927                 APR_BRIGADE_INSERT_TAIL(bb, b);
928             }
929         }
930         else if (tf->pHead && tf->HeadLength) {
931             b = apr_bucket_transient_create((char*)tf->pHead, 
932                                             tf->HeadLength);
933             APR_BRIGADE_INSERT_TAIL(bb, b);
934         }
935
936         b = apr_bucket_file_create(fd, tf->Offset, 
937                                    tf->BytesToWrite, r->pool);
938         APR_BRIGADE_INSERT_TAIL(bb, b);
939         
940         if (tf->pTail && tf->TailLength) {
941             b = apr_bucket_transient_create((char*)tf->pTail, 
942                                             tf->TailLength);
943             APR_BRIGADE_INSERT_TAIL(bb, b);
944         }
945         
946         b = apr_bucket_flush_create();
947         APR_BRIGADE_INSERT_TAIL(bb, b);
948         ap_pass_brigade(r->output_filters, bb);
949
950         /* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND)
951          */
952
953         if (tf->dwFlags & HSE_IO_ASYNC) {
954             /* XXX: Fake async response,
955              * use tf->pfnHseIO, or if NULL, then use cid->fnIOComplete
956              * pass pContect to the HseIO callback.
957              */
958         }
959         return TRUE;
960     }
961
962     case 1007: /* HSE_REQ_REFRESH_ISAPI_ACL */
963         if (cid->sconf->LogNotSupported)
964             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
965                           "ISAPI ServerSupportFunction "
966                           "HSE_REQ_REFRESH_ISAPI_ACL "
967                           "is not supported: %s", r->filename);
968         SetLastError(ERROR_INVALID_PARAMETER);
969         return FALSE;
970
971     case 1008: /* HSE_REQ_IS_KEEP_CONN */
972         *((LPBOOL) lpvBuffer) = (r->connection->keepalive == 1);
973         return TRUE;
974
975     case 1010: /* XXX: Fake it : HSE_REQ_ASYNC_READ_CLIENT */
976         if (cid->sconf->LogNotSupported)
977             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
978                           "ISAPI asynchronous I/O not supported: %s", 
979                           r->filename);
980         SetLastError(ERROR_INVALID_PARAMETER);
981         return FALSE;
982
983     case 1011: /* HSE_REQ_GET_IMPERSONATION_TOKEN  Added in ISAPI 4.0 */
984         if (cid->sconf->LogNotSupported)
985             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
986                           "ISAPI ServerSupportFunction "
987                           "HSE_REQ_GET_IMPERSONATION_TOKEN "
988                           "is not supported: %s", r->filename);
989         SetLastError(ERROR_INVALID_PARAMETER);
990         return FALSE;
991
992 #ifdef HSE_REQ_MAP_URL_TO_PATH_EX
993     case 1012: /* HSE_REQ_MAP_URL_TO_PATH_EX */
994     {
995         /* Map a URL to a filename */
996         LPHSE_URL_MAPEX_INFO info = (LPHSE_URL_MAPEX_INFO) lpdwDataType;
997         char* test_uri = apr_pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize);
998
999         subreq = ap_sub_req_lookup_uri(test_uri, r, NULL);
1000         info->cchMatchingURL = strlen(test_uri);        
1001         info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename, 
1002                                             MAX_PATH) - info->lpszPath;
1003
1004         /* Mapping started with assuming both strings matched.
1005          * Now roll on the path_info as a mismatch and handle
1006          * terminating slashes for directory matches.
1007          */
1008         if (subreq->path_info && *subreq->path_info) {
1009             apr_cpystrn(info->lpszPath + info->cchMatchingPath, 
1010                         subreq->path_info, MAX_PATH - info->cchMatchingPath);
1011             info->cchMatchingURL -= strlen(subreq->path_info);
1012             if (subreq->finfo.filetype == APR_DIR
1013                  && info->cchMatchingPath < MAX_PATH - 1) {
1014                 /* roll forward over path_info's first slash */
1015                 ++info->cchMatchingPath;
1016                 ++info->cchMatchingURL;
1017             }
1018         }
1019         else if (subreq->finfo.filetype == APR_DIR
1020                  && info->cchMatchingPath < MAX_PATH - 1) {
1021             /* Add a trailing slash for directory */
1022             info->lpszPath[info->cchMatchingPath++] = '/';
1023             info->lpszPath[info->cchMatchingPath] = '\0';
1024         }
1025
1026         /* If the matched isn't a file, roll match back to the prior slash */
1027         if (subreq->finfo.filetype == APR_NOFILE) {
1028             while (info->cchMatchingPath && info->cchMatchingURL) {
1029                 if (info->lpszPath[info->cchMatchingPath - 1] == '/') 
1030                     break;
1031                 --info->cchMatchingPath;
1032                 --info->cchMatchingURL;
1033             }
1034         }
1035         
1036         /* Paths returned with back slashes */
1037         for (test_uri = info->lpszPath; *test_uri; ++test_uri)
1038             if (*test_uri == '/')
1039                 *test_uri = '\\';
1040         
1041         /* is a combination of:
1042          * HSE_URL_FLAGS_READ         0x001 Allow read
1043          * HSE_URL_FLAGS_WRITE        0x002 Allow write
1044          * HSE_URL_FLAGS_EXECUTE      0x004 Allow execute
1045          * HSE_URL_FLAGS_SSL          0x008 Require SSL
1046          * HSE_URL_FLAGS_DONT_CACHE   0x010 Don't cache (VRoot only)
1047          * HSE_URL_FLAGS_NEGO_CERT    0x020 Allow client SSL cert
1048          * HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert
1049          * HSE_URL_FLAGS_MAP_CERT     0x080 Map client SSL cert to account
1050          * HSE_URL_FLAGS_SSL128       0x100 Require 128-bit SSL cert
1051          * HSE_URL_FLAGS_SCRIPT       0x200 Allow script execution
1052          *
1053          * XxX: As everywhere, EXEC flags could use some work...
1054          *      and this could go further with more flags, as desired.
1055          */ 
1056         info->dwFlags = (subreq->finfo.protection & APR_UREAD    ? 0x001 : 0)
1057                       | (subreq->finfo.protection & APR_UWRITE   ? 0x002 : 0)
1058                       | (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
1059         return TRUE;
1060     }
1061 #endif
1062
1063     case 1014: /* HSE_REQ_ABORTIVE_CLOSE */
1064         if (cid->sconf->LogNotSupported)
1065             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
1066                           "ISAPI ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
1067                           " is not supported: %s", r->filename);
1068         SetLastError(ERROR_INVALID_PARAMETER);
1069         return FALSE;
1070
1071     case 1015: /* HSE_REQ_GET_CERT_INFO_EX  Added in ISAPI 4.0 */
1072         if (cid->sconf->LogNotSupported)
1073             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
1074                           "ISAPI ServerSupportFunction "
1075                           "HSE_REQ_GET_CERT_INFO_EX "
1076                           "is not supported: %s", r->filename);        
1077         SetLastError(ERROR_INVALID_PARAMETER);
1078         return FALSE;
1079
1080 #ifdef HSE_REQ_SEND_RESPONSE_HEADER_EX
1081     case 1016: /* HSE_REQ_SEND_RESPONSE_HEADER_EX  Added in ISAPI 4.0 */
1082     {
1083         LPHSE_SEND_HEADER_EX_INFO shi
1084                                   = (LPHSE_SEND_HEADER_EX_INFO) lpvBuffer;
1085         /* XXX: ignore shi->fKeepConn?  We shouldn't need the advise */
1086         /* r->connection->keepalive = shi->fKeepConn; */
1087         apr_ssize_t ate = SendResponseHeaderEx(cid, shi->pszStatus, 
1088                                                shi->pszHeader,
1089                                                shi->cchStatus, 
1090                                                shi->cchHeader);
1091         if (ate < 0) {
1092             SetLastError(TODO_ERROR);
1093             return FALSE;
1094         }
1095         else if ((apr_size_t)ate < shi->cchHeader) {
1096             apr_bucket_brigade *bb;
1097             apr_bucket *b;
1098             bb = apr_brigade_create(cid->r->pool);
1099             b = apr_bucket_transient_create(shi->pszHeader + ate, 
1100                                             shi->cchHeader - ate);
1101             APR_BRIGADE_INSERT_TAIL(bb, b);
1102             b = apr_bucket_flush_create();
1103             APR_BRIGADE_INSERT_TAIL(bb, b);
1104             ap_pass_brigade(cid->r->output_filters, bb);
1105         }
1106         return TRUE;
1107
1108     }
1109 #endif
1110
1111     case 1017: /* HSE_REQ_CLOSE_CONNECTION  Added after ISAPI 4.0 */
1112         if (cid->sconf->LogNotSupported)
1113             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
1114                           "ISAPI ServerSupportFunction "
1115                           "HSE_REQ_CLOSE_CONNECTION "
1116                           "is not supported: %s", r->filename);
1117         SetLastError(ERROR_INVALID_PARAMETER);
1118         return FALSE;
1119
1120     case 1018: /* HSE_REQ_IS_CONNECTED  Added after ISAPI 4.0 */
1121         /* Returns True if client is connected c.f. MSKB Q188346
1122          * assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN
1123          */
1124         *((LPBOOL) lpvBuffer) = (r->connection->aborted == 0);
1125         return TRUE;
1126
1127     case 1020: /* HSE_REQ_EXTENSION_TRIGGER  Added after ISAPI 4.0 */
1128         /*  Undocumented - defined by the Microsoft Jan '00 Platform SDK
1129          */
1130         if (cid->sconf->LogNotSupported)
1131             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
1132                           "ISAPI ServerSupportFunction "
1133                           "HSE_REQ_EXTENSION_TRIGGER "
1134                           "is not supported: %s", r->filename);
1135         SetLastError(ERROR_INVALID_PARAMETER);
1136         return FALSE;
1137
1138     default:
1139         if (cid->sconf->LogNotSupported)
1140             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
1141                           "ISAPI ServerSupportFunction (%d) not supported: "
1142                           "%s", dwHSERequest, r->filename);
1143         SetLastError(ERROR_INVALID_PARAMETER);
1144         return FALSE;
1145     }
1146 }
1147
1148 /*
1149  * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
1150  */
1151 static const char *isapi_cmd_readaheadbuffer(cmd_parms *cmd, void *config, 
1152                                              char *arg)
1153 {
1154     isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
1155                                                    &isapi_module);
1156     char *scan;
1157     long val;
1158
1159     if (((val = strtol(arg, (char **) &scan, 10)) <= 0) || *scan)
1160         return "ISAPIReadAheadBuffer must be a legitimate value.";
1161     
1162     sconf->ReadAheadBuffer = val;
1163     return NULL;
1164 }
1165
1166 /*
1167  * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
1168  */
1169 static const char *isapi_cmd_lognotsupported(cmd_parms *cmd, void *config, 
1170                                              char *arg)
1171 {
1172     isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
1173                                                &isapi_module);
1174
1175     if (strcasecmp(arg, "on") == 0) {
1176         sconf->LogNotSupported = -1;
1177     }
1178     else if (strcasecmp(arg, "off") == 0) {
1179         sconf->LogNotSupported = 0;
1180     }
1181     else {
1182         return "ISAPILogNotSupported must be on or off";
1183     }
1184     return NULL;
1185 }
1186
1187 static const char *isapi_cmd_appendlogtoerrors(cmd_parms *cmd, void *config, 
1188                                                char *arg)
1189 {
1190     isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
1191                                                    &isapi_module);
1192
1193     if (strcasecmp(arg, "on") == 0) {
1194         sconf->AppendLogToErrors = -1;
1195     }
1196     else if (strcasecmp(arg, "off") == 0) {
1197         sconf->AppendLogToErrors = 0;
1198     }
1199     else {
1200         return "ISAPIAppendLogToErrors must be on or off";
1201     }
1202     return NULL;
1203 }
1204
1205 static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, 
1206                                               char *arg)
1207 {
1208     isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
1209                                                    &isapi_module);
1210
1211     if (strcasecmp(arg, "on") == 0) {
1212         sconf->AppendLogToQuery = -1;
1213     }
1214     else if (strcasecmp(arg, "off") == 0) {
1215         sconf->AppendLogToQuery = 0;
1216     }
1217     else {
1218         return "ISAPIAppendLogToQuery must be on or off";
1219     }
1220     return NULL;
1221 }
1222
1223 static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, 
1224                                        const char *filename)
1225
1226 {
1227     isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config, 
1228                                                     &isapi_module);
1229     isapi_loaded *isa, **newisa;
1230     apr_finfo_t tmp;
1231     apr_status_t rv;
1232     char *fspec;
1233     
1234     fspec = ap_server_root_relative(cmd->pool, filename);
1235     if ((rv = apr_stat(&tmp, fspec, 
1236                  APR_FINFO_TYPE, cmd->temp_pool)) != APR_SUCCESS) { 
1237         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
1238             "ISAPI: unable to stat(%s), skipping", filename);
1239         return NULL;
1240     }
1241     if (tmp.filetype != APR_REG) {
1242         ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
1243             "ISAPI: %s isn't a regular file, skipping", filename);
1244         return NULL;
1245     }
1246
1247     /* Load the extention as cached (passing sconf) */
1248     rv = isapi_load(cmd->pool, sconf, NULL, fspec, &isa); 
1249     if (rv != APR_SUCCESS) {
1250         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
1251                      "ISAPI: unable to cache %s, skipping", filename);
1252         return NULL;
1253     }
1254
1255     /* Add to cached list of loaded modules */
1256     newisa = apr_array_push(sconf->loaded);
1257     *newisa = isa;
1258     
1259     return NULL;
1260 }
1261
1262 static void isapi_hooks(apr_pool_t *cont)
1263 {
1264     ap_hook_post_config(isapi_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1265     ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE);
1266 }
1267
1268 static const command_rec isapi_cmds[] = {
1269 AP_INIT_TAKE1("ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF,
1270   "Maximum bytes to initially pass to the ISAPI handler"),
1271 AP_INIT_TAKE1("ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF,
1272   "Log requests not supported by the ISAPI server"),
1273 AP_INIT_TAKE1("ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF,
1274   "Send all Append Log requests to the error log"),
1275 AP_INIT_TAKE1("ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF,
1276   "Append Log requests are concatinated to the query args"),
1277 AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL, RSRC_CONF,
1278   "Cache the specified ISAPI extension in-process"),
1279 { NULL }
1280 };
1281
1282 module AP_MODULE_DECLARE_DATA isapi_module = {
1283    STANDARD20_MODULE_STUFF,
1284    NULL,                        /* create per-dir config */
1285    NULL,                        /* merge per-dir config */
1286    create_isapi_server_config,  /* server config */
1287    NULL,                        /* merge server config */
1288    isapi_cmds,                  /* command apr_table_t */
1289    isapi_hooks                  /* register hooks */
1290 };