#define CORE_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_log.h"
#include "util_script.h"
#include <stdlib.h>
#include <HttpExt.h>
#define RELAX_HEADER_RULE
#if !defined(HSE_REQ_SEND_RESPONSE_HEADER_EX) \
|| !defined(HSE_REQ_MAP_URL_TO_PATH_EX)
#pragma message("WARNING: This build of Apache is missing the recent changes")
#pragma message("in the Microsoft Win32 Platform SDK; some mod_isapi features")
#pragma message("will be disabled. To obtain the latest Platform SDK files,")
#pragma message("please refer to:")
#pragma message("http://msdn.microsoft.com/downloads/sdks/platform/platform.asp")
#endif
module isapi_module;
static DWORD ReadAheadBuffer = 49152;
static int LogNotSupported = -1;
static int AppendLogToErrors = 0;
static int AppendLogToQuery = 0;
typedef struct {
LPEXTENSION_CONTROL_BLOCK ecb;
request_rec *r;
int status;
} isapi_cid;
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
DWORD dwReserved);
BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
LPVOID lpvBuffer, LPDWORD lpdwSize,
LPDWORD lpdwDataType);
#pragma optimize("y",off)
int isapi_handler (request_rec *r) {
LPEXTENSION_CONTROL_BLOCK ecb =
ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
HINSTANCE isapi_handle;
BOOL (*isapi_version)(HSE_VERSION_INFO *);
DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK);
BOOL (*isapi_term)(DWORD);
isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
table *e = r->subprocess_env;
DWORD read;
char *p;
int retval;
int res;
if (!(ap_allow_options(r) & OPT_EXECCGI))
return FORBIDDEN;
if (r->finfo.st_mode == 0)
return NOT_FOUND;
if (S_ISDIR(r->finfo.st_mode))
return FORBIDDEN;
if (!(isapi_handle = ap_os_dso_load(r->filename))) {
ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
"ISAPI Could not load DLL: %s", r->filename);
return SERVER_ERROR;
}
if (!(isapi_version =
(void *)(ap_os_dso_sym(isapi_handle, "GetExtensionVersion")))) {
ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
"DLL could not load GetExtensionVersion(): %s",
r->filename);
ap_os_dso_unload(isapi_handle);
return SERVER_ERROR;
}
if (!(isapi_entry =
(void *)(ap_os_dso_sym(isapi_handle, "HttpExtensionProc")))) {
ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
"DLL could not load HttpExtensionProc(): %s",
r->filename);
ap_os_dso_unload(isapi_handle);
return SERVER_ERROR;
}
isapi_term = (void *)(ap_os_dso_sym(isapi_handle, "TerminateExtension"));
if (!(*isapi_version)(pVer)) {
ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
"ISAPI GetExtensionVersion() failed: %s", r->filename);
ap_os_dso_unload(isapi_handle);
return SERVER_ERROR;
}
ap_add_common_vars(r);
ap_add_cgi_vars(r);
ap_table_setn(r->subprocess_env, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
ap_table_setn(r->subprocess_env, "SERVER_PORT_SECURE", "0");
ap_table_setn(r->subprocess_env, "URL", r->uri);
ecb->ConnID = (HCONN)cid;
cid->ecb = ecb;
cid->r = r;
cid->status = 0;
ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
ecb->dwVersion = MAKELONG(0, 2);
ecb->dwHttpStatusCode = 0;
strcpy(ecb->lpszLogData, "");
ecb->lpszMethod = ap_pstrdup(r->pool, r->method);
ecb->lpszQueryString = ap_pstrdup(r->pool, ap_table_get(e, "QUERY_STRING"));
ecb->lpszPathInfo = ap_pstrdup(r->pool, ap_table_get(e, "PATH_INFO"));
ecb->lpszPathTranslated = ap_pstrdup(r->pool, ap_table_get(e, "PATH_TRANSLATED"));
ecb->lpszContentType = ap_pstrdup(r->pool, ap_table_get(e, "CONTENT_TYPE"));
if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
if (isapi_term) (*isapi_term)( 2 );
ap_os_dso_unload(isapi_handle);
return retval;
}
if (ap_should_client_block(r)) {
if (r->remaining) {
ecb->cbTotalBytes = r->remaining;
if (ecb->cbTotalBytes > ReadAheadBuffer)
ecb->cbAvailable = ReadAheadBuffer;
else
ecb->cbAvailable = ecb->cbTotalBytes;
}
else
{
ecb->cbTotalBytes = 0xffffffff;
ecb->cbAvailable = ReadAheadBuffer;
}
ecb->lpbData = ap_pcalloc(r->pool, ecb->cbAvailable + 1);
p = ecb->lpbData;
read = 0;
while (read < ecb->cbAvailable &&
((res = ap_get_client_block(r, ecb->lpbData + read,
ecb->cbAvailable - read)) > 0)) {
read += res;
}
if (res < 0) {
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
ap_os_dso_unload(isapi_handle);
return SERVER_ERROR;
}
if (res == 0)
ecb->cbAvailable = ecb->cbTotalBytes = read;
else
ecb->cbAvailable = read;
ecb->lpbData[read] = '\0';
}
else {
ecb->cbTotalBytes = 0;
ecb->cbAvailable = 0;
ecb->lpbData = NULL;
}
ecb->GetServerVariable = &GetServerVariable;
ecb->WriteClient = &WriteClient;
ecb->ReadClient = &ReadClient;
ecb->ServerSupportFunction = &ServerSupportFunction;
retval = (*isapi_entry)(ecb);
if (ecb->dwHttpStatusCode)
r->status = ecb->dwHttpStatusCode;
if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"ISAPI: %s: %s", ecb->lpszLogData, r->filename);
if (r->remaining > 0) {
char argsbuffer[HUGE_STRING_LEN];
while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0);
}
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
ap_os_dso_unload(isapi_handle);
switch(retval) {
case HSE_STATUS_SUCCESS:
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
if (cid->status)
return cid->status;
return OK;
case HSE_STATUS_PENDING:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI asynchronous I/O not supported: %s",
r->filename);
case HSE_STATUS_ERROR:
default:
return SERVER_ERROR;
}
}
#pragma optimize("",on)
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
request_rec *r = ((isapi_cid *)hConn)->r;
const char *result;
DWORD len;
if (!strcmp(lpszVariableName, "ALL_HTTP")) {
char **env = (char**) ap_table_elts(r->subprocess_env)->elts;
int nelts = 2 * ap_table_elts(r->subprocess_env)->nelts;
int i;
for (len = 0, i = 0; i < nelts; i += 2)
if (!strncmp(env[i], "HTTP_", 5))
len += strlen(env[i]) + strlen(env[i + 1]) + 2;
if (*lpdwSizeofBuffer < len + 1) {
*lpdwSizeofBuffer = len + 1;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
for (i = 0; i < nelts; i += 2)
if (!strncmp(env[i], "HTTP_", 5)) {
strcpy(lpvBuffer, env[i]);
((char*)lpvBuffer) += strlen(env[i]);
*(((char*)lpvBuffer)++) = ':';
strcpy(lpvBuffer, env[i + 1]);
((char*)lpvBuffer) += strlen(env[i + 1]);
*(((char*)lpvBuffer)++) = '\n';
}
*(((char*)lpvBuffer)++) = '\0';
*lpdwSizeofBuffer = len;
return TRUE;
}
if (!strcmp(lpszVariableName, "ALL_RAW")) {
char **raw = (char**) ap_table_elts(r->headers_in)->elts;
int nelts = 2 * ap_table_elts(r->headers_in)->nelts;
int i;
for (len = 0, i = 0; i < nelts; i += 2)
len += strlen(raw[i]) + strlen(raw[i + 1]) + 2;
if (*lpdwSizeofBuffer < len + 1) {
*lpdwSizeofBuffer = len + 1;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
for (i = 0; i < nelts; i += 2) {
strcpy(lpvBuffer, raw[i]);
((char*)lpvBuffer) += strlen(raw[i]);
*(((char*)lpvBuffer)++) = ':';
*(((char*)lpvBuffer)++) = ' ';
strcpy(lpvBuffer, raw[i + 1]);
((char*)lpvBuffer) += strlen(raw[i + 1]);
*(((char*)lpvBuffer)++) = '\n';
i += 2;
}
*(((char*)lpvBuffer)++) = '\0';
*lpdwSizeofBuffer = len;
return TRUE;
}
result = ap_table_get(r->subprocess_env, lpszVariableName);
if (result) {
len = strlen(result);
if (*lpdwSizeofBuffer < len + 1) {
*lpdwSizeofBuffer = len + 1;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
strcpy(lpvBuffer, result);
*lpdwSizeofBuffer = len;
return TRUE;
}
SetLastError(ERROR_INVALID_INDEX);
return FALSE;
}
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
DWORD dwReserved) {
request_rec *r = ((isapi_cid *)ConnID)->r;
if (dwReserved && dwReserved != HSE_IO_SYNC) {
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI asynchronous I/O not supported: %s",
r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if ((*lpwdwBytes = ap_rwrite(Buffer, *lpwdwBytes, r)) <= 0) {
if (!GetLastError())
SetLastError(ERROR);
return FALSE;
}
return TRUE;
}
BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
request_rec *r = ((isapi_cid *)ConnID)->r;
DWORD read = 0;
int res;
if (r->remaining < (long) *lpdwSize)
*lpdwSize = r->remaining;
while (read < *lpdwSize &&
((res = ap_get_client_block(r, (char*)lpvBuffer + read,
*lpdwSize - read)) > 0)) {
if (res < 0) {
*lpdwSize = 0;
if (!GetLastError())
SetLastError(ERROR);
return FALSE;
}
read += res;
}
*lpdwSize = read;
return TRUE;
}
static BOOL SendResponseHeaderEx(isapi_cid *cid, const char *stat,
const char *head, DWORD statlen,
DWORD headlen)
{
int termarg;
char *termch;
if (!stat || statlen == 0 || !*stat) {
stat = "Status: 200 OK";
}
else {
char *newstat;
newstat = ap_palloc(cid->r->pool, statlen + 9);
strcpy(newstat, "Status: ");
ap_cpystrn(newstat + 8, stat, statlen + 1);
stat = newstat;
}
if (!head || headlen == 0 || !*head) {
head = "\r\n";
}
else
{
if (head[headlen]) {
head = ap_pstrndup(cid->r->pool, head, headlen);
}
}
cid->status = ap_scan_script_header_err_strs(cid->r, NULL, &termch,
&termarg, stat, head, NULL);
cid->ecb->dwHttpStatusCode = cid->r->status;
ap_send_http_header(cid->r);
if (termch && (termarg == 1)) {
if (headlen == -1 && *termch)
ap_rputs(termch, cid->r);
else if (headlen > (size_t) (termch - head))
ap_rwrite(termch, headlen - (termch - head), cid->r);
}
if (cid->status == HTTP_INTERNAL_SERVER_ERROR)
return FALSE;
return TRUE;
}
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
LPVOID lpvBuffer, LPDWORD lpdwSize,
LPDWORD lpdwDataType) {
isapi_cid *cid = (isapi_cid *)hConn;
request_rec *r = cid->r;
request_rec *subreq;
switch (dwHSERequest) {
case 1:
ap_table_set(r->headers_out, "Location", lpvBuffer);
cid->status = cid->r->status
= cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY;
return TRUE;
case 2:
if (r->remaining > 0) {
char argsbuffer[HUGE_STRING_LEN];
while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0);
}
r->method = ap_pstrdup(r->pool, "GET");
r->method_number = M_GET;
ap_table_unset(r->headers_in, "Content-Length");
(char*)lpvBuffer = ap_pstrdup(r->pool, (char*)lpvBuffer);
ap_internal_redirect((char*)lpvBuffer, r);
return TRUE;
case 3:
{
DWORD statlen = 0, headlen = 0;
if (lpvBuffer)
statlen = strlen((char*) lpvBuffer);
if (lpdwDataType)
headlen = strlen((char*) lpdwDataType);
return SendResponseHeaderEx(cid, (char*) lpvBuffer, (char*) lpdwDataType,
statlen, headlen);
}
case 4:
return TRUE;
case 1001:
{
char *file = (char *)lpvBuffer;
DWORD len;
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, file, *lpdwSize), r);
len = ap_cpystrn(file, subreq->filename, *lpdwSize) - file;
if (S_ISDIR (subreq->finfo.st_mode)) {
if (len < *lpdwSize - 1) {
file[len++] = '\\';
file[len] = '\0';
}
}
*lpdwSize = len;
return TRUE;
}
case 1002:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1003:
ap_table_set(r->notes, "isapi-parameter", (char*) lpvBuffer);
if (AppendLogToQuery) {
if (r->args)
r->args = ap_pstrcat(r->pool, r->args, (char*) lpvBuffer, NULL);
else
r->args = ap_pstrdup(r->pool, (char*) lpvBuffer);
}
if (AppendLogToErrors)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
"ISAPI %s: %s", cid->r->filename,
(char*) lpvBuffer);
return TRUE;
case 1005:
case 1006:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI asynchronous I/O not supported: %s",
r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1007:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction "
"HSE_REQ_REFRESH_ISAPI_ACL "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1008:
*((LPBOOL) lpvBuffer) = (r->connection->keepalive == 1);
return TRUE;
case 1010:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI asynchronous I/O not supported: %s",
r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1011:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction "
"HSE_REQ_GET_IMPERSONATION_TOKEN "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
#ifdef HSE_REQ_MAP_URL_TO_PATH_EX
case 1012:
{
LPHSE_URL_MAPEX_INFO info = (LPHSE_URL_MAPEX_INFO) lpdwDataType;
char* test_uri = ap_pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize);
subreq = ap_sub_req_lookup_uri(test_uri, r);
info->cchMatchingURL = strlen(test_uri);
info->cchMatchingPath = ap_cpystrn(info->lpszPath, subreq->filename,
MAX_PATH) - info->lpszPath;
if (subreq->path_info && *subreq->path_info) {
ap_cpystrn(info->lpszPath + info->cchMatchingPath,
subreq->path_info, MAX_PATH - info->cchMatchingPath);
info->cchMatchingURL -= strlen(subreq->path_info);
if (S_ISDIR(subreq->finfo.st_mode)
&& info->cchMatchingPath < MAX_PATH - 1) {
++info->cchMatchingPath;
++info->cchMatchingURL;
}
}
else if (S_ISDIR(subreq->finfo.st_mode)
&& info->cchMatchingPath < MAX_PATH - 1) {
info->lpszPath[info->cchMatchingPath++] = '/';
info->lpszPath[info->cchMatchingPath] = '\0';
}
if (!subreq->finfo.st_mode) {
while (info->cchMatchingPath && info->cchMatchingURL) {
if (info->lpszPath[info->cchMatchingPath - 1] == '/')
break;
--info->cchMatchingPath;
--info->cchMatchingURL;
}
}
for (test_uri = info->lpszPath; *test_uri; ++test_uri)
if (*test_uri == '/')
*test_uri = '\\';
info->dwFlags = (subreq->finfo.st_mode & _S_IREAD ? 0x001 : 0)
| (subreq->finfo.st_mode & _S_IWRITE ? 0x002 : 0)
| (subreq->finfo.st_mode & _S_IEXEC ? 0x204 : 0);
return TRUE;
}
#endif
case 1014:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
" is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1015:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction "
"HSE_REQ_GET_CERT_INFO_EX "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
#ifdef HSE_REQ_SEND_RESPONSE_HEADER_EX
case 1016:
{
LPHSE_SEND_HEADER_EX_INFO shi
= (LPHSE_SEND_HEADER_EX_INFO) lpvBuffer;
return SendResponseHeaderEx(cid, shi->pszStatus, shi->pszHeader,
shi->cchStatus, shi->cchHeader);
}
#endif
case 1017:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction "
"HSE_REQ_CLOSE_CONNECTION "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case 1018:
*((LPBOOL) lpvBuffer) = (r->connection->aborted == 0);
return TRUE;
case 1020:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction "
"HSE_REQ_EXTENSION_TRIGGER "
"is not supported: %s", r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
default:
if (LogNotSupported)
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"ISAPI ServerSupportFunction (%d) not supported: "
"%s", dwHSERequest, r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
static const char *isapi_cmd_readaheadbuffer(cmd_parms *cmd, void *config,
char *arg)
{
long val;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (((val = strtol(arg, (char **) &err, 10)) <= 0) || *err)
return "ISAPIReadAheadBuffer must be a legitimate value.";
ReadAheadBuffer = val;
return NULL;
}
static const char *isapi_cmd_lognotsupported(cmd_parms *cmd, void *config,
char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (strcasecmp(arg, "on") == 0) {
LogNotSupported = -1;
}
else if (strcasecmp(arg, "off") == 0) {
LogNotSupported = 0;
}
else {
return "ISAPILogNotSupported must be on or off";
}
return NULL;
}
static const char *isapi_cmd_appendlogtoerrors(cmd_parms *cmd, void *config,
char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (strcasecmp(arg, "on") == 0) {
AppendLogToErrors = -1;
}
else if (strcasecmp(arg, "off") == 0) {
AppendLogToErrors = 0;
}
else {
return "ISAPIAppendLogToErrors must be on or off";
}
return NULL;
}
static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config,
char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (strcasecmp(arg, "on") == 0) {
AppendLogToQuery = -1;
}
else if (strcasecmp(arg, "off") == 0) {
AppendLogToQuery = 0;
}
else {
return "ISAPIAppendLogToQuery must be on or off";
}
return NULL;
}
static const command_rec isapi_cmds[] = {
{ "ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF, TAKE1,
"Maximum bytes to initially pass to the ISAPI handler" },
{ "ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF, TAKE1,
"Log requests not supported by the ISAPI server" },
{ "ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF, TAKE1,
"Send all Append Log requests to the error log" },
{ "ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF, TAKE1,
"Append Log requests are concatinated to the query args" },
{ NULL }
};
handler_rec isapi_handlers[] = {
{ "isapi-isa", isapi_handler },
{ NULL}
};
module isapi_module = {
STANDARD_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
isapi_cmds,
isapi_handlers,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};