]> granicus.if.org Git - apache/commitdiff
Forward port 1.3 overhaul of isapi to 2.0.
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 2 Oct 2000 15:38:59 +0000 (15:38 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 2 Oct 2000 15:38:59 +0000 (15:38 +0000)
  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.

PR:
Obtained from:
Submitted by:
Reviewed by:

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86360 13f79535-47bb-0310-9956-ffa450edef68

include/util_script.h
modules/arch/win32/mod_isapi.c
os/win32/mod_isapi.c
server/util_script.c

index 1aa30e1dd57d185c0193dc9a004b73c5a2ee17c8..8795c18ac2cb2139b1705d613a48ad3c06393005 100644 (file)
@@ -138,6 +138,25 @@ API_EXPORT(int) ap_scan_script_header_err(request_rec *r, apr_file_t *f, char *b
 API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *f,
                                                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
index a21929d55e653bb7e8bcfd53f07480bcd0336da7..8344dac2c0f5ff111fae3acf9065dc4f3c810a04 100644 (file)
 
 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_status_t isapi_handler (request_rec *r)
     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 @@ apr_status_t isapi_handler (request_rec *r)
     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 @@ apr_status_t isapi_handler (request_rec *r)
     /* 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 @@ apr_status_t isapi_handler (request_rec *r)
     }
 
     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 @@ apr_status_t isapi_handler (request_rec *r)
 
     /* 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 @@ apr_status_t isapi_handler (request_rec *r)
              */
             
             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 @@ apr_status_t isapi_handler (request_rec *r)
             break;
     }
 
+contentfailure:
     /* All done with the DLL... get rid of it...
      *
      * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
@@ -467,8 +499,10 @@ BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
 
     /* 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 WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
 
 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 ...
-     */
+    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(TODO_ERROR); /* XXX: Find the right error code */
+            return FALSE;
+        }
+
+        read += res;
+    }
+
+    *lpdwSize = read;
     return TRUE;
 }
 
-static char* ComposeHeaders(request_rec *r, char* data)
+static BOOL SendResponseHeaderEx(isapi_cid *cid, const char *stat,
+                                 const char *head, size_t statlen,
+                                 size_t headlen)
 {
-    /* We *should* break before this while loop ends */
-    while (*data) 
-    {
-        char *value, *lf = strchr(data, '\n');
-        int p;
-
-#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);
-            return FALSE;
-        }
+    int termarg;
+    char *termch;
 
-        /* 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;
-        }
+    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;
+    }
 
-        *value++ = '\0';
-        while (*value && apr_isspace(*value)) ++value;
+    if (!head || !*head) {
+        head = "\r\n";
+    }
+    else if ((headlen >= 0) && head[headlen]) {
+        /* Whoops... not NULL terminated */
+        head = apr_pstrndup(cid->r->pool, head, headlen);
+    }
 
-        /* Check all the special-case headers. Similar to what
-         * ap_scan_script_header_err() does (see that function for
-         * more detail)
-         */
+    /* 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;
 
-        if (!strcasecmp(data, "Content-Type")) 
-        {
-            /* Nuke trailing whitespace */    
-            char *tmp;
-            char *endp = value + strlen(value) - 1;
-            while (endp > value && apr_isspace(*endp)) 
-                *endp-- = '\0';
-
-            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,70 +602,44 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
     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));
-            }
-
-            /* 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");
+    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;
 
-            ap_internal_redirect((char *)lpvBuffer, r);
-            return TRUE;
+    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));
+        }
 
-        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;
+        /* Reset the method to GET */
+        r->method = apr_pstrdup(r->pool, "GET");
+        r->method_number = M_GET;
 
-            /* 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;
+        /* Don't let anyone think there's still data */
+        apr_table_unset(r->headers_in, "Content-Length");
 
-            /* All the headers should be set now */
-            ap_send_http_header(r);
+        /* AV fault per PR3598 - redirected path is lost! */
+        (char*)lpvBuffer = apr_pstrdup(r->pool, (char*)lpvBuffer);
+        ap_internal_redirect((char*)lpvBuffer, r);
+        return TRUE;
 
-            /* Any data left should now be sent directly */
-            if (*data)
-                ap_rputs(data, r);
+    case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */
+        /* Parse them out, or die trying */
+        return SendResponseHeaderEx(cid, (char*) lpvBuffer,
+                                    (char*) lpdwDataType, -1, -1);
 
-            return TRUE;
 
         case HSE_REQ_DONE_WITH_SESSION:
             /* Signal to resume the thread completing this request
@@ -659,218 +648,334 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
                 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;
             }
+        }
+        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';
+        }
 
-            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
-             */
-
-            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;
-
-            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 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;
 
-            }
-            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 :)
-             */
+    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;
 
-            /* All the headers should be set now */
-            ap_send_http_header(r);
+    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;
+    }
+}
 
-            /* Any data left should now be sent directly */
-            if (*data)
-                ap_rputs(data, 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;
+    }
 
-            return TRUE;
-#endif
+    if (((val = strtol(arg, (char **) &err, 10)) <= 0) || *err)
+        return "ISAPIReadAheadBuffer must be a legitimate value.";
+    
+    ReadAheadBuffer = val;
+    return NULL;
+}
 
-#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
+/*
+ * 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_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
+    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;
+}
 
-     /* 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;
+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}
@@ -882,7 +987,7 @@ module isapi_module = {
    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 */
 };
index a21929d55e653bb7e8bcfd53f07480bcd0336da7..8344dac2c0f5ff111fae3acf9065dc4f3c810a04 100644 (file)
 
 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_status_t isapi_handler (request_rec *r)
     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 @@ apr_status_t isapi_handler (request_rec *r)
     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 @@ apr_status_t isapi_handler (request_rec *r)
     /* 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 @@ apr_status_t isapi_handler (request_rec *r)
     }
 
     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 @@ apr_status_t isapi_handler (request_rec *r)
 
     /* 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 @@ apr_status_t isapi_handler (request_rec *r)
              */
             
             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 @@ apr_status_t isapi_handler (request_rec *r)
             break;
     }
 
+contentfailure:
     /* All done with the DLL... get rid of it...
      *
      * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
@@ -467,8 +499,10 @@ BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
 
     /* 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 WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
 
 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 ...
-     */
+    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(TODO_ERROR); /* XXX: Find the right error code */
+            return FALSE;
+        }
+
+        read += res;
+    }
+
+    *lpdwSize = read;
     return TRUE;
 }
 
-static char* ComposeHeaders(request_rec *r, char* data)
+static BOOL SendResponseHeaderEx(isapi_cid *cid, const char *stat,
+                                 const char *head, size_t statlen,
+                                 size_t headlen)
 {
-    /* We *should* break before this while loop ends */
-    while (*data) 
-    {
-        char *value, *lf = strchr(data, '\n');
-        int p;
-
-#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);
-            return FALSE;
-        }
+    int termarg;
+    char *termch;
 
-        /* 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;
-        }
+    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;
+    }
 
-        *value++ = '\0';
-        while (*value && apr_isspace(*value)) ++value;
+    if (!head || !*head) {
+        head = "\r\n";
+    }
+    else if ((headlen >= 0) && head[headlen]) {
+        /* Whoops... not NULL terminated */
+        head = apr_pstrndup(cid->r->pool, head, headlen);
+    }
 
-        /* Check all the special-case headers. Similar to what
-         * ap_scan_script_header_err() does (see that function for
-         * more detail)
-         */
+    /* 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;
 
-        if (!strcasecmp(data, "Content-Type")) 
-        {
-            /* Nuke trailing whitespace */    
-            char *tmp;
-            char *endp = value + strlen(value) - 1;
-            while (endp > value && apr_isspace(*endp)) 
-                *endp-- = '\0';
-
-            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,70 +602,44 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
     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));
-            }
-
-            /* 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");
+    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;
 
-            ap_internal_redirect((char *)lpvBuffer, r);
-            return TRUE;
+    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));
+        }
 
-        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;
+        /* Reset the method to GET */
+        r->method = apr_pstrdup(r->pool, "GET");
+        r->method_number = M_GET;
 
-            /* 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;
+        /* Don't let anyone think there's still data */
+        apr_table_unset(r->headers_in, "Content-Length");
 
-            /* All the headers should be set now */
-            ap_send_http_header(r);
+        /* AV fault per PR3598 - redirected path is lost! */
+        (char*)lpvBuffer = apr_pstrdup(r->pool, (char*)lpvBuffer);
+        ap_internal_redirect((char*)lpvBuffer, r);
+        return TRUE;
 
-            /* Any data left should now be sent directly */
-            if (*data)
-                ap_rputs(data, r);
+    case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */
+        /* Parse them out, or die trying */
+        return SendResponseHeaderEx(cid, (char*) lpvBuffer,
+                                    (char*) lpdwDataType, -1, -1);
 
-            return TRUE;
 
         case HSE_REQ_DONE_WITH_SESSION:
             /* Signal to resume the thread completing this request
@@ -659,218 +648,334 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
                 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;
             }
+        }
+        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';
+        }
 
-            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
-             */
-
-            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;
-
-            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 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;
 
-            }
-            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 :)
-             */
+    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;
 
-            /* All the headers should be set now */
-            ap_send_http_header(r);
+    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;
+    }
+}
 
-            /* Any data left should now be sent directly */
-            if (*data)
-                ap_rputs(data, 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;
+    }
 
-            return TRUE;
-#endif
+    if (((val = strtol(arg, (char **) &err, 10)) <= 0) || *err)
+        return "ISAPIReadAheadBuffer must be a legitimate value.";
+    
+    ReadAheadBuffer = val;
+    return NULL;
+}
 
-#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
+/*
+ * 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_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
+    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;
+}
 
-     /* 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;
+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}
@@ -882,7 +987,7 @@ module isapi_module = {
    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 */
 };
index 31f410a16a2595a1aa0b25913220f781e8de2185..1de2908241446f37863b8c00a680de4f1f03a5a1 100644 (file)
@@ -607,6 +607,64 @@ API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb,
 }
 
 
+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 */