1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
47 * ====================================================================
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/>.
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.
60 * mod_isapi.c - Internet Server Application (ISA) module for Apache
61 * by Alexei Kosut <akosut@apache.org>
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
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
78 #include "ap_config.h"
80 #include "http_config.h"
81 #include "http_core.h"
82 #include "http_protocol.h"
83 #include "http_request.h"
85 #include "util_script.h"
86 #include "apr_portable.h"
87 #include "apr_strings.h"
90 /* We use the exact same header file as the original */
93 /* TODO: Unknown errors that must be researched for correct codes */
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
103 /* Declare the ISAPI functions */
105 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
106 LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
107 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
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);
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.
120 The revisions below may eliminate this artifact.
122 #pragma optimize("y",off)
124 /* Our loaded isapi module description structure */
128 HSE_VERSION_INFO *pVer;
129 PFN_GETEXTENSIONVERSION GetExtensionVersion;
130 PFN_HTTPEXTENSIONPROC HttpExtensionProc;
131 PFN_TERMINATEEXTENSION TerminateExtension;
138 /* Our "Connection ID" structure */
141 LPEXTENSION_CONTROL_BLOCK ecb;
144 PFN_HSE_IO_COMPLETION completion;
145 PVOID completion_arg;
150 apr_status_t isapi_handler (request_rec *r)
152 apr_table_t *e = r->subprocess_env;
156 /* Use similar restrictions as CGIs
158 * If this fails, it's pointless to load the isapi dll.
160 if (!(ap_allow_options(r) & OPT_EXECCGI))
161 return HTTP_FORBIDDEN;
163 if (r->finfo.protection == 0)
164 return HTTP_NOT_FOUND;
166 if (r->finfo.filetype == APR_DIR)
167 return HTTP_FORBIDDEN;
171 * TODO: Critical section
173 * Warning: cid should not be allocated from pool if we
174 * cache the isapi process in-memory.
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()
180 isa = apr_pcalloc(r->pool, sizeof(isapi_module));
181 isa->pVer = apr_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
184 /* TODO: These may need to become overrideable, so that we
185 * assure a given isapi can be fooled into behaving well.
187 isa->timeout = INFINITE; /* microsecs */
188 isa->fakeasync = TRUE;
189 isa->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
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;
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()",
203 FreeLibrary(isa->handle);
204 return HTTP_INTERNAL_SERVER_ERROR;
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()",
212 FreeLibrary(isa->handle);
213 return HTTP_INTERNAL_SERVER_ERROR;
216 /* TerminateExtension() is an optional interface */
218 isa->TerminateExtension = (void *)(GetProcAddress(isa->handle, "TerminateExtension"));
220 /* Run GetExtensionVersion() */
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",
227 FreeLibrary(isa->handle);
228 return HTTP_INTERNAL_SERVER_ERROR;
231 /* Load of this module completed, this is the point at which *isa
232 * could be cached for later invocation.
234 * on to invoking this request...
237 /* Set up variables */
238 ap_add_common_vars(r);
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 */
250 cid->complete = NULL;
251 cid->completion = NULL;
252 cid->retval = APR_SUCCESS;
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;
275 /* Set up client input */
276 cid->retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
278 if (isa->TerminateExtension) {
279 (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
281 FreeLibrary(isa->handle);
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.
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.
294 long to_read = atol(apr_table_get(e, "CONTENT_LENGTH"));
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.
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;
309 cid->ecb->lpbData = apr_pcalloc(r->pool, 1 + to_read);
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;
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
323 cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read + 1;
324 cid->ecb->lpbData[read] = '\0';
327 cid->ecb->cbTotalBytes = 0;
328 cid->ecb->cbAvailable = 0;
329 cid->ecb->lpbData = NULL;
332 /* All right... try and run the sucker */
333 cid->retval = (*isa->HttpExtensionProc)(cid->ecb);
335 /* Set the status (for logging) */
336 if (cid->ecb->dwHttpStatusCode) {
337 cid->r->status = cid->ecb->dwHttpStatusCode;
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);
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
358 case HSE_STATUS_PENDING:
359 /* emulating async behavior...
361 * Create a cid->completed event and wait on it for some timeout
362 * so that the app thinks is it running async.
364 * All async ServerSupportFunction calls will be handled through
365 * the registered IO_COMPLETION hook.
368 if (!isa->fakeasync) {
369 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
370 "ISAPI %s asynch I/O request refused",
372 cid->retval = APR_ENOTIMPL;
375 cid->complete = CreateEvent(NULL, FALSE, FALSE, NULL);
376 if (WaitForSingleObject(cid->complete, isa->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
385 case HSE_STATUS_ERROR:
386 /* end response if we have yet to do so.
388 cid->retval = HTTP_INTERNAL_SERVER_ERROR;
392 /* TODO: log unrecognized retval for debugging
394 cid->retval = HTTP_INTERNAL_SERVER_ERROR;
398 /* All done with the DLL... get rid of it...
400 * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
401 * and if it returns TRUE, unload, otherwise, cache it.
403 if (isa->TerminateExtension) {
404 (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
406 FreeLibrary(isa->handle);
407 /* TODO: Crit section */
414 #pragma optimize("",on)
416 BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
417 LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
419 request_rec *r = ((isapi_cid *)hConn)->r;
420 apr_table_t *e = r->subprocess_env;
423 /* Mostly, we just grab it from the environment, but there are
424 * a couple of special cases
427 if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
428 /* We don't support NT users, so this is always the same as
431 result = apr_table_get(e, "REMOTE_USER");
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.
440 else if (!strcasecmp(lpszVariableName, "URL")) {
444 result = apr_table_get(e, lpszVariableName);
448 if (strlen(result) > *lpdwSizeofBuffer) {
449 *lpdwSizeofBuffer = strlen(result);
450 SetLastError(ERROR_INSUFFICIENT_BUFFER);
453 strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
458 SetLastError(ERROR_INVALID_INDEX);
462 BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
465 request_rec *r = ((isapi_cid *)ConnID)->r;
466 int writ; /* written, actually, but why shouldn't I make up words? */
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);
476 if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
477 SetLastError(WSAEDISCON); /* TODO: Find the right error code */
485 BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
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 ...
493 static char* ComposeHeaders(request_rec *r, char* data)
495 /* We *should* break before this while loop ends */
498 char *value, *lf = strchr(data, '\n');
501 #ifdef RELAX_HEADER_RULE
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);
512 /* Get rid of \n and \r */
516 if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
520 #ifdef RELAX_HEADER_RULE
523 data = lf + 1; /* Reset data */
527 if (!(value = strchr(data, ':'))) {
528 SetLastError(TODO_ERROR);
529 /* ### euh... we're passing the wrong type of error
531 ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_INTERNAL_SERVER_ERROR, r,
532 "ISAPI %s sent invalid headers", r->filename);
537 while (*value && apr_isspace(*value)) ++value;
539 /* Check all the special-case headers. Similar to what
540 * ap_scan_script_header_err() does (see that function for
544 if (!strcasecmp(data, "Content-Type"))
546 /* Nuke trailing whitespace */
548 char *endp = value + strlen(value) - 1;
549 while (endp > value && apr_isspace(*endp))
552 tmp = apr_pstrdup (r->pool, value);
554 r->content_type = tmp;
556 else if (!strcasecmp(data, "Content-Length")) {
557 apr_table_set(r->headers_out, data, value);
559 else if (!strcasecmp(data, "Transfer-Encoding")) {
560 apr_table_set(r->headers_out, data, value);
562 else if (!strcasecmp(data, "Set-Cookie")) {
563 apr_table_add(r->err_headers_out, data, value);
566 apr_table_merge(r->err_headers_out, data, value);
570 #ifdef RELAX_HEADER_RULE
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)
587 isapi_cid *cid = (isapi_cid *)hConn;
588 request_rec *r = cid->r;
592 switch (dwHSERequest) {
593 case HSE_REQ_SEND_URL_REDIRECT_RESP:
594 /* Set the status to be returned when the HttpExtensionProc()
597 apr_table_set (r->headers_out, "Location", lpvBuffer);
598 cid->r->status = cid->ecb->dwHttpStatusCode
599 = HTTP_MOVED_TEMPORARILY;
602 case HSE_REQ_SEND_URL:
603 /* Read any additional input */
605 if (r->remaining > 0) {
606 char argsbuffer[HUGE_STRING_LEN];
608 while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
611 /* Reset the method to GET */
612 r->method = apr_pstrdup(r->pool, "GET");
613 r->method_number = M_GET;
615 /* Don't let anyone think there's still data */
616 apr_table_unset(r->headers_in, "Content-Length");
618 ap_internal_redirect((char *)lpvBuffer, r);
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;
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
634 ap_send_http_header(r);
638 /* Make a copy - don't disturb the original */
639 data = apr_pstrdup(r->pool, (char *)lpdwDataType);
641 /* Parse them out, or die trying */
642 data = ComposeHeaders(r, data);
646 /* All the headers should be set now */
647 ap_send_http_header(r);
649 /* Any data left should now be sent directly */
655 case HSE_REQ_DONE_WITH_SESSION:
656 /* Signal to resume the thread completing this request
659 SetEvent(cid->complete);
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,
667 GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
669 /* IIS puts a trailing slash on directories, Apache doesn't */
671 if (subreq->finfo.filetype == APR_DIR) {
672 int l = strlen((char *)lpvBuffer);
674 ((char *)lpvBuffer)[l] = '\\';
675 ((char *)lpvBuffer)[l + 1] = '\0';
680 case HSE_REQ_GET_SSPI_INFO:
681 SetLastError(ERROR_INVALID_PARAMETER);
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...
688 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
689 "ISAPI %s: %s", cid->r->filename,
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.
701 if (!cid->isa->fakeasync)
703 cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
704 cid->completion_arg = (PVOID) lpdwDataType;
707 case HSE_REQ_TRANSMIT_FILE:
708 /* Use TransmitFile... nothing wrong with that :)
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",
718 case HSE_REQ_REFRESH_ISAPI_ACL:
719 SetLastError(ERROR_INVALID_PARAMETER);
722 case HSE_REQ_IS_KEEP_CONN:
723 SetLastError(ERROR_INVALID_PARAMETER);
726 case HSE_REQ_ASYNC_READ_CLIENT:
727 SetLastError(ERROR_INVALID_PARAMETER);
730 case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
731 SetLastError(ERROR_INVALID_PARAMETER);
734 case HSE_REQ_MAP_URL_TO_PATH_EX:
735 SetLastError(ERROR_INVALID_PARAMETER);
738 /* TODO: Not quite ready for prime time yet */
740 /* Map a URL to a filename */
741 subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, (char *)lpvBuffer,
744 GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
746 /* IIS puts a trailing slash on directories, Apache doesn't */
748 if (subreq->finfo.filetype == APR_DIR) {
749 int l = strlen((char *)lpvBuffer);
751 ((char *)lpvBuffer)[l] = '\\';
752 ((char *)lpvBuffer)[l + 1] = '\0';
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.
771 /* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingPath
772 * (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingURL
777 case HSE_REQ_ABORTIVE_CLOSE:
778 SetLastError(ERROR_INVALID_PARAMETER);
781 case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
782 SetLastError(ERROR_INVALID_PARAMETER);
785 case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
786 SetLastError(ERROR_INVALID_PARAMETER);
789 /* TODO: Not quite ready for prime time */
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);
798 r->status_line = apr_pstrdup(r->pool, "200 OK");
800 sscanf(r->status_line, "%d", &r->status);
801 cid->ecb->dwHttpStatusCode = r->status;
803 if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader
804 && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader)
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);
811 /* Parse them out, or die trying */
812 data = ComposeHeaders(r, data);
821 /* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn;
823 * Now how are we about to start listening to an ISAPI's
824 * idea of keeping or closing a connection? Seriously :)
827 /* All the headers should be set now */
828 ap_send_http_header(r);
830 /* Any data left should now be sent directly */
836 /* HSE_REQ_CLOSE_CONNACTION and HSE_REQ_IS_CONNECTED are not defined on
838 case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
839 SetLastError(ERROR_INVALID_PARAMETER);
842 case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
843 /* Returns True if client is connected c.f. Q188346*/
847 /* case HSE_REQ_EXTENSION_TRIGGER:
848 * Added after ISAPI 4.0?
849 * Undocumented - from the Microsoft Jan '00 Platform SDK
852 /* TODO: log unrecognized ServerSupportCommand for debugging
854 SetLastError(ERROR_INVALID_PARAMETER);
859 handler_rec isapi_handlers[] = {
860 { "isapi-isa", isapi_handler },
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 */