diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c')
-rw-r--r-- | rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c | 1760 |
1 files changed, 0 insertions, 1760 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c deleted file mode 100644 index 859d5670..00000000 --- a/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c +++ /dev/null @@ -1,1760 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * mod_isapi.c - Internet Server Application (ISA) module for Apache - * by Alexei Kosut <akosut apache.org>, significant overhauls and - * redesign by William Rowe <wrowe covalent.net>, and hints from many - * other developer/users who have hit on specific flaws. - * - * This module implements the ISAPI Handler architecture, allowing - * Apache to load Internet Server Applications (ISAPI extensions), - * similar to the support in IIS, Zope, O'Reilly's WebSite and others. - * - * It is a complete implementation of the ISAPI 2.0 specification, - * except for "Microsoft extensions" to the API which provide - * asynchronous I/O. It is further extended to include additional - * "Microsoft extentions" through IIS 5.0, with some deficiencies - * where one-to-one mappings don't exist. - * - * Refer to /manual/mod/mod_isapi.html for additional details on - * configuration and use, but check this source for specific support - * of the API, - */ - -#include "ap_config.h" -#include "httpd.h" -#include "http_config.h" -#include "http_core.h" -#include "http_protocol.h" -#include "http_request.h" -#include "http_log.h" -#include "util_script.h" -#include "mod_core.h" -#include "apr_lib.h" -#include "apr_strings.h" -#include "apr_portable.h" -#include "apr_buckets.h" -#include "apr_thread_mutex.h" -#include "apr_thread_rwlock.h" -#include "apr_hash.h" -#include "mod_isapi.h" - -/* Retry frequency for a failed-to-load isapi .dll */ -#define ISAPI_RETRY apr_time_from_sec(30) - -/********************************************************** - * - * ISAPI Module Configuration - * - **********************************************************/ - -module AP_MODULE_DECLARE_DATA isapi_module; - -#define ISAPI_UNDEF -1 - -/* Our isapi per-dir config structure */ -typedef struct isapi_dir_conf { - int read_ahead_buflen; - int log_unsupported; - int log_to_errlog; - int log_to_query; - int fake_async; -} isapi_dir_conf; - -typedef struct isapi_loaded isapi_loaded; - -apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, - const char *fpath, isapi_loaded** isa); - -static void *create_isapi_dir_config(apr_pool_t *p, char *dummy) -{ - isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf)); - - dir->read_ahead_buflen = ISAPI_UNDEF; - dir->log_unsupported = ISAPI_UNDEF; - dir->log_to_errlog = ISAPI_UNDEF; - dir->log_to_query = ISAPI_UNDEF; - dir->fake_async = ISAPI_UNDEF; - - return dir; -} - -static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_) -{ - isapi_dir_conf *base = (isapi_dir_conf *) base_; - isapi_dir_conf *add = (isapi_dir_conf *) add_; - isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf)); - - dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF) - ? base->read_ahead_buflen - : add->read_ahead_buflen; - dir->log_unsupported = (add->log_unsupported == ISAPI_UNDEF) - ? base->log_unsupported - : add->log_unsupported; - dir->log_to_errlog = (add->log_to_errlog == ISAPI_UNDEF) - ? base->log_to_errlog - : add->log_to_errlog; - dir->log_to_query = (add->log_to_query == ISAPI_UNDEF) - ? base->log_to_query - : add->log_to_query; - dir->fake_async = (add->fake_async == ISAPI_UNDEF) - ? base->fake_async - : add->fake_async; - - return dir; -} - -static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, - const char *filename) -{ - isapi_loaded *isa; - apr_finfo_t tmp; - apr_status_t rv; - char *fspec; - - /* ### Just an observation ... it would be terribly cool to be - * able to use this per-dir, relative to the directory block being - * defined. The hash result remains global, but shorthand of - * <Directory "c:/webapps/isapi"> - * ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll - * </Directory> - * would be very convienent. - */ - fspec = ap_server_root_relative(cmd->pool, filename); - if (!fspec) { - ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server, - "ISAPI: invalid module path, skipping %s", filename); - return NULL; - } - if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE, - cmd->temp_pool)) != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, - "ISAPI: unable to stat, skipping %s", fspec); - return NULL; - } - if (tmp.filetype != APR_REG) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, - "ISAPI: not a regular file, skipping %s", fspec); - return NULL; - } - - /* Load the extention as cached (with null request_rec) */ - rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa); - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, - "ISAPI: unable to cache, skipping %s", fspec); - return NULL; - } - - return NULL; -} - -static const command_rec isapi_cmds[] = { - AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot, - (void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen), - OR_FILEINFO, "Maximum client request body to initially pass to the" - " ISAPI handler (default: 49152)"), - AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot, - (void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported), - OR_FILEINFO, "Log requests not supported by the ISAPI server" - " on or off (default: off)"), - AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot, - (void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog), - OR_FILEINFO, "Send all Append Log requests to the error log" - " on or off (default: off)"), - AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot, - (void *)APR_OFFSETOF(isapi_dir_conf, log_to_query), - OR_FILEINFO, "Append Log requests are concatinated to the query args" - " on or off (default: on)"), - AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot, - (void *)APR_OFFSETOF(isapi_dir_conf, fake_async), - OR_FILEINFO, "Fake Asynchronous support for isapi callbacks" - " on or off [Experimental] (default: off)"), - AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL, - RSRC_CONF, "Cache the specified ISAPI extension in-process"), - {NULL} -}; - -/********************************************************** - * - * ISAPI Module Cache handling section - * - **********************************************************/ - -/* Our isapi global config values */ -static struct isapi_global_conf { - apr_pool_t *pool; - apr_thread_mutex_t *lock; - apr_hash_t *hash; -} loaded; - -/* Our loaded isapi module description structure */ -struct isapi_loaded { - const char *filename; - apr_thread_rwlock_t *in_progress; - apr_status_t last_load_rv; - apr_time_t last_load_time; - apr_dso_handle_t *handle; - HSE_VERSION_INFO *isapi_version; - apr_uint32_t report_version; - apr_uint32_t timeout; - PFN_GETEXTENSIONVERSION GetExtensionVersion; - PFN_HTTPEXTENSIONPROC HttpExtensionProc; - PFN_TERMINATEEXTENSION TerminateExtension; -}; - -static apr_status_t isapi_unload(isapi_loaded *isa, int force) -{ - /* All done with the DLL... get rid of it... - * - * If optionally cached, and we weren't asked to force the unload, - * pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload, - * otherwise, leave it alone (it didn't choose to cooperate.) - */ - if (!isa->handle) { - return APR_SUCCESS; - } - if (isa->TerminateExtension) { - if (force) { - (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD); - } - else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) { - return APR_EGENERAL; - } - } - apr_dso_unload(isa->handle); - isa->handle = NULL; - return APR_SUCCESS; -} - -static apr_status_t cleanup_isapi(void *isa_) -{ - isapi_loaded* isa = (isapi_loaded*) isa_; - - /* We must force the module to unload, we are about - * to lose the isapi structure's allocation entirely. - */ - return isapi_unload(isa, 1); -} - -static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa) -{ - apr_status_t rv; - - isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO)); - - /* TODO: These aught to become overrideable, so that we - * assure a given isapi can be fooled into behaving well. - * - * The tricky bit, they aren't really a per-dir sort of - * config, they will always be constant across every - * reference to the .dll no matter what context (vhost, - * location, etc) they apply to. - */ - isa->report_version = 0x500; /* Revision 5.0 */ - isa->timeout = 300 * 1000000; /* microsecs, not used */ - - rv = apr_dso_load(&isa->handle, isa->filename, p); - if (rv) - { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, - "ISAPI: failed to load %s", isa->filename); - isa->handle = NULL; - return rv; - } - - rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle, - "GetExtensionVersion"); - if (rv) - { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, - "ISAPI: missing GetExtensionVersion() in %s", - isa->filename); - apr_dso_unload(isa->handle); - isa->handle = NULL; - return rv; - } - - rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle, - "HttpExtensionProc"); - if (rv) - { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, - "ISAPI: missing HttpExtensionProc() in %s", - isa->filename); - apr_dso_unload(isa->handle); - isa->handle = NULL; - return rv; - } - - /* TerminateExtension() is an optional interface */ - rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle, - "TerminateExtension"); - apr_set_os_error(0); - - /* Run GetExtensionVersion() */ - if (!(isa->GetExtensionVersion)(isa->isapi_version)) { - apr_status_t rv = apr_get_os_error(); - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, - "ISAPI: failed call to GetExtensionVersion() in %s", - isa->filename); - apr_dso_unload(isa->handle); - isa->handle = NULL; - return rv; - } - - apr_pool_cleanup_register(p, isa, cleanup_isapi, - apr_pool_cleanup_null); - - return APR_SUCCESS; -} - -apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, - const char *fpath, isapi_loaded** isa) -{ - apr_status_t rv; - const char *key; - - if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) { - return rv; - } - - *isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING); - - if (*isa) { - - /* If we find this lock exists, use a set-aside copy of gainlock - * to avoid race conditions on NULLing the in_progress variable - * when the load has completed. Release the global isapi hash - * lock so other requests can proceed, then rdlock for completion - * of loading our desired dll or wrlock if we would like to retry - * loading the dll (because last_load_rv failed and retry is up.) - */ - apr_thread_rwlock_t *gainlock = (*isa)->in_progress; - - /* gainlock is NULLed after the module loads successfully. - * This free-threaded module can be used without any locking. - */ - if (!gainlock) { - rv = (*isa)->last_load_rv; - apr_thread_mutex_unlock(loaded.lock); - return rv; - } - - - if ((*isa)->last_load_rv == APR_SUCCESS) { - apr_thread_mutex_unlock(loaded.lock); - if ((rv = apr_thread_rwlock_rdlock(gainlock)) - != APR_SUCCESS) { - return rv; - } - rv = (*isa)->last_load_rv; - apr_thread_rwlock_unlock(gainlock); - return rv; - } - - if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) { - - /* Remember last_load_time before releasing the global - * hash lock to avoid colliding with another thread - * that hit this exception at the same time as our - * retry attempt, since we unlock the global mutex - * before attempting a write lock for this module. - */ - apr_time_t check_time = (*isa)->last_load_time; - apr_thread_mutex_unlock(loaded.lock); - - if ((rv = apr_thread_rwlock_wrlock(gainlock)) - != APR_SUCCESS) { - return rv; - } - - /* If last_load_time is unchanged, we still own this - * retry, otherwise presume another thread provided - * our retry (for good or ill). Relock the global - * hash for updating last_load_ vars, so their update - * is always atomic to the global lock. - */ - if (check_time == (*isa)->last_load_time) { - - rv = isapi_load(loaded.pool, s, *isa); - - apr_thread_mutex_lock(loaded.lock); - (*isa)->last_load_rv = rv; - (*isa)->last_load_time = apr_time_now(); - apr_thread_mutex_unlock(loaded.lock); - } - else { - rv = (*isa)->last_load_rv; - } - apr_thread_rwlock_unlock(gainlock); - - return rv; - } - - /* We haven't hit timeup on retry, let's grab the last_rv - * within the hash mutex before unlocking. - */ - rv = (*isa)->last_load_rv; - apr_thread_mutex_unlock(loaded.lock); - - return rv; - } - - /* If the module was not found, it's time to create a hash key entry - * before releasing the hash lock to avoid multiple threads from - * loading the same module. - */ - key = apr_pstrdup(loaded.pool, fpath); - *isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded)); - (*isa)->filename = key; - if (r) { - /* A mutex that exists only long enough to attempt to - * load this isapi dll, the release this module to all - * other takers that came along during the one-time - * load process. Short lifetime for this lock would - * be great, however, using r->pool is nasty if those - * blocked on the lock haven't all unlocked before we - * attempt to destroy. A nastier race condition than - * I want to deal with at this moment... - */ - apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool); - apr_thread_rwlock_wrlock((*isa)->in_progress); - } - - apr_hash_set(loaded.hash, key, APR_HASH_KEY_STRING, *isa); - - /* Now attempt to load the isapi on our own time, - * allow other isapi processing to resume. - */ - apr_thread_mutex_unlock(loaded.lock); - - rv = isapi_load(loaded.pool, s, *isa); - (*isa)->last_load_time = apr_time_now(); - (*isa)->last_load_rv = rv; - - if (r && (rv == APR_SUCCESS)) { - /* Let others who are blocked on this particular - * module resume their requests, for better or worse. - */ - apr_thread_rwlock_t *unlock = (*isa)->in_progress; - (*isa)->in_progress = NULL; - apr_thread_rwlock_unlock(unlock); - } - else if (!r && (rv != APR_SUCCESS)) { - /* We must leave a rwlock around for requests to retry - * loading this dll after timeup... since we were in - * the setup code we had avoided creating this lock. - */ - apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool); - } - - return (*isa)->last_load_rv; -} - -/********************************************************** - * - * ISAPI Module request callbacks section - * - **********************************************************/ - -/* Our "Connection ID" structure */ -struct isapi_cid { - EXTENSION_CONTROL_BLOCK *ecb; - isapi_dir_conf dconf; - isapi_loaded *isa; - request_rec *r; - int headers_set; - int response_sent; - PFN_HSE_IO_COMPLETION completion; - void *completion_arg; - apr_thread_mutex_t *completed; -}; - -int APR_THREAD_FUNC GetServerVariable (isapi_cid *cid, - char *variable_name, - void *buf_ptr, - apr_uint32_t *buf_size) -{ - request_rec *r = cid->r; - const char *result; - char *buf_data = (char*)buf_ptr; - apr_uint32_t len; - - if (!strcmp(variable_name, "ALL_HTTP")) - { - /* crlf delimited, colon split, comma separated and - * null terminated list of HTTP_ vars - */ - const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); - const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; - int i; - - for (len = 0, i = 0; i < arr->nelts; i++) { - if (!strncmp(elts[i].key, "HTTP_", 5)) { - len += strlen(elts[i].key) + strlen(elts[i].val) + 3; - } - } - - if (*buf_size < len + 1) { - *buf_size = len + 1; - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER)); - return 0; - } - - for (i = 0; i < arr->nelts; i++) { - if (!strncmp(elts[i].key, "HTTP_", 5)) { - strcpy(buf_data, elts[i].key); - buf_data += strlen(elts[i].key); - *(buf_data++) = ':'; - strcpy(buf_data, elts[i].val); - buf_data += strlen(elts[i].val); - *(buf_data++) = '\r'; - *(buf_data++) = '\n'; - } - } - - *(buf_data++) = '\0'; - *buf_size = len + 1; - return 1; - } - - if (!strcmp(variable_name, "ALL_RAW")) - { - /* crlf delimited, colon split, comma separated and - * null terminated list of the raw request header - */ - const apr_array_header_t *arr = apr_table_elts(r->headers_in); - const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; - int i; - - for (len = 0, i = 0; i < arr->nelts; i++) { - len += strlen(elts[i].key) + strlen(elts[i].val) + 4; - } - - if (*buf_size < len + 1) { - *buf_size = len + 1; - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER)); - return 0; - } - - for (i = 0; i < arr->nelts; i++) { - strcpy(buf_data, elts[i].key); - buf_data += strlen(elts[i].key); - *(buf_data++) = ':'; - *(buf_data++) = ' '; - strcpy(buf_data, elts[i].val); - buf_data += strlen(elts[i].val); - *(buf_data++) = '\r'; - *(buf_data++) = '\n'; - } - *(buf_data++) = '\0'; - *buf_size = len + 1; - return 1; - } - - /* Not a special case */ - result = apr_table_get(r->subprocess_env, variable_name); - - if (result) { - len = strlen(result); - if (*buf_size < len + 1) { - *buf_size = len + 1; - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER)); - return 0; - } - strcpy(buf_data, result); - *buf_size = len + 1; - return 1; - } - - /* Not Found */ - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_INDEX)); - return 0; -} - -int APR_THREAD_FUNC ReadClient(isapi_cid *cid, - void *buf_data, - apr_uint32_t *buf_size) -{ - request_rec *r = cid->r; - apr_uint32_t read = 0; - int res; - - if (r->remaining < *buf_size) { - *buf_size = (apr_size_t)r->remaining; - } - - while (read < *buf_size && - ((res = ap_get_client_block(r, (char*)buf_data + read, - *buf_size - read)) > 0)) { - read += res; - } - - *buf_size = read; - if (res < 0) { - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_READ_FAULT)); - } - return (res >= 0); -} - -/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and - * the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s) - * as well as other functions that write responses and presume that - * the support functions above are optional. - * - * Other callers trying to split headers and body bytes should pass - * head/headlen alone (leaving stat/statlen NULL/0), so that they - * get a proper count of bytes consumed. The argument passed to stat - * isn't counted as the head bytes are. - */ -static apr_ssize_t send_response_header(isapi_cid *cid, - const char *stat, - const char *head, - apr_size_t statlen, - apr_size_t headlen) -{ - int head_present = 1; - int termarg; - int res; - int old_status; - const char *termch; - apr_size_t ate = 0; - - if (!head || headlen == 0 || !*head) { - head = stat; - stat = NULL; - headlen = statlen; - statlen = 0; - head_present = 0; /* Don't eat the header */ - } - - if (!stat || statlen == 0 || !*stat) { - if (head && headlen && *head && ((stat = memchr(head, '\r', headlen)) - || (stat = memchr(head, '\n', headlen)) - || (stat = memchr(head, '\0', headlen)) - || (stat = head + headlen))) { - statlen = stat - head; - if (memchr(head, ':', statlen)) { - stat = "Status: 200 OK"; - statlen = strlen(stat); - } - else { - const char *flip = head; - head = stat; - stat = flip; - headlen -= statlen; - ate += statlen; - if (*head == '\r' && headlen) - ++head, --headlen, ++ate; - if (*head == '\n' && headlen) - ++head, --headlen, ++ate; - } - } - } - - if (stat && (statlen > 0) && *stat) { - char *newstat; - if (!apr_isdigit(*stat)) { - const char *stattok = stat; - int toklen = statlen; - while (toklen && *stattok && !apr_isspace(*stattok)) { - ++stattok; --toklen; - } - while (toklen && apr_isspace(*stattok)) { - ++stattok; --toklen; - } - /* Now decide if we follow the xxx message - * or the http/x.x xxx message format - */ - if (toklen && apr_isdigit(*stattok)) { - statlen = toklen; - stat = stattok; - } - } - newstat = apr_palloc(cid->r->pool, statlen + 9); - strcpy(newstat, "Status: "); - apr_cpystrn(newstat + 8, stat, statlen + 1); - stat = newstat; - statlen += 8; - } - - if (!head || headlen == 0 || !*head) { - head = "\r\n"; - headlen = 2; - } - else - { - if (head[headlen - 1] && head[headlen]) { - /* Whoops... not NULL terminated */ - head = apr_pstrndup(cid->r->pool, head, headlen); - } - } - - /* Seems IIS does not enforce the requirement for \r\n termination - * on HSE_REQ_SEND_RESPONSE_HEADER, but we won't panic... - * ap_scan_script_header_err_strs handles this aspect for us. - * - * Parse them out, or die trying - */ - old_status = cid->r->status; - - if (stat) { - res = ap_scan_script_header_err_strs(cid->r, NULL, &termch, &termarg, - stat, head, NULL); - } - else { - res = ap_scan_script_header_err_strs(cid->r, NULL, &termch, &termarg, - head, NULL); - } - - /* Set our status. */ - if (res) { - /* This is an immediate error result from the parser - */ - cid->r->status = res; - cid->r->status_line = ap_get_status_line(cid->r->status); - cid->ecb->dwHttpStatusCode = cid->r->status; - } - else if (cid->r->status) { - /* We have a status in r->status, so let's just use it. - * This is likely to be the Status: parsed above, and - * may also be a delayed error result from the parser. - * If it was filled in, status_line should also have - * been filled in. - */ - cid->ecb->dwHttpStatusCode = cid->r->status; - } - else if (cid->ecb->dwHttpStatusCode - && cid->ecb->dwHttpStatusCode != HTTP_OK) { - /* Now we fall back on dwHttpStatusCode if it appears - * ap_scan_script_header fell back on the default code. - * Any other results set dwHttpStatusCode to the decoded - * status value. - */ - cid->r->status = cid->ecb->dwHttpStatusCode; - cid->r->status_line = ap_get_status_line(cid->r->status); - } - else if (old_status) { - /* Well... either there is no dwHttpStatusCode or it's HTTP_OK. - * In any case, we don't have a good status to return yet... - * Perhaps the one we came in with will be better. Let's use it, - * if we were given one (note this is a pendantic case, it would - * normally be covered above unless the scan script code unset - * the r->status). Should there be a check here as to whether - * we are setting a valid response code? - */ - cid->r->status = old_status; - cid->r->status_line = ap_get_status_line(cid->r->status); - cid->ecb->dwHttpStatusCode = cid->r->status; - } - else { - /* None of dwHttpStatusCode, the parser's r->status nor the - * old value of r->status were helpful, and nothing was decoded - * from Status: string passed to us. Let's just say HTTP_OK - * and get the data out, this was the isapi dev's oversight. - */ - cid->r->status = HTTP_OK; - cid->r->status_line = ap_get_status_line(cid->r->status); - cid->ecb->dwHttpStatusCode = cid->r->status; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, cid->r, - "ISAPI: Could not determine HTTP response code; using %d", - cid->r->status); - } - - if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR) { - return -1; - } - - /* If only Status was passed, we consumed nothing - */ - if (!head_present) - return 0; - - cid->headers_set = 1; - - /* If all went well, tell the caller we consumed the headers complete - */ - if (!termch) - return(ate + headlen); - - /* Any data left must be sent directly by the caller, all we - * give back is the size of the headers we consumed (which only - * happens if the parser got to the head arg, which varies based - * on whether we passed stat+head to scan, or only head. - */ - if (termch && (termarg == (stat ? 1 : 0)) - && head_present && head + headlen > termch) { - return ate + termch - head; - } - return ate; -} - -int APR_THREAD_FUNC WriteClient(isapi_cid *cid, - void *buf_ptr, - apr_uint32_t *size_arg, - apr_uint32_t flags) -{ - request_rec *r = cid->r; - conn_rec *c = r->connection; - apr_uint32_t buf_size = *size_arg; - char *buf_data = (char*)buf_ptr; - apr_bucket_brigade *bb; - apr_bucket *b; - apr_status_t rv = APR_SUCCESS; - - if (!cid->headers_set) { - /* It appears that the foxisapi module and other clients - * presume that WriteClient("headers\n\nbody") will work. - * Parse them out, or die trying. - */ - apr_ssize_t ate; - ate = send_response_header(cid, NULL, buf_data, 0, buf_size); - if (ate < 0) { - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - - buf_data += ate; - buf_size -= ate; - } - - if (buf_size) { - bb = apr_brigade_create(r->pool, c->bucket_alloc); - b = apr_bucket_transient_create(buf_data, buf_size, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - rv = ap_pass_brigade(r->output_filters, bb); - cid->response_sent = 1; - if (rv != APR_SUCCESS) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "ISAPI: WriteClient ap_pass_brigade " - "failed: %s", r->filename); - } - - if ((flags & HSE_IO_ASYNC) && cid->completion) { - if (rv == APR_SUCCESS) { - cid->completion(cid->ecb, cid->completion_arg, - *size_arg, ERROR_SUCCESS); - } - else { - cid->completion(cid->ecb, cid->completion_arg, - *size_arg, ERROR_WRITE_FAULT); - } - } - return (rv == APR_SUCCESS); -} - -/* A "safe" maximum bucket size, 1Gb */ -#define MAX_BUCKET_SIZE (0x40000000) - -apr_bucket *brigade_insert_file(apr_bucket_brigade *bb, - apr_file_t *f, - apr_off_t start, - apr_off_t length, - apr_pool_t *p) -{ - apr_bucket *e; - - if (sizeof(apr_off_t) == sizeof(apr_size_t) || length < MAX_BUCKET_SIZE) { - e = apr_bucket_file_create(f, start, (apr_size_t)length, p, - bb->bucket_alloc); - } - else { - /* Several buckets are needed. */ - e = apr_bucket_file_create(f, start, MAX_BUCKET_SIZE, p, - bb->bucket_alloc); - - while (length > MAX_BUCKET_SIZE) { - apr_bucket *ce; - apr_bucket_copy(e, &ce); - APR_BRIGADE_INSERT_TAIL(bb, ce); - e->start += MAX_BUCKET_SIZE; - length -= MAX_BUCKET_SIZE; - } - e->length = (apr_size_t)length; /* Resize just the last bucket */ - } - - APR_BRIGADE_INSERT_TAIL(bb, e); - return e; -} - -int APR_THREAD_FUNC ServerSupportFunction(isapi_cid *cid, - apr_uint32_t HSE_code, - void *buf_ptr, - apr_uint32_t *buf_size, - apr_uint32_t *data_type) -{ - request_rec *r = cid->r; - conn_rec *c = r->connection; - char *buf_data = (char*)buf_ptr; - request_rec *subreq; - apr_status_t rv; - - switch (HSE_code) { - case 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", buf_data); - cid->r->status = cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY; - cid->r->status_line = ap_get_status_line(cid->r->status); - cid->headers_set = 1; - return 1; - - case 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! */ - buf_data = apr_pstrdup(r->pool, (char*)buf_data); - ap_internal_redirect(buf_data, r); - return 1; - - case HSE_REQ_SEND_RESPONSE_HEADER: - { - /* Parse them out, or die trying */ - apr_size_t statlen = 0, headlen = 0; - apr_ssize_t ate; - if (buf_data) - statlen = strlen((char*) buf_data); - if (data_type) - headlen = strlen((char*) data_type); - ate = send_response_header(cid, (char*) buf_data, - (char*) data_type, - statlen, headlen); - if (ate < 0) { - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - else if ((apr_size_t)ate < headlen) { - apr_bucket_brigade *bb; - apr_bucket *b; - bb = apr_brigade_create(cid->r->pool, c->bucket_alloc); - b = apr_bucket_transient_create((char*) data_type + ate, - headlen - ate, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - rv = ap_pass_brigade(cid->r->output_filters, bb); - cid->response_sent = 1; - if (rv != APR_SUCCESS) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "ISAPI: ServerSupport function " - "HSE_REQ_SEND_RESPONSE_HEADER " - "ap_pass_brigade failed: %s", r->filename); - return (rv == APR_SUCCESS); - } - /* Deliberately hold off sending 'just the headers' to begin to - * accumulate the body and speed up the overall response, or at - * least wait for the end the session. - */ - return 1; - } - - case HSE_REQ_DONE_WITH_SESSION: - /* Signal to resume the thread completing this request, - * leave it to the pool cleanup to dispose of our mutex. - */ - if (cid->completed) { - (void)apr_thread_mutex_unlock(cid->completed); - return 1; - } - else if (cid->dconf.log_unsupported) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_DONE_WITH_SESSION is not supported: %s", - r->filename); - } - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_MAP_URL_TO_PATH: - { - /* Map a URL to a filename */ - char *file = (char *)buf_data; - apr_uint32_t len; - subreq = ap_sub_req_lookup_uri( - apr_pstrndup(cid->r->pool, file, *buf_size), r, NULL); - - if (!subreq->filename) { - ap_destroy_sub_req(subreq); - return 0; - } - - len = (apr_uint32_t)strlen(r->filename); - - if ((subreq->finfo.filetype == APR_DIR) - && (!subreq->path_info) - && (file[len - 1] != '/')) - file = apr_pstrcat(cid->r->pool, subreq->filename, "/", NULL); - else - file = apr_pstrcat(cid->r->pool, subreq->filename, - subreq->path_info, NULL); - - ap_destroy_sub_req(subreq); - -#ifdef WIN32 - /* We need to make this a real Windows path name */ - apr_filepath_merge(&file, "", file, APR_FILEPATH_NATIVE, r->pool); -#endif - - *buf_size = apr_cpystrn(buf_data, file, *buf_size) - buf_data; - - return 1; - } - - case HSE_REQ_GET_SSPI_INFO: - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction HSE_REQ_GET_SSPI_INFO " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_APPEND_LOG_PARAMETER: - /* Log buf_data, of buf_size bytes, in the URI Query (cs-uri-query) field - */ - apr_table_set(r->notes, "isapi-parameter", (char*) buf_data); - if (cid->dconf.log_to_query) { - if (r->args) - r->args = apr_pstrcat(r->pool, r->args, (char*) buf_data, NULL); - else - r->args = apr_pstrdup(r->pool, (char*) buf_data); - } - if (cid->dconf.log_to_errlog) - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "ISAPI: %s: %s", cid->r->filename, - (char*) buf_data); - return 1; - - case HSE_REQ_IO_COMPLETION: - /* Emulates a completion port... Record callback address and - * user defined arg, we will call this after any async request - * (e.g. transmitfile) as if the request executed async. - * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call - * to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL. - */ - if (cid->dconf.fake_async) { - cid->completion = (PFN_HSE_IO_COMPLETION) buf_data; - cid->completion_arg = (void *) data_type; - return 1; - } - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction HSE_REQ_IO_COMPLETION " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_TRANSMIT_FILE: - { - /* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND) - */ - HSE_TF_INFO *tf = (HSE_TF_INFO*)buf_data; - apr_uint32_t sent = 0; - apr_ssize_t ate = 0; - apr_bucket_brigade *bb; - apr_bucket *b; - apr_file_t *fd; - apr_off_t fsize; - - if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) { - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction HSE_REQ_TRANSMIT_FILE " - "as HSE_IO_ASYNC is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - - /* Presume the handle was opened with the CORRECT semantics - * for TransmitFile - */ - if ((rv = apr_os_file_put(&fd, &tf->hFile, - APR_READ | APR_XTHREAD, r->pool)) - != APR_SUCCESS) { - return 0; - } - if (tf->BytesToWrite) { - fsize = tf->BytesToWrite; - } - else { - apr_finfo_t fi; - if (apr_file_info_get(&fi, APR_FINFO_SIZE, fd) != APR_SUCCESS) { - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - fsize = fi.size - tf->Offset; - } - - /* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */ - bb = apr_brigade_create(r->pool, c->bucket_alloc); - - /* According to MS: if calling HSE_REQ_TRANSMIT_FILE with the - * HSE_IO_SEND_HEADERS flag, then you can't otherwise call any - * HSE_SEND_RESPONSE_HEADERS* fn, but if you don't use the flag, - * you must have done so. They document that the pHead headers - * option is valid only for HSE_IO_SEND_HEADERS - we are a bit - * more flexible and assume with the flag, pHead are the - * response headers, and without, pHead simply contains text - * (handled after this case). - */ - if ((tf->dwFlags & HSE_IO_SEND_HEADERS) && tf->pszStatusCode) { - ate = send_response_header(cid, tf->pszStatusCode, - (char*)tf->pHead, - strlen(tf->pszStatusCode), - tf->HeadLength); - } - else if (!cid->headers_set && tf->pHead && tf->HeadLength - && *(char*)tf->pHead) { - ate = send_response_header(cid, NULL, (char*)tf->pHead, - 0, tf->HeadLength); - if (ate < 0) - { - apr_brigade_destroy(bb); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - } - - if (tf->pHead && (apr_size_t)ate < tf->HeadLength) { - b = apr_bucket_transient_create((char*)tf->pHead + ate, - tf->HeadLength - ate, - c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - sent = tf->HeadLength; - } - - sent += (apr_uint32_t)fsize; - brigade_insert_file(bb, fd, tf->Offset, fsize, r->pool); - - if (tf->pTail && tf->TailLength) { - sent += tf->TailLength; - b = apr_bucket_transient_create((char*)tf->pTail, - tf->TailLength, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - } - - b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - rv = ap_pass_brigade(r->output_filters, bb); - cid->response_sent = 1; - if (rv != APR_SUCCESS) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "ISAPI: ServerSupport function " - "HSE_REQ_TRANSMIT_FILE " - "ap_pass_brigade failed: %s", r->filename); - - /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete - * pass pContect to the HseIO callback. - */ - if (tf->dwFlags & HSE_IO_ASYNC) { - if (tf->pfnHseIO) { - if (rv == APR_SUCCESS) { - tf->pfnHseIO(cid->ecb, tf->pContext, - ERROR_SUCCESS, sent); - } - else { - tf->pfnHseIO(cid->ecb, tf->pContext, - ERROR_WRITE_FAULT, sent); - } - } - else if (cid->completion) { - if (rv == APR_SUCCESS) { - cid->completion(cid->ecb, cid->completion_arg, - sent, ERROR_SUCCESS); - } - else { - cid->completion(cid->ecb, cid->completion_arg, - sent, ERROR_WRITE_FAULT); - } - } - } - return (rv == APR_SUCCESS); - } - - case HSE_REQ_REFRESH_ISAPI_ACL: - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_REFRESH_ISAPI_ACL " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_IS_KEEP_CONN: - *((int *)buf_data) = (r->connection->keepalive == AP_CONN_KEEPALIVE); - return 1; - - case HSE_REQ_ASYNC_READ_CLIENT: - { - apr_uint32_t read = 0; - int res; - if (!cid->dconf.fake_async) { - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: asynchronous I/O not supported: %s", - r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - - if (r->remaining < *buf_size) { - *buf_size = (apr_size_t)r->remaining; - } - - while (read < *buf_size && - ((res = ap_get_client_block(r, (char*)buf_data + read, - *buf_size - read)) > 0)) { - read += res; - } - - if ((*data_type & HSE_IO_ASYNC) && cid->completion) { - /* XXX: Many authors issue their next HSE_REQ_ASYNC_READ_CLIENT - * within the completion logic. An example is MS's own PSDK - * sample web/iis/extensions/io/ASyncRead. This potentially - * leads to stack exhaustion. To refactor, the notification - * logic needs to move to isapi_handler() - differentiating - * the cid->completed event with a new flag to indicate - * an async-notice versus the async request completed. - */ - if (res >= 0) { - cid->completion(cid->ecb, cid->completion_arg, - read, ERROR_SUCCESS); - } - else { - cid->completion(cid->ecb, cid->completion_arg, - read, ERROR_READ_FAULT); - } - } - return (res >= 0); - } - - case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */ - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_GET_IMPERSONATION_TOKEN " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_MAP_URL_TO_PATH_EX: - { - /* Map a URL to a filename */ - HSE_URL_MAPEX_INFO *info = (HSE_URL_MAPEX_INFO*)data_type; - char* test_uri = apr_pstrndup(r->pool, (char *)buf_data, *buf_size); - - subreq = ap_sub_req_lookup_uri(test_uri, r, NULL); - info->cchMatchingURL = strlen(test_uri); - info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename, - sizeof(info->lpszPath)) - 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) { - apr_cpystrn(info->lpszPath + info->cchMatchingPath, - subreq->path_info, - sizeof(info->lpszPath) - info->cchMatchingPath); - info->cchMatchingURL -= strlen(subreq->path_info); - if (subreq->finfo.filetype == APR_DIR - && info->cchMatchingPath < sizeof(info->lpszPath) - 1) { - /* roll forward over path_info's first slash */ - ++info->cchMatchingPath; - ++info->cchMatchingURL; - } - } - else if (subreq->finfo.filetype == APR_DIR - && info->cchMatchingPath < sizeof(info->lpszPath) - 1) { - /* Add a trailing slash for directory */ - info->lpszPath[info->cchMatchingPath++] = '/'; - info->lpszPath[info->cchMatchingPath] = '\0'; - } - - /* 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 1; - } - - case HSE_REQ_ABORTIVE_CLOSE: - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE" - " is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */ - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_GET_CERT_INFO_EX " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */ - { - HSE_SEND_HEADER_EX_INFO *shi = (HSE_SEND_HEADER_EX_INFO*)buf_data; - - /* Ignore shi->fKeepConn - we don't want the advise - */ - apr_ssize_t ate = send_response_header(cid, shi->pszStatus, - shi->pszHeader, - shi->cchStatus, - shi->cchHeader); - if (ate < 0) { - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } - else if ((apr_size_t)ate < shi->cchHeader) { - apr_bucket_brigade *bb; - apr_bucket *b; - bb = apr_brigade_create(cid->r->pool, c->bucket_alloc); - b = apr_bucket_transient_create(shi->pszHeader + ate, - shi->cchHeader - ate, - c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - rv = ap_pass_brigade(cid->r->output_filters, bb); - cid->response_sent = 1; - if (rv != APR_SUCCESS) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "ISAPI: ServerSupport function " - "HSE_REQ_SEND_RESPONSE_HEADER_EX " - "ap_pass_brigade failed: %s", r->filename); - return (rv == APR_SUCCESS); - } - /* Deliberately hold off sending 'just the headers' to begin to - * accumulate the body and speed up the overall response, or at - * least wait for the end the session. - */ - return 1; - } - - case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */ - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_CLOSE_CONNECTION " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */ - /* Returns True if client is connected c.f. MSKB Q188346 - * assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN - */ - *((int *)buf_data) = (r->connection->aborted == 0); - return 1; - - case HSE_REQ_EXTENSION_TRIGGER: /* Added after ISAPI 4.0 */ - /* Undocumented - defined by the Microsoft Jan '00 Platform SDK - */ - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction " - "HSE_REQ_EXTENSION_TRIGGER " - "is not supported: %s", r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - - default: - if (cid->dconf.log_unsupported) - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: ServerSupportFunction (%d) not supported: " - "%s", HSE_code, r->filename); - apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER)); - return 0; - } -} - -/********************************************************** - * - * ISAPI Module request invocation section - * - **********************************************************/ - -apr_status_t isapi_handler (request_rec *r) -{ - isapi_dir_conf *dconf; - apr_table_t *e; - apr_status_t rv; - isapi_loaded *isa; - isapi_cid *cid; - const char *val; - apr_uint32_t read; - int res; - - if(strcmp(r->handler, "isapi-isa") - && strcmp(r->handler, "isapi-handler")) { - /* Hang on to the isapi-isa for compatibility with older docs - * (wtf did '-isa' mean in the first place?) but introduce - * a newer and clearer "isapi-handler" name. - */ - return DECLINED; - } - dconf = ap_get_module_config(r->per_dir_config, &isapi_module); - e = r->subprocess_env; - - /* Use similar restrictions as CGIs - * - * If this fails, it's pointless to load the isapi dll. - */ - if (!(ap_allow_options(r) & OPT_EXECCGI)) { - return HTTP_FORBIDDEN; - } - if (r->finfo.filetype == APR_NOFILE) { - return HTTP_NOT_FOUND; - } - if (r->finfo.filetype != APR_REG) { - return HTTP_FORBIDDEN; - } - if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && - r->path_info && *r->path_info) { - /* default to accept */ - return HTTP_NOT_FOUND; - } - - if (isapi_lookup(r->pool, r->server, r, r->filename, &isa) - != APR_SUCCESS) { - return HTTP_INTERNAL_SERVER_ERROR; - } - /* Set up variables */ - ap_add_common_vars(r); - ap_add_cgi_vars(r); - apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER"); - if ((val = apr_table_get(e, "HTTPS")) && (strcmp(val, "on") == 0)) - apr_table_setn(e, "SERVER_PORT_SECURE", "1"); - else - apr_table_setn(e, "SERVER_PORT_SECURE", "0"); - apr_table_setn(e, "URL", r->uri); - - /* Set up connection structure and ecb, - * NULL or zero out most fields. - */ - cid = apr_pcalloc(r->pool, sizeof(isapi_cid)); - - /* Fixup defaults for dconf */ - cid->dconf.read_ahead_buflen = (dconf->read_ahead_buflen == ISAPI_UNDEF) - ? 49152 : dconf->read_ahead_buflen; - cid->dconf.log_unsupported = (dconf->log_unsupported == ISAPI_UNDEF) - ? 0 : dconf->log_unsupported; - cid->dconf.log_to_errlog = (dconf->log_to_errlog == ISAPI_UNDEF) - ? 0 : dconf->log_to_errlog; - cid->dconf.log_to_query = (dconf->log_to_query == ISAPI_UNDEF) - ? 1 : dconf->log_to_query; - cid->dconf.fake_async = (dconf->fake_async == ISAPI_UNDEF) - ? 0 : dconf->fake_async; - - cid->ecb = apr_pcalloc(r->pool, sizeof(EXTENSION_CONTROL_BLOCK)); - cid->ecb->ConnID = cid; - cid->isa = isa; - cid->r = r; - r->status = 0; - - cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK); - cid->ecb->dwVersion = isa->report_version; - cid->ecb->dwHttpStatusCode = 0; - strcpy(cid->ecb->lpszLogData, ""); - /* TODO: are copies really needed here? - */ - cid->ecb->lpszMethod = (char*) r->method; - cid->ecb->lpszQueryString = (char*) apr_table_get(e, "QUERY_STRING"); - cid->ecb->lpszPathInfo = (char*) apr_table_get(e, "PATH_INFO"); - cid->ecb->lpszPathTranslated = (char*) apr_table_get(e, "PATH_TRANSLATED"); - cid->ecb->lpszContentType = (char*) apr_table_get(e, "CONTENT_TYPE"); - - /* Set up the callbacks */ - cid->ecb->GetServerVariable = GetServerVariable; - cid->ecb->WriteClient = WriteClient; - cid->ecb->ReadClient = ReadClient; - cid->ecb->ServerSupportFunction = ServerSupportFunction; - - /* Set up client input */ - res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR); - if (res) { - return res; - } - - if (ap_should_client_block(r)) { - /* Time to start reading the appropriate amount of data, - * and allow the administrator to tweak the number - */ - if (r->remaining) { - cid->ecb->cbTotalBytes = (apr_size_t)r->remaining; - if (cid->ecb->cbTotalBytes > (apr_uint32_t)cid->dconf.read_ahead_buflen) - cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen; - else - cid->ecb->cbAvailable = cid->ecb->cbTotalBytes; - } - else - { - cid->ecb->cbTotalBytes = 0xffffffff; - cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen; - } - - cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1); - - read = 0; - while (read < cid->ecb->cbAvailable && - ((res = ap_get_client_block(r, (char*)cid->ecb->lpbData + read, - cid->ecb->cbAvailable - read)) > 0)) { - read += res; - } - - if (res < 0) { - return HTTP_INTERNAL_SERVER_ERROR; - } - - /* Although it's not to spec, IIS seems to null-terminate - * its lpdData string. So we will too. - */ - if (res == 0) - cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read; - else - cid->ecb->cbAvailable = read; - cid->ecb->lpbData[read] = '\0'; - } - else { - cid->ecb->cbTotalBytes = 0; - cid->ecb->cbAvailable = 0; - cid->ecb->lpbData = NULL; - } - - /* To emulate async behavior... - * - * We create a cid->completed mutex and lock on it so that the - * app can believe is it running async. - * - * This request completes upon a notification through - * ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION), which - * unlocks this mutex. If the HttpExtensionProc() returns - * HSE_STATUS_PENDING, we will attempt to gain this lock again - * which may *only* happen once HSE_REQ_DONE_WITH_SESSION has - * unlocked the mutex. - */ - if (cid->dconf.fake_async) { - rv = apr_thread_mutex_create(&cid->completed, - APR_THREAD_MUTEX_UNNESTED, - r->pool); - if (cid->completed && (rv == APR_SUCCESS)) { - rv = apr_thread_mutex_lock(cid->completed); - } - - if (!cid->completed || (rv != APR_SUCCESS)) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: Failed to create completion mutex"); - return HTTP_INTERNAL_SERVER_ERROR; - } - } - - /* All right... try and run the sucker */ - rv = (*isa->HttpExtensionProc)(cid->ecb); - - /* Check for a log message - and log it */ - if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData) - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "ISAPI: %s: %s", r->filename, cid->ecb->lpszLogData); - - switch(rv) { - case 0: /* Strange, but MS isapi accepts this as success */ - case HSE_STATUS_SUCCESS: - case HSE_STATUS_SUCCESS_AND_KEEP_CONN: - /* Ignore the keepalive stuff; Apache handles it just fine without - * the ISAPI Handler's "advice". - * Per Microsoft: "In IIS versions 4.0 and later, the return - * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN - * are functionally identical: Keep-Alive connections are - * maintained, if supported by the client." - * ... so we were pat all this time - */ - break; - - case HSE_STATUS_PENDING: - /* emulating async behavior... - */ - if (cid->completed) { - /* The completion port was locked prior to invoking - * HttpExtensionProc(). Once we can regain the lock, - * when ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION) - * is called by the extension to release the lock, - * we may finally destroy the request. - */ - (void)apr_thread_mutex_lock(cid->completed); - break; - } - else if (cid->dconf.log_unsupported) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: asynch I/O result HSE_STATUS_PENDING " - "from HttpExtensionProc() is not supported: %s", - r->filename); - r->status = HTTP_INTERNAL_SERVER_ERROR; - } - break; - - case HSE_STATUS_ERROR: - /* end response if we have yet to do so. - */ - ap_log_rerror(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), r, - "ISAPI: HSE_STATUS_ERROR result from " - "HttpExtensionProc(): %s", r->filename); - r->status = HTTP_INTERNAL_SERVER_ERROR; - break; - - default: - ap_log_rerror(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), r, - "ISAPI: unrecognized result code %d " - "from HttpExtensionProc(): %s ", - rv, r->filename); - r->status = HTTP_INTERNAL_SERVER_ERROR; - break; - } - - /* Flush the response now, including headers-only responses */ - if (cid->headers_set || cid->response_sent) { - conn_rec *c = r->connection; - apr_bucket_brigade *bb; - apr_bucket *b; - apr_status_t rv; - - bb = apr_brigade_create(r->pool, c->bucket_alloc); - b = apr_bucket_eos_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - rv = ap_pass_brigade(r->output_filters, bb); - cid->response_sent = 1; - - if (rv != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "ISAPI: ap_pass_brigade failed to " - "complete the response: %s ", r->filename); - } - - return OK; /* NOT r->status, even if it has changed. */ - } - - /* As the client returned no error, and if we did not error out - * ourselves, trust dwHttpStatusCode to say something relevant. - */ - if (!ap_is_HTTP_SERVER_ERROR(r->status) && cid->ecb->dwHttpStatusCode) { - r->status = cid->ecb->dwHttpStatusCode; - } - - /* For all missing-response situations simply return the status, - * and let the core respond to the client. - */ - return r->status; -} - -/********************************************************** - * - * ISAPI Module Setup Hooks - * - **********************************************************/ - -static int isapi_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) -{ - apr_status_t rv; - - apr_pool_create_ex(&loaded.pool, pconf, NULL, NULL); - if (!loaded.pool) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, NULL, - "ISAPI: could not create the isapi cache pool"); - return APR_EGENERAL; - } - - loaded.hash = apr_hash_make(loaded.pool); - if (!loaded.hash) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "ISAPI: Failed to create module cache"); - return APR_EGENERAL; - } - - rv = apr_thread_mutex_create(&loaded.lock, APR_THREAD_MUTEX_DEFAULT, - loaded.pool); - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, rv, 0, NULL, - "ISAPI: Failed to create module cache lock"); - return rv; - } - return OK; -} - -static void isapi_hooks(apr_pool_t *cont) -{ - ap_hook_pre_config(isapi_pre_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE); -} - -module AP_MODULE_DECLARE_DATA isapi_module = { - STANDARD20_MODULE_STUFF, - create_isapi_dir_config, /* create per-dir config */ - merge_isapi_dir_configs, /* merge per-dir config */ - NULL, /* server config */ - NULL, /* merge server config */ - isapi_cmds, /* command apr_table_t */ - isapi_hooks /* register hooks */ -}; |