Mailing List Archive

cvs commit: apache-2.0/src/include util_script.h
wrowe 00/10/02 08:39:02

Modified: src/os/win32 mod_isapi.c
src/main util_script.c
src/include util_script.h
Log:
Forward port 1.3 overhaul of isapi to 2.0.

Yes - it's blatently obvious that this should be 'bucketized', but the
sequence I will follow is:

*) compatibility of sources
*) extension to async + transmitfile emulated support
*) localization of ISAPI* directives to Directory, File blocks
*) addition of ISAPI caching
*) recode for buckets.

I'm actually looking forward to the mod_cgi bucketization as a model.

Revision Changes Path
1.20 +467 -362 apache-2.0/src/os/win32/mod_isapi.c

Index: mod_isapi.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/os/win32/mod_isapi.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- mod_isapi.c 2000/09/11 19:02:40 1.19
+++ mod_isapi.c 2000/10/02 15:38:55 1.20
@@ -100,6 +100,11 @@

module isapi_module;

+static DWORD ReadAheadBuffer = 49152;
+static int LogNotSupported = -1;
+static int AppendLogToErrors = 0;
+static int AppendLogToQuery = 0;
+
/* Declare the ISAPI functions */

BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
@@ -152,6 +157,10 @@
apr_table_t *e = r->subprocess_env;
isapi_loaded *isa;
isapi_cid *cid;
+ DWORD read;
+ char *fspec;
+ char *p;
+ int res;

/* Use similar restrictions as CGIs
*
@@ -160,17 +169,32 @@
if (!(ap_allow_options(r) & OPT_EXECCGI))
return HTTP_FORBIDDEN;

- if (r->finfo.protection == 0)
+ if (r->finfo.filetype == APR_NOFILE)
return HTTP_NOT_FOUND;

- if (r->finfo.filetype == APR_DIR)
+ if (r->finfo.filetype != APR_FILE)
return HTTP_FORBIDDEN;

+ /* Load the module...
+ * per PR2555, the LoadLibraryEx function is very picky about slashes.
+ * Debugging on NT 4 SP 6a reveals First Chance Exception within NTDLL.
+ * LoadLibrary in the MS PSDK also reveals that it -explicitly- states
+ * that backslashes must be used.
+ *
+ * Transpose '\' for '/' in the filename.
+ */
+ p = fspec = ap_pstrdup(r->pool, r->filename);
+ while (*p) {
+ if (*p == '/')
+ *p = '\\';
+ ++p;
+ }
+
/* Load the module
*
* TODO: Critical section
*
- * Warning: cid should not be allocated from pool if we
+ * Warning: cid should not be allocated from request pool if we
* cache the isapi process in-memory.
*
* This code could use cacheing... everything that follows
@@ -237,6 +261,9 @@
/* Set up variables */
ap_add_common_vars(r);
ap_add_cgi_vars(r);
+ apr_table_setn(r->subprocess_env, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
+ apr_table_setn(r->subprocess_env, "SERVER_PORT_SECURE", "0");
+ apr_table_setn(r->subprocess_env, "URL", r->uri);

/* Set up connection structure and ecb */
cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
@@ -283,44 +310,45 @@
}

if (ap_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.
- *
- * But we can be smarter and read up to our 48k and then allow
- * the ISAPI app to read further blocks as desired.
+ /* Time to start reading the appropriate amount of data,
+ * and allow the administrator to tweak the number
+ * TODO: add the httpd.conf option for ReadAheadBuffer.
*/
- long to_read = atol(apr_table_get(e, "CONTENT_LENGTH"));
- long read;
+ if (r->remaining) {
+ cid->ecb->cbTotalBytes = r->remaining;
+ if (cid->ecb->cbTotalBytes > ReadAheadBuffer)
+ cid->ecb->cbAvailable = ReadAheadBuffer;
+ else
+ cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
+ }
+ else
+ {
+ cid->ecb->cbTotalBytes = 0xffffffff;
+ cid->ecb->cbAvailable = ReadAheadBuffer;
+ }

- /* 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.
- */
+ cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);

- if (to_read > 49152) {
- if (isa->TerminateExtension)
- (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
- FreeLibrary(isa->handle);
- return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ p = cid->ecb->lpbData;
+ read = 0;
+ while (read < cid->ecb->cbAvailable &&
+ ((res = ap_get_client_block(r, cid->ecb->lpbData + read,
+ cid->ecb->cbAvailable - read)) > 0)) {
+ read += res;
}
-
- cid->ecb->lpbData = apr_pcalloc(r->pool, 1 + to_read);

- if ((read = ap_get_client_block(r, cid->ecb->lpbData, to_read)) < 0) {
- if (isa->TerminateExtension)
- (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
- FreeLibrary(isa->handle);
- return HTTP_INTERNAL_SERVER_ERROR;
+ if (res < 0) {
+ cid->retval = HTTP_INTERNAL_SERVER_ERROR;
+ goto contentfailure;
}

/* 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.
+ * its lpdData string. So we will too.
*/
- cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read + 1;
+ if (res == 0)
+ cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
+ else
+ cid->ecb->cbAvailable = read;
cid->ecb->lpbData[read] = '\0';
}
else {
@@ -339,7 +367,7 @@

/* Check for a log message - and log it */
if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
"ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);

switch(cid->retval) {
@@ -366,10 +394,13 @@
*/

if (!isa->fakeasync) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
- "ISAPI %s asynch I/O request refused",
- r->filename);
- cid->retval = APR_ENOTIMPL;
+ if (LogNotSupported)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING|ARP_NOERRNO, 0, r,
+ "ISAPI %s asynch I/O request refused",
+ r->filename);
+ cid->retval = APR_ENOTIMPL;
+ }
}
else {
cid->complete = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -395,6 +426,7 @@
break;
}

+contentfailure:
/* All done with the DLL... get rid of it...
*
* If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
@@ -467,8 +499,10 @@

/* We only support synchronous writing */
if (dwReserved && dwReserved != HSE_IO_SYNC) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
- "ISAPI %s asynch write", r->filename);
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING|API_NOERRNO, 0, r,
+ "ISAPI %s asynch I/O request refused",
+ r->filename);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
@@ -484,102 +518,83 @@

BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
{
- /* TODO: If the request was a huge transmit or chunked, continue piping the
- * request here, but if it's of a sane size, continue to ...
- */
- return TRUE;
-}
+ request_rec *r = ((isapi_cid *)ConnID)->r;
+ DWORD read = 0;
+ int res;

-static char* ComposeHeaders(request_rec *r, char* data)
-{
- /* We *should* break before this while loop ends */
- while (*data)
- {
- char *value, *lf = strchr(data, '\n');
- int p;
+ if (r->remaining < (long) *lpdwSize)
+ *lpdwSize = r->remaining;

-#ifdef RELAX_HEADER_RULE
- if (lf)
- *lf = '\0';
-#else
- if (!lf) { /* Huh? Invalid data, I think */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "ISAPI %s sent invalid headers", r->filename);
- SetLastError(TODO_ERROR);
+ while (read < *lpdwSize &&
+ ((res = ap_get_client_block(r, (char*)lpvBuffer + read,
+ *lpdwSize - read)) > 0)) {
+ if (res < 0) {
+ *lpdwSize = 0;
+ if (!GetLastError())
+ SetLastError(TODO_ERROR); /* XXX: Find the right error code */
return FALSE;
}
-
- /* Get rid of \n and \r */
- *lf = '\0';
-#endif
- p = strlen(data);
- if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
-
- /* End of headers */
- if (*data == '\0') {
-#ifdef RELAX_HEADER_RULE
- if (lf)
-#endif
- data = lf + 1; /* Reset data */
- break;
- }

- if (!(value = strchr(data, ':'))) {
- SetLastError(TODO_ERROR);
- /* ### euh... we're passing the wrong type of error
- ### code here */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_INTERNAL_SERVER_ERROR, r,
- "ISAPI %s sent invalid headers", r->filename);
- return FALSE;
- }
+ read += res;
+ }

- *value++ = '\0';
- while (*value && apr_isspace(*value)) ++value;
+ *lpdwSize = read;
+ return TRUE;
+}

- /* Check all the special-case headers. Similar to what
- * ap_scan_script_header_err() does (see that function for
- * more detail)
- */
+static BOOL SendResponseHeaderEx(isapi_cid *cid, const char *stat,
+ const char *head, size_t statlen,
+ size_t headlen)
+{
+ int termarg;
+ char *termch;

- if (!strcasecmp(data, "Content-Type"))
- {
- /* Nuke trailing whitespace */
- char *tmp;
- char *endp = value + strlen(value) - 1;
- while (endp > value && apr_isspace(*endp))
- *endp-- = '\0';
+ if (!stat || !*stat) {
+ stat = "Status: 200 OK";
+ }
+ else {
+ char *newstat;
+ if (statlen == 0)
+ statlen = strlen(stat);
+ /* Whoops... not NULL terminated */
+ newstat = apr_palloc(cid->r->pool, statlen + 9);
+ strcpy(newstat, "Status: ");
+ strncpy(newstat + 8, stat, statlen);
+ stat = newstat;
+ }
+
+ if (!head || !*head) {
+ head = "\r\n";
+ }
+ else if ((headlen >= 0) && head[headlen]) {
+ /* Whoops... not NULL terminated */
+ head = apr_pstrndup(cid->r->pool, head, headlen);
+ }
+
+ /* Parse them out, or die trying */
+ cid->retval = ap_scan_script_header_err_strs(cid->r, NULL, &termch,
+ &termarg, stat, head, NULL);
+ cid->ecb->dwHttpStatusCode = cid->r->status;

- tmp = apr_pstrdup (r->pool, value);
- ap_str_tolower(tmp);
- r->content_type = tmp;
- }
- else if (!strcasecmp(data, "Content-Length")) {
- apr_table_set(r->headers_out, data, value);
- }
- else if (!strcasecmp(data, "Transfer-Encoding")) {
- apr_table_set(r->headers_out, data, value);
- }
- else if (!strcasecmp(data, "Set-Cookie")) {
- apr_table_add(r->err_headers_out, data, value);
- }
- else {
- apr_table_merge(r->err_headers_out, data, value);
- }
+ /* All the headers should be set now */
+ ap_send_http_header(cid->r);

- /* Reset data */
-#ifdef RELAX_HEADER_RULE
- if (!lf) {
- data += p;
- break;
- }
-#endif
- data = lf + 1;
+ /* Any data left should now be sent directly,
+ * it may be raw if headlen was provided.
+ */
+ 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);
}
- return data;
-}

+ if (cid->retval == HTTP_INTERNAL_SERVER_ERROR)
+ return FALSE;
+ return TRUE;
+}

-/* XXX: There is an O(n^2) attack possible here. */
+/* XXX: Is there is still an O(n^2) attack possible here? Please detail. */
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
LPVOID lpvBuffer, LPDWORD lpdwSize,
LPDWORD lpdwDataType)
@@ -587,290 +602,380 @@
isapi_cid *cid = (isapi_cid *)hConn;
request_rec *r = cid->r;
request_rec *subreq;
- char *data;

switch (dwHSERequest) {
- case HSE_REQ_SEND_URL_REDIRECT_RESP:
- /* Set the status to be returned when the HttpExtensionProc()
- * is done.
- */
- apr_table_set (r->headers_out, "Location", lpvBuffer);
- cid->r->status = cid->ecb->dwHttpStatusCode
- = HTTP_MOVED_TEMPORARILY;
- return TRUE;
-
- case HSE_REQ_SEND_URL:
- /* Read any additional input */
-
- if (r->remaining > 0) {
- char argsbuffer[HUGE_STRING_LEN];
-
- while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
- }
+ case 1: /* HSE_REQ_SEND_URL_REDIRECT_RESP */
+ /* Set the status to be returned when the HttpExtensionProc()
+ * is done.
+ * WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP
+ * and HSE_REQ_SEND_URL as equivalant per the Jan 2000 SDK.
+ * They most definately are not, even in their own samples.
+ */
+ apr_table_set (r->headers_out, "Location", lpvBuffer);
+ cid->r->status = cid->ecb->dwHttpStatusCode
+ = HTTP_MOVED_TEMPORARILY;
+ return TRUE;

- /* Reset the method to GET */
- r->method = apr_pstrdup(r->pool, "GET");
- r->method_number = M_GET;
+ case 2: /* HSE_REQ_SEND_URL */
+ /* Soak up remaining input */
+ if (r->remaining > 0) {
+ char argsbuffer[HUGE_STRING_LEN];
+ while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
+ }
+
+ /* Reset the method to GET */
+ r->method = apr_pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ /* Don't let anyone think there's still data */
+ apr_table_unset(r->headers_in, "Content-Length");
+
+ /* AV fault per PR3598 - redirected path is lost! */
+ (char*)lpvBuffer = apr_pstrdup(r->pool, (char*)lpvBuffer);
+ ap_internal_redirect((char*)lpvBuffer, r);
+ return TRUE;

- /* Don't let anyone think there's still data */
- apr_table_unset(r->headers_in, "Content-Length");
+ case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */
+ /* Parse them out, or die trying */
+ return SendResponseHeaderEx(cid, (char*) lpvBuffer,
+ (char*) lpdwDataType, -1, -1);

- ap_internal_redirect((char *)lpvBuffer, r);
- return TRUE;

- case HSE_REQ_SEND_RESPONSE_HEADER:
- r->status_line = lpvBuffer ? lpvBuffer : apr_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) {
- ap_send_http_header(r);
- return TRUE;
- }
-
- /* Make a copy - don't disturb the original */
- data = apr_pstrdup(r->pool, (char *)lpdwDataType);
-
- /* Parse them out, or die trying */
- data = ComposeHeaders(r, data);
- if (!data)
- return FALSE;
-
- /* All the headers should be set now */
- ap_send_http_header(r);
-
- /* Any data left should now be sent directly */
- if (*data)
- ap_rputs(data, r);
-
- return TRUE;
-
case HSE_REQ_DONE_WITH_SESSION:
/* Signal to resume the thread completing this request
*/
if (cid->complete)
SetEvent(cid->complete);
return TRUE;
-
- case HSE_REQ_MAP_URL_TO_PATH:
- /* Map a URL to a filename */
- subreq = ap_sub_req_lookup_uri(apr_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 (subreq->finfo.filetype == APR_DIR) {
- int l = strlen((char *)lpvBuffer);
-
- ((char *)lpvBuffer)[l] = '\\';
- ((char *)lpvBuffer)[l + 1] = '\0';
+ case 1001: /* HSE_REQ_MAP_URL_TO_PATH */
+ {
+ /* Map a URL to a filename */
+ char *file = (char *)lpvBuffer;
+ subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *lpdwSize), r);
+
+ strncpy(file, subreq->filename, *lpdwSize - 1);
+ file[*lpdwSize - 1] = '\0';
+
+ /* IIS puts a trailing slash on directories, Apache doesn't */
+ if (subreq->finfo.filetype == APR_DIR) {
+ DWORD l = strlen(file);
+ if (l < *lpdwSize - 1) {
+ file[l] = '\\';
+ file[l + 1] = '\0';
}
-
- return TRUE;
+ }
+ return TRUE;
+ }

- case HSE_REQ_GET_SSPI_INFO:
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
+ case 1002: /* HSE_REQ_GET_SSPI_INFO */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- case HSE_APPEND_LOG_PARAMETER:
- /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
- * This code will do for now...
- */
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
- "ISAPI %s: %s", cid->r->filename,
- (char*) lpvBuffer);
- return TRUE;
+ case 1003: /* HSE_APPEND_LOG_PARAMETER */
+ /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
+ */
+ apr_table_set(r->notes, "isapi-parameter", (char*) lpvBuffer);
+ if (AppendLogToQuery) {
+ if (r->args)
+ r->args = apr_pstrcat(r->pool, r->args, (char*) lpvBuffer, NULL);
+ else
+ r->args = apr_pstrdup(r->pool, (char*) lpvBuffer);
+ }
+ if (AppendLogToErrors)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
+ "ISAPI %s: %s", cid->r->filename,
+ (char*) lpvBuffer);
+ return TRUE;

- case HSE_REQ_IO_COMPLETION:
- /* TODO: Emulate a completion port, if we can...
- * Record the callback address and user defined argument...
- * we will call this after any async request (e.g. transmitfile)
- * as if the request had completed async execution.
- * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
- * to HSE_REQ_IO_COMPLETION, and lpvBuffer may be set to NULL.
- */
- if (!cid->isa->fakeasync)
- return FALSE;
- cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
- cid->completion_arg = (PVOID) lpdwDataType;
- return TRUE;
+ case 1005: /* HSE_REQ_IO_COMPLETION */
+ /* TODO: Emulate a completion port, if we can...
+ * Record the callback address and user defined argument...
+ * we will call this after any async request (e.g. transmitfile)
+ * as if the request had completed async execution.
+ * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
+ * to HSE_REQ_IO_COMPLETION, and lpvBuffer may be set to NULL.
+ */
+ if (!cid->isa->fakeasync)
+ return FALSE;
+ cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
+ cid->completion_arg = (PVOID) lpdwDataType;
+ return TRUE;

- case HSE_REQ_TRANSMIT_FILE:
- /* Use TransmitFile... nothing wrong with that :)
- */
+ case 1006: /* HSE_REQ_TRANSMIT_FILE */
+ /* Use TransmitFile... nothing wrong with that :)
+ * Just not quite ready yet...
+ */

- /* ### euh... we're passing the wrong type of error code here */
- ap_log_rerror(APLOG_MARK, APLOG_WARNING,
- HTTP_INTERNAL_SERVER_ERROR, r,
- "ISAPI asynchronous I/O not supported: %s",
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI asynchronous I/O not supported: %s",
r->filename);
- return FALSE;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- case HSE_REQ_REFRESH_ISAPI_ACL:
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-
-#if defined(HSE_REQ_IS_KEEP_CONN) /* this and a lot more aren't in VC++ 5.0 */
- case HSE_REQ_IS_KEEP_CONN:
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-#endif
-
-#if defined(HSE_REQ_ASYNC_READ_CLIENT)
- case HSE_REQ_ASYNC_READ_CLIENT:
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-#endif
-
-#if defined(HSE_REQ_GET_IMPERSONATION_TOKEN)
- case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-#endif
-
-#if defined(HSE_REQ_MAP_URL_TO_PATH_EX)
- case HSE_REQ_MAP_URL_TO_PATH_EX:
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-
- /* TODO: Not quite ready for prime time yet */
-
- /* Map a URL to a filename */
- subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, (char *)lpvBuffer,
- *lpdwSize), r);
+ case 1007: /* HSE_REQ_REFRESH_ISAPI_ACL */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction "
+ "HSE_REQ_REFRESH_ISAPI_ACL "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
+ case 1008: /* HSE_REQ_IS_KEEP_CONN */
+ *((LPBOOL) lpvBuffer) = (r->connection->keepalive == 1);
+ return TRUE;

- /* IIS puts a trailing slash on directories, Apache doesn't */
+ case 1010: /* HSE_REQ_ASYNC_READ_CLIENT */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI asynchronous I/O not supported: %s",
+ r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- if (subreq->finfo.filetype == APR_DIR) {
- int l = strlen((char *)lpvBuffer);
+ case 1011: /* HSE_REQ_GET_IMPERSONATION_TOKEN Added in ISAPI 4.0 */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction "
+ "HSE_REQ_GET_IMPERSONATION_TOKEN "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- ((char *)lpvBuffer)[l] = '\\';
- ((char *)lpvBuffer)[l + 1] = '\0';
+ case 1012: /* HSE_REQ_MAP_URL_TO_PATH_EX */
+ {
+ /* Map a URL to a filename */
+ LPHSE_URL_MAPEX_INFO info = (LPHSE_URL_MAPEX_INFO) lpdwDataType;
+ char* test_uri = apr_pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize);
+
+ subreq = ap_sub_req_lookup_uri(test_uri, r);
+ info->lpszPath[MAX_PATH - 1] = '\0';
+ strncpy(info->lpszPath, subreq->filename, MAX_PATH - 1);
+ info->cchMatchingURL = strlen(test_uri);
+ info->cchMatchingPath = strlen(info->lpszPath);
+ /* Mapping started with assuming both strings matched.
+ * Now roll on the path_info as a mismatch and handle
+ * terminating slashes for directory matches.
+ */
+ if (subreq->path_info && *subreq->path_info) {
+ strncpy(info->lpszPath + info->cchMatchingPath, subreq->path_info,
+ MAX_PATH - info->cchMatchingPath - 1);
+ info->cchMatchingURL -= strlen(subreq->path_info);
+ if (subreq->finfo.filetype == APR_DIR
+ && info->cchMatchingPath < MAX_PATH - 1) {
+ /* roll forward over path_info's first slash */
+ ++info->cchMatchingPath;
+ ++info->cchMatchingURL;
}
-
- lpdwDataType = (LPDWORD) apr_palloc(r->pool, sizeof(HSE_URL_MAPEX_INFO));
- strncpy(((LPHSE_URL_MAPEX_INFO)lpdwDataType)->lpszPath,
- (char *) lpvBuffer, MAX_PATH);
- ((LPHSE_URL_MAPEX_INFO)lpdwDataType)->dwFlags = 0;
- /* is a combination of:
- * HSE_URL_FLAGS_READ Allow for read.
- * HSE_URL_FLAGS_WRITE Allow for write.
- * HSE_URL_FLAGS_EXECUTE Allow for execute.
- * HSE_URL_FLAGS_SSL Require SSL.
- * HSE_URL_FLAGS_DONT_CACHE Don't cache (virtual root only).
- * HSE_URL_FLAGS_NEGO_CERT Allow client SSL certifications.
- * HSE_URL_FLAGS_REQUIRE_CERT Require client SSL certifications.
- * HSE_URL_FLAGS_MAP_CERT Map SSL certification to a Windows account.
- * HSE_URL_FLAGS_SSL128 Requires a 128-bit SSL.
- * HSE_URL_FLAGS_SCRIPT Allows for script execution.
- */
- /* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingPath
- * (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingURL
- */
+ }
+ else if (subreq->finfo.filetype == APR_DIR
+ && info->cchMatchingPath < MAX_PATH - 1) {
+ /* Add a trailing slash for directory */
+ info->lpszPath[info->cchMatchingPath++] = '/';
+ info->lpszPath[info->cchMatchingPath] = '\0';
+ }

- return TRUE;
-#endif
+ /* If the matched isn't a file, roll match back to the prior slash */
+ if (subreq->finfo.filetype == APR_NOFILE) {
+ while (info->cchMatchingPath && info->cchMatchingURL) {
+ if (info->lpszPath[info->cchMatchingPath - 1] == '/')
+ break;
+ --info->cchMatchingPath;
+ --info->cchMatchingURL;
+ }
+ }
+
+ /* Paths returned with back slashes */
+ for (test_uri = info->lpszPath; *test_uri; ++test_uri)
+ if (*test_uri == '/')
+ *test_uri = '\\';
+
+ /* is a combination of:
+ * HSE_URL_FLAGS_READ 0x001 Allow read
+ * HSE_URL_FLAGS_WRITE 0x002 Allow write
+ * HSE_URL_FLAGS_EXECUTE 0x004 Allow execute
+ * HSE_URL_FLAGS_SSL 0x008 Require SSL
+ * HSE_URL_FLAGS_DONT_CACHE 0x010 Don't cache (VRoot only)
+ * HSE_URL_FLAGS_NEGO_CERT 0x020 Allow client SSL cert
+ * HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert
+ * HSE_URL_FLAGS_MAP_CERT 0x080 Map client SSL cert to account
+ * HSE_URL_FLAGS_SSL128 0x100 Require 128-bit SSL cert
+ * HSE_URL_FLAGS_SCRIPT 0x200 Allow script execution
+ *
+ * XxX: As everywhere, EXEC flags could use some work...
+ * and this could go further with more flags, as desired.
+ */
+ info->dwFlags = (subreq->finfo.protection & APR_UREAD ? 0x001 : 0)
+ | (subreq->finfo.protection & APR_UWRITE ? 0x002 : 0)
+ | (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
+ return TRUE;
+ }

-#if defined(HSE_REQ_ABORTIVE_CLOSE)
- case HSE_REQ_ABORTIVE_CLOSE:
- SetLastError(ERROR_INVALID_PARAMETER);
+ case 1014: /* HSE_REQ_ABORTIVE_CLOSE */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
+ " is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
-#endif

-#if defined(HSE_REQ_GET_CERT_INFO_EX)
- case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
- SetLastError(ERROR_INVALID_PARAMETER);
+ case 1015: /* HSE_REQ_GET_CERT_INFO_EX Added in ISAPI 4.0 */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction "
+ "HSE_REQ_GET_CERT_INFO_EX "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
-#endif

-#if defined(HSE_REQ_SEND_RESPONSE_HEADER_EX)
- case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
+ case 1016: /* HSE_REQ_SEND_RESPONSE_HEADER_EX Added in ISAPI 4.0 */
+ {
+ LPHSE_SEND_HEADER_EX_INFO shi
+ = (LPHSE_SEND_HEADER_EX_INFO) lpvBuffer;
+ /* XXX: ignore shi->fKeepConn? We shouldn't need the advise */
+ /* r->connection->keepalive = shi->fKeepConn; */
+ return SendResponseHeaderEx(cid, shi->pszStatus, shi->pszHeader,
+ shi->cchStatus, shi->cchHeader);
+ }

- /* TODO: Not quite ready for prime time */
+ case 1017: /* HSE_REQ_CLOSE_CONNECTION Added after ISAPI 4.0 */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction "
+ "HSE_REQ_CLOSE_CONNECTION "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
- && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus) {
- r->status_line = apr_pstrndup(r->pool,
- ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
- ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
- }
- else {
- r->status_line = apr_pstrdup(r->pool, "200 OK");
- }
- sscanf(r->status_line, "%d", &r->status);
- cid->ecb->dwHttpStatusCode = r->status;
+ case 1018: /* HSE_REQ_IS_CONNECTED Added after ISAPI 4.0 */
+ /* Returns True if client is connected c.f. MSKB Q188346
+ * XXX: That statement is very ambigious... assuming the
+ * identical return mechanism as HSE_REQ_IS_KEEP_CONN.
+ */
+ *((LPBOOL) lpvBuffer) = (r->connection->aborted == 0);
+ return TRUE;

- if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader
- && ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader)
- {
- /* Make a copy - don't disturb the original */
- data = apr_pstrndup(r->pool,
- ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader,
- ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader);
-
- /* Parse them out, or die trying */
- data = ComposeHeaders(r, data);
- if (!data)
- return FALSE;
+ case 1020: /* HSE_REQ_EXTENSION_TRIGGER Added after ISAPI 4.0 */
+ /* Undocumented - defined by the Microsoft Jan '00 Platform SDK
+ */
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction "
+ "HSE_REQ_EXTENSION_TRIGGER "
+ "is not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;

- }
- else {
- data = "\0";
- }
-
- /* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn;
- *
- * Now how are we about to start listening to an ISAPI's
- * idea of keeping or closing a connection? Seriously :)
- */
+ default:
+ if (LogNotSupported)
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
+ "ISAPI ServerSupportFunction (%d) not supported: "
+ "%s", dwHSERequest, r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+}

- /* All the headers should be set now */
- ap_send_http_header(r);
+/*
+ * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
+ */
+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;
+ }

- /* Any data left should now be sent directly */
- if (*data)
- ap_rputs(data, r);
+ if (((val = strtol(arg, (char **) &err, 10)) <= 0) || *err)
+ return "ISAPIReadAheadBuffer must be a legitimate value.";
+
+ ReadAheadBuffer = val;
+ return NULL;
+}

- return TRUE;
-#endif
+/*
+ * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
+ */
+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 defined(HSE_REQ_CLOSE_CONNECTION) /* not in VC++ 5.0 or 6.0 */
- case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
-#endif
+ 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;
+}

-#if defined(HSE_REQ_IS_CONNECTED) /* not in VC++ 5.0 or 6.0 */
- case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
- /* Returns True if client is connected c.f. Q188346*/
- return TRUE;
-#endif
+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;
+ }

- /* case HSE_REQ_EXTENSION_TRIGGER:
- * Added after ISAPI 4.0?
- * Undocumented - from the Microsoft Jan '00 Platform SDK
- */
- default:
- /* TODO: log unrecognized ServerSupportCommand for debugging
- */
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
+ 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}
@@ -882,7 +987,7 @@
NULL, /* merge per-dir config */
NULL, /* server config */
NULL, /* merge server config */
- NULL, /* command apr_table_t */
+ isapi_cmds, /* command apr_table_t */
isapi_handlers, /* handlers */
NULL /* register hooks */
};



1.41 +58 -0 apache-2.0/src/main/util_script.c

Index: util_script.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/util_script.c,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -r1.40 -r1.41
--- util_script.c 2000/08/07 19:26:00 1.40
+++ util_script.c 2000/10/02 15:38:57 1.41
@@ -607,6 +607,64 @@
}


+struct vastrs {
+ va_list args;
+ int arg;
+ const char *curpos;
+};
+
+static int getsfunc_STRING(char *w, int len, void *pvastrs)
+{
+ struct vastrs *strs = (struct vastrs*) pvastrs;
+ char *p;
+ int t;
+
+ if (!strs->curpos || !*strs->curpos)
+ return 0;
+ p = strchr(strs->curpos, '\n');
+ if (p)
+ ++p;
+ else
+ p = strchr(strs->curpos, '\0');
+ t = p - strs->curpos;
+ if (t > len)
+ t = len;
+ strncpy (w, strs->curpos, t);
+ w[t] = '\0';
+ if (!strs->curpos[t]) {
+ ++strs->arg;
+ strs->curpos = va_arg(strs->args, const char *);
+ }
+ else
+ strs->curpos += t;
+ return t;
+}
+
+/* ap_scan_script_header_err_strs() accepts additional const char* args...
+ * each is treated as one or more header lines, and the first non-header
+ * character is returned to **arg, **data. (The first optional arg is
+ * counted as 0.)
+ */
+API_EXPORT_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
+ char *buffer,
+ const char **termch,
+ int *termarg, ...)
+{
+ struct vastrs strs;
+ int res;
+
+ va_start(strs.args, termarg);
+ strs.arg = 0;
+ strs.curpos = va_arg(strs.args, char*);
+ res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs);
+ if (termch)
+ *termch = strs.curpos;
+ if (termarg)
+ *termarg = strs.arg;
+ va_end(strs.args);
+ return res;
+}
+
API_EXPORT(void) ap_send_size(apr_ssize_t size, request_rec *r)
{
/* XXX: this -1 thing is a gross hack */



1.11 +19 -0 apache-2.0/src/include/util_script.h

Index: util_script.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/util_script.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- util_script.h 2000/08/07 03:07:50 1.10
+++ util_script.h 2000/10/02 15:38:59 1.11
@@ -139,6 +139,25 @@
char *buffer);

/**
+ * Read headers strings from a script, ensuring that the output is valid. If
+ * the output is valid, then the headers are added to the headers out of the
+ * current request
+ * @param r The current request
+ * @param buffer Empty when calling the function. On output, if there was an
+ * error, the string that cause the error is stored here.
+ * @param termch Pointer to the last character parsed.
+ * @param termarg Pointer to an int to capture the last argument parsed.
+ * @param args String arguments to parse consecutively for headers,
+ * a NULL argument terminates the list.
+ * @return HTTP_OK on success, HTTP_INTERNAL_SERVER_ERROR otherwise
+ * @deffunc int ap_scan_script_header_err_core(request_rec *r, char *buffer, int (*getsfunc)(char *, int, void *), void *getsfunc_data)
+ */
+API_EXPORT_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
+ char *buffer,
+ const char **termch,
+ int *termarg, ...);
+
+/**
* Read headers output from a script, ensuring that the output is valid. If
* the output is valid, then the headers are added to the headers out of the
* current request