ftp.nice.ch/pub/next/unix/network/www/apache.1.3a1.NIHS.bs.tar.gz#/apache.1.3a1.NIHS.bs/original-source/src/nt/mod_isapi.c

This is mod_isapi.c in view mode; [Download] [Up]

/* ====================================================================
 * Copyright (c) 1995-1997 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

/*
 * mod_isapi.c - Internet Server Application (ISA) module for Apache
 * by Alexei Kosut <akosut@apache.org>
 *
 * This module implements Microsoft's ISAPI, allowing Apache (when running
 * under Windows) to load Internet Server Applications (ISAPI extensions).
 * It implements all of the ISAPI 2.0 specification, except for the 
 * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
 * extensions that use only synchronous I/O and are compatible with the
 * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
 * function as well).
 *
 * To load, simply place the ISA in a location in the document tree.
 * Then add an "AddHandler isapi-isa dll" into your config file.
 * You should now be able to load ISAPI DLLs just be reffering to their
 * URLs. Make sure the ExecCGI option is active in the directory
 * the ISA is in.
 */

#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"

/* We use the exact same header file as the original */
#include <HttpExt.h>

module isapi_module;

/* Our "Connection ID" structure */

typedef struct {
    LPEXTENSION_CONTROL_BLOCK ecb;
    request_rec *r;
    int status;
} isapi_cid;

/* Declare the ISAPI functions */

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);

int isapi_handler (request_rec *r) {
    LPEXTENSION_CONTROL_BLOCK ecb =
	pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
    HSE_VERSION_INFO *pVer = pcalloc(r->pool, sizeof(HSE_VERSION_INFO));

    HINSTANCE isapi_handle;
    BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
    DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
    BOOL (*isapi_term)(DWORD); /* optional entry point 3 */

    isapi_cid *cid = pcalloc(r->pool, sizeof(isapi_cid));
    table *e = r->subprocess_env;
    int retval;

    /* Use similar restrictions as CGIs */

    if (!(allow_options(r) & OPT_EXECCGI))
	return FORBIDDEN;

    if (S_ISDIR(r->finfo.st_mode))
	return FORBIDDEN;

    if (r->finfo.st_mode == 0)
	return NOT_FOUND;

    /* Load the module */

    if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
				       LOAD_WITH_ALTERED_SEARCH_PATH))) {
	log_reason("Could not load DLL", r->filename, r);
	return SERVER_ERROR;
    }

    if (!(isapi_version =
	  (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
	log_reason("DLL could not load GetExtensionVersion()", r->filename, r);
	FreeLibrary(isapi_handle);
	return SERVER_ERROR;
    }

    if (!(isapi_entry =
	  (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
	log_reason("DLL could not load HttpExtensionProc()", r->filename, r);
	FreeLibrary(isapi_handle);
	return SERVER_ERROR;
    }

    isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));

    /* Run GetExtensionVersion() */

    if ((*isapi_version)(pVer) != TRUE) {
	log_reason("ISAPI GetExtensionVersion() failed", r->filename, r);
	FreeLibrary(isapi_handle);
	return SERVER_ERROR;
    }

    /* Set up variables */
    add_common_vars(r);
    add_cgi_vars(r);

    /* Set up connection ID */
    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 = r->method;
    ecb->lpszQueryString = table_get(e, "QUERY_STRING");
    ecb->lpszPathInfo = table_get(e, "PATH_INFO");
    ecb->lpszPathTranslated = table_get(e, "PATH_TRANSLATED");
    ecb->lpszContentType = table_get(e, "CONTENT_TYPE");

    /* Set up client input */
    if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
	if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
	FreeLibrary(isapi_handle);
	return retval;
    }

    if (should_client_block(r)) {
	/* Unlike IIS, which limits this to 48k, we read the whole
	 * sucker in. I suppose this could be bad for memory if someone
	 * uploaded the complete works of Shakespeare. Well, WebSite
	 * does the same thing.
	 */
	long to_read = atol(table_get(e, "CONTENT_LENGTH"));
	long read;

	/* Actually, let's cap it at 48k, until we figure out what
	 * to do with this... we don't want a Content-Length: 1000000000
	 * taking out the machine.
	 */

	if (to_read > 49152) {
	    if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
	    FreeLibrary(isapi_handle);
	    return HTTP_REQUEST_ENTITY_TOO_LARGE;
	}

	ecb->lpbData = pcalloc(r->pool, 1 + to_read);

	if ((read = get_client_block(r, ecb->lpbData, to_read)) < 0) {
	    if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
	    FreeLibrary(isapi_handle);
	    return SERVER_ERROR;
	}

	/* Although its not to spec, IIS seems to null-terminate
	 * its lpdData string. So we will too. To make sure
	 * cbAvailable matches cbTotalBytes, we'll up the latter
	 * and equalize them.
	 */
	ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
	ecb->lpbData[read] = '\0';
	}
    else {
	ecb->cbTotalBytes = 0;
	ecb->cbAvailable = 0;
	ecb->lpbData = NULL;
    }

    /* Set up the callbacks */

    ecb->GetServerVariable = &GetServerVariable;
    ecb->WriteClient = &WriteClient;
    ecb->ReadClient = &ReadClient;
    ecb->ServerSupportFunction = &ServerSupportFunction;

    /* All right... try and load the sucker */
    retval = (*isapi_entry)(ecb);

    /* Set the status (for logging) */
    if (ecb->dwHttpStatusCode)
	r->status = ecb->dwHttpStatusCode;

    /* Check for a log message - and log it */
    if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
	log_reason(ecb->lpszLogData, r->filename, r);

    /* All done with the DLL... get rid of it */
    if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
    FreeLibrary(isapi_handle);

    switch(retval) {
    case HSE_STATUS_SUCCESS:
    case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
	/* Ignore the keepalive stuff; Apache handles it just fine without
	 * the ISA's "advice".
	 */

	if (cid->status) /* We have a special status to return */
	    return cid->status;

	return OK;
    case HSE_STATUS_PENDING:	/* We don't support this */
	log_reason("ISAPI asynchronous I/O not supported", r->filename, r);
    case HSE_STATUS_ERROR:
    default:
	return SERVER_ERROR;
    }

}

BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
			       LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
    request_rec *r = ((isapi_cid *)hConn)->r;
    table *e = r->subprocess_env;
    char *result;
    
    /* Mostly, we just grab it from the environment, but there are
     * a couple of special cases
     */

    if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
	/* We don't support NT users, so this is always the same as
	 * REMOTE_USER
	 */
	result = table_get(e, "REMOTE_USER");
    }
    else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
	/* Apache doesn't support secure requests inherently, so
	 * we have no way of knowing. We'll be conservative, and say
	 * all requests are insecure.
	 */
	result = "0";
    }
    else if (!strcasecmp(lpszVariableName, "URL")) {
	result = r->uri;
    }
    else {
	result = table_get(e, lpszVariableName);
    }

    if (result) {
	if (strlen(result) > *lpdwSizeofBuffer) {
	    *lpdwSizeofBuffer = strlen(result);
	    SetLastError(ERROR_INSUFFICIENT_BUFFER);
	    return FALSE;
	}
	strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
	return TRUE;
    }

    /* Didn't find it */
    SetLastError(ERROR_INVALID_INDEX);
    return FALSE;
}

BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
			 DWORD dwReserved) {
    request_rec *r = ((isapi_cid *)ConnID)->r;
    int writ;	/* written, actually, but why shouldn't I make up words? */

    /* We only support synchronous writing */
    if (dwReserved && dwReserved != HSE_IO_SYNC) {
	log_reason("ISAPI asynchronous I/O not supported", r->filename, r);
	SetLastError(ERROR_INVALID_PARAMETER);
	return FALSE;
    }

    if ((writ = rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
	SetLastError(ERROR); /* XXX: Find the right error code */
	return FALSE;
    }
   
    *lpwdwBytes = writ;
    return TRUE;
}

BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
    /* Doesn't need to do anything; we've read all the data already */
    return TRUE;
}

BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
				   LPVOID lpvBuffer, LPDWORD lpdwSize,
				   LPDWORD lpdwDataType) {
    isapi_cid *cid = (isapi_cid *)hConn;
    request_rec *subreq, *r = cid->r;
    char *data;

    switch (dwHSERequest) {
    case HSE_REQ_SEND_URL_REDIRECT_RESP:
	/* Set the status to be returned when the HttpExtensionProc()
	 * is done.
	 */
	table_set (r->headers_out, "Location", lpvBuffer);
	cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT;
	return TRUE;

    case HSE_REQ_SEND_URL:
	/* Read any additional input */

	if (r->remaining > 0) {
	    char argsbuffer[HUGE_STRING_LEN];

	    while (get_client_block(r, argsbuffer, HUGE_STRING_LEN));
	}
	
	/* Reset the method to GET */
	r->method = pstrdup(r->pool, "GET");
	r->method_number = M_GET;

	/* Don't let anyone think there's still data */
	table_unset(r->headers_in, "Content-Length");
	
	internal_redirect((char *)lpvBuffer, r);	
	return TRUE;

    case HSE_REQ_SEND_RESPONSE_HEADER:
	r->status_line = lpvBuffer ? lpvBuffer : pstrdup(r->pool, "200 OK");
	sscanf(r->status_line, "%d", &r->status);
	cid->ecb->dwHttpStatusCode = r->status;

	/* Now fill in the HTTP headers, and the rest of it. Ick.
	 * lpdwDataType contains a string that has headers (in MIME
	 * format), a blank like, then (possibly) data. We need
	 * to parse it.
	 *
	 * Easy case first:
	 */
	if (!lpdwDataType) {
	    send_http_header(r);
	    return TRUE;
	}

	/* Make a copy - don't disturb the original */
	data = pstrdup(r->pool, (char *)lpdwDataType);

	/* We *should* break before this while loop ends */
	while (*data) {
	    char *value, *lf = strchr(data, '\n');
	    int p;

	    if (!lf) { /* Huh? Invalid data, I think */
		log_reason("ISA sent invalid headers", r->filename, r);
		SetLastError(ERROR);	/* XXX: Find right error */
		return FALSE;
	    }

	    /* Get rid of \n and \r */
	    *lf = '\0';
	    p = strlen(data);
	    if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
	    
	    /* End of headers */
	    if (*data == '\0') {
		data = lf + 1;	/* Reset data */
		break;
	    }

	    if (!(value = strchr(data, ':'))) {
		SetLastError(ERROR);	/* XXX: Find right error */
		log_reason("ISA sent invalid headers", r->filename, r);
		return FALSE;
	    }

	    *value++ = '\0';
	    while (*value && isspace(*value)) ++value;

	    /* Check all the special-case headers. Similar to what
	     * scan_script_header() does (see that function for
	     * more detail)
	     */

	    if (!strcasecmp(data, "Content-Type")) {
		/* Nuke trailing whitespace */
		
		char *endp = value + strlen(value) - 1;
		while (endp > value && isspace(*endp)) *endp-- = '\0';
            
		r->content_type = pstrdup (r->pool, value);
	    }
	    else if (!strcasecmp(data, "Content-Length")) {
		table_set(r->headers_out, data, value);
	    }
	    else if (!strcasecmp(data, "Transfer-Encoding")) {
		table_set(r->headers_out, data, value);
	    }
	    else if (!strcasecmp(data, "Set-Cookie")) {
		table_add(r->err_headers_out, data, value);
	    }
	    else {
		table_merge(r->err_headers_out, data, value);
	    }
	  
	    /* Reset data */
	    data = lf + 1;
	}
	
	/* All the headers should be set now */

	send_http_header(r);

	/* Any data left should now be sent directly */
	rputs(data, r);

	return TRUE;

    case HSE_REQ_MAP_URL_TO_PATH:
	/* Map a URL to a filename */
	subreq = sub_req_lookup_uri(pstrndup(r->pool, (char *)lpvBuffer,
					      *lpdwSize), r);

	GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);

	/* IIS puts a trailing slash on directories, Apache doesn't */

	if (S_ISDIR (subreq->finfo.st_mode)) {
		int l = strlen((char *)lpvBuffer);

		((char *)lpvBuffer)[l] = '\\';
		((char *)lpvBuffer)[l + 1] = '\0';
	}
	
	return TRUE;

    case HSE_REQ_DONE_WITH_SESSION:
	/* Do nothing... since we don't support async I/O, they'll
	 * return from HttpExtensionProc soon
	 */
	return TRUE;

    /* We don't support all this async I/O, Microsoft-specific stuff */
    case HSE_REQ_IO_COMPLETION:
    case HSE_REQ_TRANSMIT_FILE:
	log_reason("ISAPI asynchronous I/O not supported", r->filename, r);
    default:
	SetLastError(ERROR_INVALID_PARAMETER);
	return FALSE;
    }
}

handler_rec isapi_handlers[] = {
{ "isapi-isa", isapi_handler },
{ NULL}
};

module isapi_module = {
   STANDARD_MODULE_STUFF,
   NULL,			/* initializer */
   NULL,			/* create per-dir config */
   NULL,			/* merge per-dir config */
   NULL,			/* server config */
   NULL,			/* merge server config */
   NULL,			/* command table */
   isapi_handlers,	       	/* handlers */
   NULL,			/* filename translation */
   NULL,			/* check_user_id */
   NULL,			/* check auth */
   NULL,			/* check access */
   NULL,			/* type_checker */
   NULL,			/* logger */
   NULL				/* header parser */
};

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.