summaryrefslogtreecommitdiffstats
path: root/rubbos/app/httpd-2.0.64/modules/arch
diff options
context:
space:
mode:
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/arch')
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/libprews.c70
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_anon.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_basic.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_dbm.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_digest.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cache.def5
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cern_meta.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_dav.def3
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_disk_cache.def3
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_echo.def2
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_expires.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_file_cache.def2
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_headers.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_info.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_logio.def2
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mem_cache.def3
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mime_magic.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_netware.c194
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_nw_ssl.c1151
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy.def6
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_connect.def4
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_ftp.def4
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_http.def7
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_rewrite.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_speling.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_status.def2
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_unique_id.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_usertrack.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/mod_vhost_alias.def2
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/netware/moddavfs.def1
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/.deps0
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile8
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile.in3
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/config.m411
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c1760
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.dsp132
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.h271
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/mod_win32.c553
-rw-r--r--rubbos/app/httpd-2.0.64/modules/arch/win32/modules.mk3
39 files changed, 4215 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/libprews.c b/rubbos/app/httpd-2.0.64/modules/arch/netware/libprews.c
new file mode 100644
index 00000000..5ac5e54c
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/libprews.c
@@ -0,0 +1,70 @@
+/* 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.
+ */
+
+/*------------------------------------------------------------------
+ These functions are to be called when the shared NLM starts and
+ stops. By using these functions instead of defining a main()
+ and calling ExitThread(TSR_THREAD, 0), the load time of the
+ shared NLM is faster and memory size reduced.
+
+ You may also want to override these in your own Apache module
+ to do any cleanup other than the mechanism Apache modules
+ provide.
+------------------------------------------------------------------*/
+#include <netware.h>
+//#include "stddef.h"
+#include "novsock2.h"
+
+int _NonAppStart
+(
+ void *NLMHandle,
+ void *errorScreen,
+ const char *cmdLine,
+ const char *loadDirPath,
+ size_t uninitializedDataLength,
+ void *NLMFileHandle,
+ int (*readRoutineP)( int conn, void *fileHandle, size_t offset,
+ size_t nbytes, size_t *bytesRead, void *buffer ),
+ size_t customDataOffset,
+ size_t customDataSize,
+ int messageCount,
+ const char **messages
+)
+{
+#pragma unused(cmdLine)
+#pragma unused(loadDirPath)
+#pragma unused(uninitializedDataLength)
+#pragma unused(NLMFileHandle)
+#pragma unused(readRoutineP)
+#pragma unused(customDataOffset)
+#pragma unused(customDataSize)
+#pragma unused(messageCount)
+#pragma unused(messages)
+
+ WSADATA wsaData;
+
+ return WSAStartup((WORD) MAKEWORD(2, 0), &wsaData);
+}
+
+void _NonAppStop( void )
+{
+ WSACleanup();
+}
+
+int _NonAppCheckUnload( void )
+{
+ return 0;
+}
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_anon.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_anon.def
new file mode 100644
index 00000000..ab6b138f
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_anon.def
@@ -0,0 +1 @@
+EXPORT auth_anon_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_basic.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_basic.def
new file mode 100644
index 00000000..0a6f81aa
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_basic.def
@@ -0,0 +1 @@
+EXPORT auth_basic_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_dbm.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_dbm.def
new file mode 100644
index 00000000..830f194d
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_dbm.def
@@ -0,0 +1 @@
+EXPORT auth_dbm_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_digest.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_digest.def
new file mode 100644
index 00000000..6a3aa085
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_auth_digest.def
@@ -0,0 +1 @@
+EXPORT auth_digest_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cache.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cache.def
new file mode 100644
index 00000000..6fd6423b
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cache.def
@@ -0,0 +1,5 @@
+EXPORT cache_module
+EXPORT @mod_cache.imp
+
+
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cern_meta.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cern_meta.def
new file mode 100644
index 00000000..5638325b
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_cern_meta.def
@@ -0,0 +1 @@
+EXPORT cern_meta_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_dav.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_dav.def
new file mode 100644
index 00000000..fb56c92f
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_dav.def
@@ -0,0 +1,3 @@
+EXPORT dav_module
+EXPORT @dav.imp
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_disk_cache.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_disk_cache.def
new file mode 100644
index 00000000..0a9440ad
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_disk_cache.def
@@ -0,0 +1,3 @@
+IMPORT @mod_cache.imp
+EXPORT disk_cache_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_echo.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_echo.def
new file mode 100644
index 00000000..694135a5
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_echo.def
@@ -0,0 +1,2 @@
+EXPORT echo_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_expires.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_expires.def
new file mode 100644
index 00000000..bc416630
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_expires.def
@@ -0,0 +1 @@
+EXPORT expires_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_file_cache.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_file_cache.def
new file mode 100644
index 00000000..8ab98cfb
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_file_cache.def
@@ -0,0 +1,2 @@
+EXPORT file_cache_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_headers.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_headers.def
new file mode 100644
index 00000000..2fe35a85
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_headers.def
@@ -0,0 +1 @@
+EXPORT headers_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_info.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_info.def
new file mode 100644
index 00000000..ce71cb37
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_info.def
@@ -0,0 +1 @@
+EXPORT info_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_logio.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_logio.def
new file mode 100644
index 00000000..68c70891
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_logio.def
@@ -0,0 +1,2 @@
+EXPORT logio_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mem_cache.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mem_cache.def
new file mode 100644
index 00000000..531d6871
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mem_cache.def
@@ -0,0 +1,3 @@
+IMPORT @mod_cache.imp
+EXPORT mem_cache_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mime_magic.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mime_magic.def
new file mode 100644
index 00000000..95307476
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_mime_magic.def
@@ -0,0 +1 @@
+EXPORT mime_magic_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_netware.c b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_netware.c
new file mode 100644
index 00000000..3fec8cc4
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_netware.c
@@ -0,0 +1,194 @@
+/* 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.
+ */
+
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_buckets.h"
+#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_optional.h"
+#include "apr_lib.h"
+#include "mod_cgi.h"
+
+#ifdef NETWARE
+
+
+module AP_MODULE_DECLARE_DATA netware_module;
+
+typedef struct {
+ apr_table_t *file_type_handlers; /* CGI map from file types to CGI modules */
+ apr_table_t *file_handler_mode; /* CGI module mode (spawn in same address space or not) */
+ apr_table_t *extra_env_vars; /* Environment variables to be added to the CGI environment */
+} netware_dir_config;
+
+
+static void *create_netware_dir_config(apr_pool_t *p, char *dir)
+{
+ netware_dir_config *new = (netware_dir_config*) apr_palloc(p, sizeof(netware_dir_config));
+
+ new->file_type_handlers = apr_table_make(p, 10);
+ new->file_handler_mode = apr_table_make(p, 10);
+ new->extra_env_vars = apr_table_make(p, 10);
+
+ apr_table_set(new->file_type_handlers, "NLM", "OS");
+
+ return new;
+}
+
+static void *merge_netware_dir_configs(apr_pool_t *p, void *basev, void *addv)
+{
+ netware_dir_config *base = (netware_dir_config *) basev;
+ netware_dir_config *add = (netware_dir_config *) addv;
+ netware_dir_config *new = (netware_dir_config *) apr_palloc(p, sizeof(netware_dir_config));
+
+ new->file_type_handlers = apr_table_overlay(p, add->file_type_handlers, base->file_type_handlers);
+ new->file_handler_mode = apr_table_overlay(p, add->file_handler_mode, base->file_handler_mode);
+ new->extra_env_vars = apr_table_overlay(p, add->extra_env_vars, base->extra_env_vars);
+
+ return new;
+}
+
+static const char *set_extension_map(cmd_parms *cmd, netware_dir_config *m,
+ char *CGIhdlr, char *ext, char *detach)
+{
+ int i, len;
+
+ if (*ext == '.')
+ ++ext;
+
+ if (CGIhdlr != NULL) {
+ len = strlen(CGIhdlr);
+ for (i=0; i<len; i++) {
+ if (CGIhdlr[i] == '\\') {
+ CGIhdlr[i] = '/';
+ }
+ }
+ }
+
+ apr_table_set(m->file_type_handlers, ext, CGIhdlr);
+ if (detach) {
+ apr_table_set(m->file_handler_mode, ext, "y");
+ }
+
+ return NULL;
+}
+
+static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
+ request_rec *r, apr_pool_t *p,
+ cgi_exec_info_t *e_info)
+{
+ char *ext = NULL;
+ char *cmd_only, *ptr;
+ const char *new_cmd;
+ netware_dir_config *d;
+ apr_file_t *fh;
+ const char *args = "";
+
+ d = (netware_dir_config *)ap_get_module_config(r->per_dir_config,
+ &netware_module);
+
+ if (e_info->process_cgi) {
+ /* Handle the complete file name, we DON'T want to follow suexec, since
+ * an unrooted command is as predictable as shooting craps in Win32.
+ *
+ * Notice that unlike most mime extension parsing, we have to use the
+ * win32 parsing here, therefore the final extension is the only one
+ * we will consider
+ */
+ *cmd = r->filename;
+ if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
+ args = r->args;
+ }
+ }
+
+ cmd_only = apr_pstrdup(p, *cmd);
+ e_info->cmd_type = APR_PROGRAM;
+
+ /* truncate any arguments from the cmd */
+ for (ptr = cmd_only; *ptr && (*ptr != ' '); ptr++);
+ *ptr = '\0';
+
+ /* Figure out what the extension is so that we can matche it. */
+ ext = strrchr(apr_filepath_name_get(cmd_only), '.');
+
+ /* If there isn't an extension then give it an empty string */
+ if (!ext) {
+ ext = "";
+ }
+
+ /* eliminate the '.' if there is one */
+ if (*ext == '.')
+ ++ext;
+
+ /* check if we have a registered command for the extension*/
+ new_cmd = apr_table_get(d->file_type_handlers, ext);
+ e_info->detached = AP_PROC_DETACHED;
+ if (new_cmd == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Could not find a command associated with the %s extension", ext);
+ return APR_EBADF;
+ }
+ if (stricmp(new_cmd, "OS")) {
+ /* If we have a registered command then add the file that was passed in as a
+ parameter to the registered command. */
+ *cmd = apr_pstrcat (p, new_cmd, " ", cmd_only, NULL);
+
+ /* Run in its own address space if specified */
+ if(apr_table_get(d->file_handler_mode, ext))
+ e_info->detached |= AP_PROC_NEWADDRSPACE;
+ }
+
+ /* Tokenize the full command string into its arguments */
+ apr_tokenize_to_argv(*cmd, (char***)argv, p);
+
+ /* The first argument should be the executible */
+ *cmd = ap_server_root_relative(p, *argv[0]);
+
+ return APR_SUCCESS;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ APR_REGISTER_OPTIONAL_FN(ap_cgi_build_command);
+}
+
+static const command_rec netware_cmds[] = {
+AP_INIT_TAKE23("CGIMapExtension", set_extension_map, NULL, OR_FILEINFO,
+ "Full path to the CGI NLM module followed by a file extension. If the "
+ "first parameter is set to \"OS\" then the following file extension is "
+ "treated as NLM. The optional parameter \"detach\" can be specified if "
+ "the NLM should be launched in its own address space."),
+{ NULL }
+};
+
+module AP_MODULE_DECLARE_DATA netware_module = {
+ STANDARD20_MODULE_STUFF,
+ create_netware_dir_config, /* create per-dir config */
+ merge_netware_dir_configs, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ netware_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
+
+#endif
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_nw_ssl.c b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_nw_ssl.c
new file mode 100644
index 00000000..3d45f149
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_nw_ssl.c
@@ -0,0 +1,1151 @@
+/* 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_tls.c - Apache SSL/TLS module for NetWare by Mike Gardiner.
+ *
+ * This module gives Apache the ability to do SSL/TLS with a minimum amount
+ * of effort. All of the SSL/TLS logic is already on NetWare versions 5 and
+ * above and is interfaced through WinSock on NetWare. As you can see in
+ * the code below SSL/TLS sockets can be created with three WinSock calls.
+ *
+ * To load, simply place the module in the modules directory under the main
+ * apache tree. Then add a "SecureListen" with two arguments. The first
+ * argument is an address and/or port. The second argument is the key pair
+ * name as created in ConsoleOne.
+ *
+ * Examples:
+ *
+ * SecureListen 443 "SSL CertificateIP"
+ * SecureListen 123.45.67.89:443 mycert
+ */
+
+#define WS_SSL
+
+#define MAX_ADDRESS 512
+#define MAX_KEY 80
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_core.h"
+#include "ap_listen.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_optional.h"
+
+#include <unilib.h>
+
+#ifndef SO_TLS_UNCLEAN_SHUTDOWN
+#define SO_TLS_UNCLEAN_SHUTDOWN 0
+#endif
+
+/* The ssl_var_lookup() optional function retrieves SSL environment
+ * variables. */
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+ (apr_pool_t *, server_rec *,
+ conn_rec *, request_rec *,
+ char *));
+
+/* An optional function which returns non-zero if the given connection
+ * is using SSL/TLS. */
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
+
+/* The ssl_proxy_enable() and ssl_engine_disable() optional functions
+ * are used by mod_proxy to enable use of SSL for outgoing
+ * connections. */
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+
+#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
+#define strNE(s1,s2) (strcmp(s1,s2) != 0)
+#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0)
+#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0)
+
+#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0)
+#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0)
+#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
+#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
+
+#define strIsEmpty(s) (s == NULL || s[0] == NUL)
+
+
+module AP_MODULE_DECLARE_DATA nwssl_module;
+
+typedef struct NWSSLSrvConfigRec NWSSLSrvConfigRec;
+typedef struct seclisten_rec seclisten_rec;
+typedef struct seclistenup_rec seclistenup_rec;
+typedef struct secsocket_data secsocket_data;
+
+struct seclisten_rec {
+ seclisten_rec *next;
+ struct sockaddr_in local_addr; /* local IP address and port */
+ int fd;
+ int used; /* Only used during restart */
+ char key[MAX_KEY];
+ int mutual;
+ char *addr;
+ apr_port_t port;
+};
+
+struct seclistenup_rec {
+ seclistenup_rec *next;
+ char key[MAX_KEY];
+ char *addr;
+ apr_port_t port;
+};
+
+struct NWSSLSrvConfigRec {
+ apr_table_t *sltable;
+ apr_table_t *slutable;
+ apr_pool_t *pPool;
+};
+
+struct secsocket_data {
+ apr_socket_t* csd;
+ int is_secure;
+};
+
+static apr_array_header_t *certlist = NULL;
+static unicode_t** certarray = NULL;
+static int numcerts = 0;
+static seclisten_rec* ap_seclisteners = NULL;
+static seclistenup_rec* ap_seclistenersup = NULL;
+
+#define get_nwssl_cfg(srv) (NWSSLSrvConfigRec *) ap_get_module_config(srv->module_config, &nwssl_module)
+
+
+static void build_cert_list (apr_pool_t *p)
+{
+ int i;
+ char **rootcerts = (char **)certlist->elts;
+
+ numcerts = certlist->nelts;
+ certarray = apr_palloc(p, sizeof(unicode_t*)*numcerts);
+
+ for (i = 0; i < numcerts; ++i) {
+ unicode_t *unistr;
+ unistr = (unicode_t*)apr_palloc(p, strlen(rootcerts[i])*4);
+ loc2uni (UNI_LOCAL_DEFAULT, unistr, rootcerts[i], 0, 2);
+ certarray[i] = unistr;
+ }
+}
+
+/*
+ * Parses a host of the form <address>[:port]
+ * :port is permitted if 'port' is not NULL
+ */
+static unsigned long parse_addr(const char *w, unsigned short *ports)
+{
+ struct hostent *hep;
+ unsigned long my_addr;
+ char *p;
+
+ p = strchr(w, ':');
+ if (ports != NULL) {
+ *ports = 0;
+ if (p != NULL && strcmp(p + 1, "*") != 0)
+ *ports = atoi(p + 1);
+ }
+
+ if (p != NULL)
+ *p = '\0';
+ if (strcmp(w, "*") == 0) {
+ if (p != NULL)
+ *p = ':';
+ return htonl(INADDR_ANY);
+ }
+
+ my_addr = apr_inet_addr((char *)w);
+ if (my_addr != INADDR_NONE) {
+ if (p != NULL)
+ *p = ':';
+ return my_addr;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ /* XXX Should be echoing by h_errno the actual failure, no?
+ * ap_log_error would be good here. Better yet - APRize.
+ */
+ fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w);
+ exit(1);
+ }
+
+ if (hep->h_addr_list[1]) {
+ fprintf(stderr, "Host %s has multiple addresses ---\n", w);
+ fprintf(stderr, "you must choose one explicitly for use as\n");
+ fprintf(stderr, "a secure port. Exiting!!!\n");
+ exit(1);
+ }
+
+ if (p != NULL)
+ *p = ':';
+
+ return ((struct in_addr *) (hep->h_addr))->s_addr;
+}
+
+static int find_secure_listener(seclisten_rec *lr)
+{
+ seclisten_rec *sl;
+
+ for (sl = ap_seclisteners; sl; sl = sl->next) {
+ if (!memcmp(&sl->local_addr, &lr->local_addr, sizeof(sl->local_addr))) {
+ sl->used = 1;
+ return sl->fd;
+ }
+ }
+ return -1;
+}
+
+static char *get_port_key(conn_rec *c)
+{
+ seclistenup_rec *sl;
+
+ for (sl = ap_seclistenersup; sl; sl = sl->next) {
+ if ((sl->port == (c->local_addr)->port) &&
+ ((strcmp(sl->addr, "0.0.0.0") == 0) || (strcmp(sl->addr, c->local_ip) == 0))) {
+ return sl->key;
+ }
+ }
+ return NULL;
+}
+
+static int make_secure_socket(apr_pool_t *pconf, const struct sockaddr_in *server,
+ char* key, int mutual, server_rec *sconf)
+{
+ int s;
+ int one = 1;
+ char addr[MAX_ADDRESS];
+ struct sslserveropts opts;
+ unsigned int optParam;
+ WSAPROTOCOL_INFO SecureProtoInfo;
+ int no = 1;
+
+ if (server->sin_addr.s_addr != htonl(INADDR_ANY))
+ apr_snprintf(addr, sizeof(addr), "address %s port %d",
+ inet_ntoa(server->sin_addr), ntohs(server->sin_port));
+ else
+ apr_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
+
+ /* note that because we're about to slack we don't use psocket */
+ memset(&SecureProtoInfo, 0, sizeof(WSAPROTOCOL_INFO));
+
+ SecureProtoInfo.iAddressFamily = AF_INET;
+ SecureProtoInfo.iSocketType = SOCK_STREAM;
+ SecureProtoInfo.iProtocol = IPPROTO_TCP;
+ SecureProtoInfo.iSecurityScheme = SECURITY_PROTOCOL_SSL;
+
+ s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ (LPWSAPROTOCOL_INFO)&SecureProtoInfo, 0, 0);
+
+ if (s == INVALID_SOCKET) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
+ "make_secure_socket: failed to get a socket for %s",
+ addr);
+ return -1;
+ }
+
+ if (!mutual) {
+ optParam = SO_SSL_ENABLE | SO_SSL_SERVER;
+
+ if (WSAIoctl(s, SO_SSL_SET_FLAGS, (char *)&optParam,
+ sizeof(optParam), NULL, 0, NULL, NULL, NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
+ "make_secure_socket: for %s, WSAIoctl: "
+ "(SO_SSL_SET_FLAGS)", addr);
+ return -1;
+ }
+ }
+
+ opts.cert = key;
+ opts.certlen = strlen(key);
+ opts.sidtimeout = 0;
+ opts.sidentries = 0;
+ opts.siddir = NULL;
+
+ if (WSAIoctl(s, SO_SSL_SET_SERVER, (char *)&opts, sizeof(opts),
+ NULL, 0, NULL, NULL, NULL) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
+ "make_secure_socket: for %s, WSAIoctl: "
+ "(SO_SSL_SET_SERVER)", addr);
+ return -1;
+ }
+
+ if (mutual) {
+ optParam = 0x07; // SO_SSL_AUTH_CLIENT
+
+ if(WSAIoctl(s, SO_SSL_SET_FLAGS, (char*)&optParam,
+ sizeof(optParam), NULL, 0, NULL, NULL, NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
+ "make_secure_socket: for %s, WSAIoctl: "
+ "(SO_SSL_SET_FLAGS)", addr);
+ return -1;
+ }
+ }
+
+ optParam = SO_TLS_UNCLEAN_SHUTDOWN;
+ WSAIoctl(s, SO_SSL_SET_FLAGS, (char *)&optParam, sizeof(optParam),
+ NULL, 0, NULL, NULL, NULL);
+
+ return s;
+}
+
+int convert_secure_socket(conn_rec *c, apr_socket_t *csd)
+{
+ int rcode;
+ struct tlsclientopts sWS2Opts;
+ struct nwtlsopts sNWTLSOpts;
+ struct sslserveropts opts;
+ unsigned long ulFlags;
+ SOCKET sock;
+ unicode_t keyFileName[60];
+
+ apr_os_sock_get(&sock, csd);
+
+ /* zero out buffers */
+ memset((char *)&sWS2Opts, 0, sizeof(struct tlsclientopts));
+ memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts));
+
+ /* turn on ssl for the socket */
+ ulFlags = (numcerts ? SO_TLS_ENABLE : SO_TLS_ENABLE | SO_TLS_BLIND_ACCEPT);
+ rcode = WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long),
+ NULL, 0, NULL, NULL, NULL);
+ if (SOCKET_ERROR == rcode)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
+ "Error: %d with ioctlsocket(flag SO_TLS_ENABLE)", WSAGetLastError());
+ return rcode;
+ }
+
+ ulFlags = SO_TLS_UNCLEAN_SHUTDOWN;
+ WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long),
+ NULL, 0, NULL, NULL, NULL);
+
+ /* setup the socket for SSL */
+ memset (&sWS2Opts, 0, sizeof(sWS2Opts));
+ memset (&sNWTLSOpts, 0, sizeof(sNWTLSOpts));
+ sWS2Opts.options = &sNWTLSOpts;
+
+ if (numcerts) {
+ sNWTLSOpts.walletProvider = WAL_PROV_DER; //the wallet provider defined in wdefs.h
+ sNWTLSOpts.TrustedRootList = certarray; //array of certs in UNICODE format
+ sNWTLSOpts.numElementsInTRList = numcerts; //number of certs in TRList
+ }
+ else {
+ /* setup the socket for SSL */
+ unicpy(keyFileName, L"SSL CertificateIP");
+ sWS2Opts.wallet = keyFileName; /* no client certificate */
+ sWS2Opts.walletlen = unilen(keyFileName);
+
+ sNWTLSOpts.walletProvider = WAL_PROV_KMO; //the wallet provider defined in wdefs.h
+ }
+
+ /* make the IOCTL call */
+ rcode = WSAIoctl(sock, SO_TLS_SET_CLIENT, &sWS2Opts,
+ sizeof(struct tlsclientopts), NULL, 0, NULL,
+ NULL, NULL);
+
+ /* make sure that it was successfull */
+ if(SOCKET_ERROR == rcode ){
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
+ "Error: %d with ioctl (SO_TLS_SET_CLIENT)", WSAGetLastError());
+ }
+ return rcode;
+}
+
+int SSLize_Socket(SOCKET socketHnd, char *key, request_rec *r)
+{
+ int rcode;
+ struct tlsserveropts sWS2Opts;
+ struct nwtlsopts sNWTLSOpts;
+ unicode_t SASKey[512];
+ unsigned long ulFlag;
+
+ memset((char *)&sWS2Opts, 0, sizeof(struct tlsserveropts));
+ memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts));
+
+
+ ulFlag = SO_TLS_ENABLE;
+ rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL);
+ if(rcode)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_ENABLE)", WSAGetLastError());
+ goto ERR;
+ }
+
+
+ ulFlag = SO_TLS_SERVER;
+ rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, sizeof(unsigned long),NULL, 0, NULL, NULL, NULL);
+
+ if(rcode)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_SERVER)", WSAGetLastError());
+ goto ERR;
+ }
+
+ loc2uni(UNI_LOCAL_DEFAULT, SASKey, key, 0, 0);
+
+ //setup the tlsserveropts struct
+ sWS2Opts.wallet = SASKey;
+ sWS2Opts.walletlen = unilen(SASKey);
+ sWS2Opts.sidtimeout = 0;
+ sWS2Opts.sidentries = 0;
+ sWS2Opts.siddir = NULL;
+ sWS2Opts.options = &sNWTLSOpts;
+
+ //setup the nwtlsopts structure
+
+ sNWTLSOpts.walletProvider = WAL_PROV_KMO;
+ sNWTLSOpts.keysList = NULL;
+ sNWTLSOpts.numElementsInKeyList = 0;
+ sNWTLSOpts.reservedforfutureuse = NULL;
+ sNWTLSOpts.reservedforfutureCRL = NULL;
+ sNWTLSOpts.reservedforfutureCRLLen = 0;
+ sNWTLSOpts.reserved1 = NULL;
+ sNWTLSOpts.reserved2 = NULL;
+ sNWTLSOpts.reserved3 = NULL;
+
+
+ rcode = WSAIoctl(socketHnd,
+ SO_TLS_SET_SERVER,
+ &sWS2Opts,
+ sizeof(struct tlsserveropts),
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL);
+ if(SOCKET_ERROR == rcode) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Error: %d with WSAIoctl(SO_TLS_SET_SERVER)", WSAGetLastError());
+ goto ERR;
+ }
+
+ERR:
+ return rcode;
+}
+
+static const char *set_secure_listener(cmd_parms *cmd, void *dummy,
+ const char *ips, const char* key,
+ const char* mutual)
+{
+ NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server);
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ char *ports, *addr;
+ unsigned short port;
+ seclisten_rec *new;
+
+
+ if (err != NULL)
+ return err;
+
+ ports = strchr(ips, ':');
+
+ if (ports != NULL) {
+ if (ports == ips)
+ return "Missing IP address";
+ else if (ports[1] == '\0')
+ return "Address must end in :<port-number>";
+
+ *(ports++) = '\0';
+ }
+ else {
+ ports = (char*)ips;
+ }
+
+ new = apr_pcalloc(cmd->pool, sizeof(seclisten_rec));
+ new->local_addr.sin_family = AF_INET;
+
+ if (ports == ips) {
+ new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr = apr_pstrdup(cmd->pool, "0.0.0.0");
+ }
+ else {
+ new->local_addr.sin_addr.s_addr = parse_addr(ips, NULL);
+ addr = apr_pstrdup(cmd->pool, ips);
+ }
+
+ port = atoi(ports);
+
+ if (!port)
+ return "Port must be numeric";
+
+ apr_table_add(sc->sltable, ports, addr);
+
+ new->local_addr.sin_port = htons(port);
+ new->fd = -1;
+ new->used = 0;
+ new->next = ap_seclisteners;
+ strcpy(new->key, key);
+ new->mutual = (mutual) ? 1 : 0;
+ new->addr = addr;
+ new->port = port;
+ ap_seclisteners = new;
+ return NULL;
+}
+
+static const char *set_secure_upgradeable_listener(cmd_parms *cmd, void *dummy,
+ const char *ips, const char* key)
+{
+ NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server);
+ seclistenup_rec *listen_node;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ char *ports, *addr;
+ unsigned short port;
+ seclistenup_rec *new;
+
+ if (err != NULL)
+ return err;
+
+ ports = strchr(ips, ':');
+
+ if (ports != NULL) {
+ if (ports == ips)
+ return "Missing IP address";
+ else if (ports[1] == '\0')
+ return "Address must end in :<port-number>";
+
+ *(ports++) = '\0';
+ }
+ else {
+ ports = (char*)ips;
+ }
+
+ if (ports == ips) {
+ addr = apr_pstrdup(cmd->pool, "0.0.0.0");
+ }
+ else {
+ addr = apr_pstrdup(cmd->pool, ips);
+ }
+
+ port = atoi(ports);
+
+ if (!port)
+ return "Port must be numeric";
+
+ apr_table_set(sc->slutable, ports, addr);
+
+ new = apr_pcalloc(cmd->pool, sizeof(seclistenup_rec));
+ new->next = ap_seclistenersup;
+ strcpy(new->key, key);
+ new->addr = addr;
+ new->port = port;
+ ap_seclistenersup = new;
+
+ return err;
+}
+
+static apr_status_t nwssl_socket_cleanup(void *data)
+{
+ ap_listen_rec* slr = (ap_listen_rec*)data;
+ ap_listen_rec* lr;
+
+ /* Remove our secure listener from the listener list */
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ /* slr is at the head of the list */
+ if (lr == slr) {
+ ap_listeners = slr->next;
+ break;
+ }
+ /* slr is somewhere in between or at the end*/
+ if (lr->next == slr) {
+ lr->next = slr->next;
+ break;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static const char *set_trusted_certs(cmd_parms *cmd, void *dummy, char *arg)
+{
+ char **ptr = (char **)apr_array_push(certlist);
+
+ *ptr = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+static int nwssl_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ ap_seclisteners = NULL;
+ ap_seclistenersup = NULL;
+ certlist = apr_array_make(pconf, 1, sizeof(char *));
+
+ return OK;
+}
+
+static int nwssl_pre_connection(conn_rec *c, void *csd)
+{
+
+ if (apr_table_get(c->notes, "nwconv-ssl")) {
+ convert_secure_socket(c, (apr_socket_t*)csd);
+ }
+ else {
+ secsocket_data *csd_data = apr_palloc(c->pool, sizeof(secsocket_data));
+
+ csd_data->csd = (apr_socket_t*)csd;
+ csd_data->is_secure = 0;
+ ap_set_module_config(c->conn_config, &nwssl_module, (void*)csd_data);
+ }
+
+ return OK;
+}
+
+static int nwssl_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ seclisten_rec* sl;
+ ap_listen_rec* lr;
+ apr_socket_t* sd;
+ apr_status_t status;
+ seclistenup_rec *slu;
+ int found;
+
+ for (sl = ap_seclisteners; sl != NULL; sl = sl->next) {
+ sl->fd = find_secure_listener(sl);
+
+ if (sl->fd < 0)
+ sl->fd = make_secure_socket(pconf, &sl->local_addr, sl->key, sl->mutual, s);
+
+ if (sl->fd >= 0) {
+ apr_os_sock_info_t sock_info;
+
+ sock_info.os_sock = &(sl->fd);
+ sock_info.local = (struct sockaddr*)&(sl->local_addr);
+ sock_info.remote = NULL;
+ sock_info.family = APR_INET;
+ sock_info.type = SOCK_STREAM;
+
+ apr_os_sock_make(&sd, &sock_info, pconf);
+
+ lr = apr_pcalloc(pconf, sizeof(ap_listen_rec));
+
+ if (lr) {
+ lr->sd = sd;
+ if ((status = apr_sockaddr_info_get(&lr->bind_addr, sl->addr, APR_UNSPEC, sl->port, 0,
+ pconf)) != APR_SUCCESS) {
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, status, pconf,
+ "alloc_listener: failed to set up sockaddr for %s:%d", sl->addr, sl->port);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ lr->next = ap_listeners;
+ ap_listeners = lr;
+ apr_pool_cleanup_register(pconf, lr, nwssl_socket_cleanup, apr_pool_cleanup_null);
+ }
+ } else {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ for (slu = ap_seclistenersup; slu; slu = slu->next) {
+ /* Check the listener list for a matching upgradeable listener */
+ found = 0;
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ if (slu->port == lr->bind_addr->port) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, plog,
+ "No Listen directive found for upgradeable listener %s:%d", slu->addr, slu->port);
+ }
+ }
+
+ build_cert_list(pconf);
+
+ return OK;
+}
+
+static void *nwssl_config_server_create(apr_pool_t *p, server_rec *s)
+{
+ NWSSLSrvConfigRec *new = apr_palloc(p, sizeof(NWSSLSrvConfigRec));
+ new->sltable = apr_table_make(p, 5);
+ new->slutable = apr_table_make(p, 5);
+ return new;
+}
+
+static void *nwssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
+{
+ NWSSLSrvConfigRec *base = (NWSSLSrvConfigRec *)basev;
+ NWSSLSrvConfigRec *add = (NWSSLSrvConfigRec *)addv;
+ NWSSLSrvConfigRec *merged = (NWSSLSrvConfigRec *)apr_palloc(p, sizeof(NWSSLSrvConfigRec));
+ return merged;
+}
+
+static int compare_ipports(void *rec, const char *key, const char *value)
+{
+ conn_rec *c = (conn_rec*)rec;
+
+ if (value &&
+ ((strcmp(value, "0.0.0.0") == 0) || (strcmp(value, c->local_ip) == 0)))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+static int isSecureConnEx (const server_rec *s, const conn_rec *c, const apr_table_t *t)
+{
+ char port[8];
+
+ itoa((c->local_addr)->port, port, 10);
+ if (!apr_table_do(compare_ipports, (void*)c, t, port, NULL))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int isSecureConn (const server_rec *s, const conn_rec *c)
+{
+ NWSSLSrvConfigRec *sc = get_nwssl_cfg(s);
+
+ return isSecureConnEx (s, c, sc->sltable);
+}
+
+static int isSecureConnUpgradeable (const server_rec *s, const conn_rec *c)
+{
+ NWSSLSrvConfigRec *sc = get_nwssl_cfg(s);
+
+ return isSecureConnEx (s, c, sc->slutable);
+}
+
+static int isSecure (const request_rec *r)
+{
+ return isSecureConn (r->server, r->connection);
+}
+
+static int isSecureUpgradeable (const request_rec *r)
+{
+ return isSecureConnUpgradeable (r->server, r->connection);
+}
+
+static int isSecureUpgraded (const request_rec *r)
+{
+ secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module);
+
+ return csd_data->is_secure;
+}
+
+static int nwssl_hook_Fixup(request_rec *r)
+{
+ int i;
+
+ if (!isSecure(r) && !isSecureUpgraded(r))
+ return DECLINED;
+
+ apr_table_set(r->subprocess_env, "HTTPS", "on");
+
+ return DECLINED;
+}
+
+static const char *nwssl_hook_http_method (const request_rec *r)
+{
+ if (isSecure(r) && !isSecureUpgraded(r))
+ return "https";
+
+ return NULL;
+}
+
+static apr_port_t nwssl_hook_default_port(const request_rec *r)
+{
+ if (isSecure(r))
+ return DEFAULT_HTTPS_PORT;
+
+ return 0;
+}
+
+int ssl_proxy_enable(conn_rec *c)
+{
+ apr_table_set(c->notes, "nwconv-ssl", "Y");
+
+ return 1;
+}
+
+int ssl_engine_disable(conn_rec *c)
+{
+ return 1;
+}
+
+static int ssl_is_https(conn_rec *c)
+{
+ secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(c->conn_config, &nwssl_module);
+
+ return isSecureConn (c->base_server, c) || (csd_data && csd_data->is_secure);
+}
+
+/* This function must remain safe to use for a non-SSL connection. */
+char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
+{
+ NWSSLSrvConfigRec *mc = get_nwssl_cfg(s);
+ const char *result;
+ BOOL resdup;
+ apr_time_exp_t tm;
+
+ result = NULL;
+ resdup = TRUE;
+
+ /*
+ * When no pool is given try to find one
+ */
+ if (p == NULL) {
+ if (r != NULL)
+ p = r->pool;
+ else if (c != NULL)
+ p = c->pool;
+ else
+ p = mc->pPool;
+ }
+
+ /*
+ * Request dependent stuff
+ */
+ if (r != NULL) {
+ switch (var[0]) {
+ case 'H':
+ case 'h':
+ if (strcEQ(var, "HTTP_USER_AGENT"))
+ result = apr_table_get(r->headers_in, "User-Agent");
+ else if (strcEQ(var, "HTTP_REFERER"))
+ result = apr_table_get(r->headers_in, "Referer");
+ else if (strcEQ(var, "HTTP_COOKIE"))
+ result = apr_table_get(r->headers_in, "Cookie");
+ else if (strcEQ(var, "HTTP_FORWARDED"))
+ result = apr_table_get(r->headers_in, "Forwarded");
+ else if (strcEQ(var, "HTTP_HOST"))
+ result = apr_table_get(r->headers_in, "Host");
+ else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
+ result = apr_table_get(r->headers_in, "Proxy-Connection");
+ else if (strcEQ(var, "HTTP_ACCEPT"))
+ result = apr_table_get(r->headers_in, "Accept");
+ else if (strcEQ(var, "HTTPS")) {
+ if (isSecure(r) || isSecureUpgraded(r))
+ result = "on";
+ else
+ result = "off";
+ }
+ else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
+ /* all other headers from which we are still not know about */
+ result = apr_table_get(r->headers_in, var+5);
+ break;
+
+ case 'R':
+ case 'r':
+ if (strcEQ(var, "REQUEST_METHOD"))
+ result = r->method;
+ else if (strcEQ(var, "REQUEST_SCHEME"))
+ result = ap_http_method(r);
+ else if (strcEQ(var, "REQUEST_URI"))
+ result = r->uri;
+ else if (strcEQ(var, "REQUEST_FILENAME"))
+ result = r->filename;
+ else if (strcEQ(var, "REMOTE_HOST"))
+ result = ap_get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_NAME, NULL);
+ else if (strcEQ(var, "REMOTE_IDENT"))
+ result = ap_get_remote_logname(r);
+ else if (strcEQ(var, "REMOTE_USER"))
+ result = r->user;
+ break;
+
+ case 'S':
+ case 's':
+ if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
+
+ if (strcEQ(var, "SERVER_ADMIN"))
+ result = r->server->server_admin;
+ else if (strcEQ(var, "SERVER_NAME"))
+ result = ap_get_server_name(r);
+ else if (strcEQ(var, "SERVER_PORT"))
+ result = apr_psprintf(p, "%u", ap_get_server_port(r));
+ else if (strcEQ(var, "SERVER_PROTOCOL"))
+ result = r->protocol;
+ else if (strcEQ(var, "SCRIPT_FILENAME"))
+ result = r->filename;
+ break;
+
+ default:
+ if (strcEQ(var, "PATH_INFO"))
+ result = r->path_info;
+ else if (strcEQ(var, "QUERY_STRING"))
+ result = r->args;
+ else if (strcEQ(var, "IS_SUBREQ"))
+ result = (r->main != NULL ? "true" : "false");
+ else if (strcEQ(var, "DOCUMENT_ROOT"))
+ result = ap_document_root(r);
+ else if (strcEQ(var, "AUTH_TYPE"))
+ result = r->ap_auth_type;
+ else if (strcEQ(var, "THE_REQUEST"))
+ result = r->the_request;
+ break;
+ }
+ }
+
+ /*
+ * Connection stuff
+ */
+ if (result == NULL && c != NULL) {
+
+ /* XXX-Can't get specific SSL info from NetWare */
+ /* SSLConnRec *sslconn = myConnConfig(c);
+ if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
+ && sslconn && sslconn->ssl)
+ result = ssl_var_lookup_ssl(p, c, var+4);*/
+
+ if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
+ result = NULL;
+ else if (strcEQ(var, "REMOTE_ADDR"))
+ result = c->remote_ip;
+ }
+
+ /*
+ * Totally independent stuff
+ */
+ if (result == NULL) {
+ if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
+ result = NULL;
+ /* XXX-Can't get specific SSL info from NetWare */
+ /*result = ssl_var_lookup_ssl_version(p, var+12);*/
+ else if (strcEQ(var, "SERVER_SOFTWARE"))
+ result = ap_get_server_version();
+ else if (strcEQ(var, "API_VERSION")) {
+ result = apr_itoa(p, MODULE_MAGIC_NUMBER);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "TIME_YEAR")) {
+ apr_time_exp_lt(&tm, apr_time_now());
+ result = apr_psprintf(p, "%02d%02d",
+ (tm.tm_year / 100) + 19, tm.tm_year % 100);
+ resdup = FALSE;
+ }
+#define MKTIMESTR(format, tmfield) \
+ apr_time_exp_lt(&tm, apr_time_now()); \
+ result = apr_psprintf(p, format, tm.tmfield); \
+ resdup = FALSE;
+ else if (strcEQ(var, "TIME_MON")) {
+ MKTIMESTR("%02d", tm_mon+1)
+ }
+ else if (strcEQ(var, "TIME_DAY")) {
+ MKTIMESTR("%02d", tm_mday)
+ }
+ else if (strcEQ(var, "TIME_HOUR")) {
+ MKTIMESTR("%02d", tm_hour)
+ }
+ else if (strcEQ(var, "TIME_MIN")) {
+ MKTIMESTR("%02d", tm_min)
+ }
+ else if (strcEQ(var, "TIME_SEC")) {
+ MKTIMESTR("%02d", tm_sec)
+ }
+ else if (strcEQ(var, "TIME_WDAY")) {
+ MKTIMESTR("%d", tm_wday)
+ }
+ else if (strcEQ(var, "TIME")) {
+ apr_time_exp_lt(&tm, apr_time_now());
+ result = apr_psprintf(p,
+ "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
+ (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ resdup = FALSE;
+ }
+ /* all other env-variables from the parent Apache process */
+ else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+ result = apr_table_get(r->notes, var+4);
+ if (result == NULL)
+ result = apr_table_get(r->subprocess_env, var+4);
+ if (result == NULL)
+ result = getenv(var+4);
+ }
+ }
+
+ if (result != NULL && resdup)
+ result = apr_pstrdup(p, result);
+ if (result == NULL)
+ result = "";
+ return (char *)result;
+}
+
+static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+
+{
+#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
+#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"
+#define CONNECTION_HEADER "Connection: Upgrade"
+ const char *upgrade;
+ const char *connection;
+ apr_bucket_brigade *upgradebb;
+ request_rec *r = f->r;
+ apr_socket_t *csd = NULL;
+ char *key;
+ unicode_t keyFileName[512];
+ int ret;
+ char *token_string;
+ char *token;
+ char *token_state;
+ secsocket_data *csd_data;
+
+ /* Just remove the filter, if it doesn't work the first time, it won't
+ * work at all for this request.
+ */
+ ap_remove_output_filter(f);
+
+ /* No need to ensure that this is a server with optional SSL, the filter
+ * is only inserted if that is true.
+ */
+
+ upgrade = apr_table_get(r->headers_in, "Upgrade");
+ if (upgrade == NULL) {
+ return ap_pass_brigade(f->next, bb);
+ }
+ token_string = apr_pstrdup(r->pool,upgrade);
+ token = apr_strtok(token_string,", ",&token_state);
+ while (token && strcmp(token,"TLS/1.0")) {
+ apr_strtok(NULL,", ",&token_state);
+ }
+ // "Upgrade: TLS/1.0" header not found, don't do Upgrade
+ if (!token) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ connection = apr_table_get(r->headers_in, "Connection");
+ token_string = apr_pstrdup(r->pool,connection);
+ token = apr_strtok(token_string,",",&token_state);
+ while (token && strcmp(token,"Upgrade")) {
+ apr_strtok(NULL,",",&token_state);
+ }
+ // "Connection: Upgrade" header not found, don't do Upgrade
+ if (!token) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ apr_table_unset(r->headers_out, "Upgrade");
+
+ if (r) {
+ csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module);
+ csd = csd_data->csd;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Unable to get upgradeable socket handle");
+ return ap_pass_brigade(f->next, bb);
+ }
+
+
+ if (r->method_number == M_OPTIONS) {
+ apr_bucket *b = NULL;
+ /* This is a mandatory SSL upgrade. */
+
+ upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+
+ ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF,
+ UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL);
+
+ b = apr_bucket_flush_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(upgradebb, b);
+ ap_pass_brigade(f->next, upgradebb);
+ }
+ else {
+ /* This is optional, and should be configurable, for now don't bother
+ * doing anything.
+ */
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ key = get_port_key(r->connection);
+
+ if (csd && key) {
+ int sockdes;
+ apr_os_sock_get(&sockdes, csd);
+
+
+ ret = SSLize_Socket(sockdes, key, r);
+ if (!ret) {
+ csd_data->is_secure = 1;
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Upgradeable socket handle not found");
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
+ "Awaiting re-negotiation handshake");
+
+ return ap_pass_brigade(f->next, bb);
+}
+
+static void ssl_hook_Insert_Filter(request_rec *r)
+{
+ NWSSLSrvConfigRec *sc = get_nwssl_cfg(r->server);
+
+ if (isSecureUpgradeable (r)) {
+ ap_add_output_filter("UPGRADE_FILTER", NULL, r, r->connection);
+ }
+}
+
+static const command_rec nwssl_module_cmds[] =
+{
+ AP_INIT_TAKE23("SecureListen", set_secure_listener, NULL, RSRC_CONF,
+ "specify an address and/or port with a key pair name.\n"
+ "Optional third parameter of MUTUAL configures the port for mutual authentication."),
+ AP_INIT_TAKE2("NWSSLUpgradeable", set_secure_upgradeable_listener, NULL, RSRC_CONF,
+ "specify an address and/or port with a key pair name, that can be upgraded to an SSL connection.\n"
+ "The address and/or port must have already be defined using a Listen directive."),
+ AP_INIT_ITERATE("NWSSLTrustedCerts", set_trusted_certs, NULL, RSRC_CONF,
+ "Adds trusted certificates that are used to create secure connections to proxied servers"),
+ {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
+
+ ap_hook_pre_config(nwssl_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_pre_connection(nwssl_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_config(nwssl_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_fixups(nwssl_hook_Fixup, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_http_method(nwssl_hook_http_method, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_default_port (nwssl_hook_default_port, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_filter (ssl_hook_Insert_Filter, NULL,NULL, APR_HOOK_MIDDLE);
+
+ APR_REGISTER_OPTIONAL_FN(ssl_is_https);
+ APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
+
+ APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+ APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
+}
+
+module AP_MODULE_DECLARE_DATA nwssl_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ nwssl_config_server_create, /* server config */
+ nwssl_config_server_merge, /* merge server config */
+ nwssl_module_cmds, /* command apr_table_t */
+ register_hooks
+};
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy.def
new file mode 100644
index 00000000..ab02a53c
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy.def
@@ -0,0 +1,6 @@
+EXPORT proxy_module
+EXPORT proxy_hook_scheme_handler
+EXPORT proxy_hook_canon_handler
+EXPORT ap_proxy_ssl_enable
+EXPORT ap_proxy_ssl_disable
+EXPORT proxy_run_fixups
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_connect.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_connect.def
new file mode 100644
index 00000000..13611140
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_connect.def
@@ -0,0 +1,4 @@
+EXPORT proxy_connect_module
+IMPORT proxy_module
+IMPORT proxy_hook_scheme_handler
+IMPORT proxy_hook_canon_handler
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_ftp.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_ftp.def
new file mode 100644
index 00000000..f2dba7d6
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_ftp.def
@@ -0,0 +1,4 @@
+EXPORT proxy_ftp_module
+IMPORT proxy_module
+IMPORT proxy_hook_scheme_handler
+IMPORT proxy_hook_canon_handler
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_http.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_http.def
new file mode 100644
index 00000000..b24358b1
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_proxy_http.def
@@ -0,0 +1,7 @@
+EXPORT proxy_http_module
+IMPORT proxy_module
+IMPORT proxy_hook_scheme_handler
+IMPORT proxy_run_fixups
+IMPORT proxy_hook_canon_handler
+IMPORT ap_proxy_ssl_enable
+IMPORT ap_proxy_ssl_disable
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_rewrite.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_rewrite.def
new file mode 100644
index 00000000..cfdcf6b1
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_rewrite.def
@@ -0,0 +1 @@
+EXPORT rewrite_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_speling.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_speling.def
new file mode 100644
index 00000000..3d45a6aa
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_speling.def
@@ -0,0 +1 @@
+EXPORT speling_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_status.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_status.def
new file mode 100644
index 00000000..9a5a32d4
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_status.def
@@ -0,0 +1,2 @@
+EXPORT status_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_unique_id.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_unique_id.def
new file mode 100644
index 00000000..0b72c1ec
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_unique_id.def
@@ -0,0 +1 @@
+EXPORT unique_id_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_usertrack.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_usertrack.def
new file mode 100644
index 00000000..7264c41e
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_usertrack.def
@@ -0,0 +1 @@
+EXPORT usertrack_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_vhost_alias.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_vhost_alias.def
new file mode 100644
index 00000000..574b85f9
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/mod_vhost_alias.def
@@ -0,0 +1,2 @@
+EXPORT vhost_alias_module
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/netware/moddavfs.def b/rubbos/app/httpd-2.0.64/modules/arch/netware/moddavfs.def
new file mode 100644
index 00000000..67ec3117
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/netware/moddavfs.def
@@ -0,0 +1 @@
+EXPORT dav_fs_module
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/.deps b/rubbos/app/httpd-2.0.64/modules/arch/win32/.deps
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/.deps
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile b/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile
new file mode 100644
index 00000000..7886ef35
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile
@@ -0,0 +1,8 @@
+top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64
+top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64
+srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/arch/win32
+builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/arch/win32
+VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/arch/win32
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile.in b/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile.in
new file mode 100644
index 00000000..7c5c149d
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/Makefile.in
@@ -0,0 +1,3 @@
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/config.m4 b/rubbos/app/httpd-2.0.64/modules/arch/win32/config.m4
new file mode 100644
index 00000000..25f7a850
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/config.m4
@@ -0,0 +1,11 @@
+dnl modules enabled in this directory by default
+
+dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
+
+APACHE_MODPATH_INIT(arch/win32)
+
+APACHE_MODULE(isapi, isapi extension support, , , no)
+
+APR_ADDTO(LT_LDFLAGS,-export-dynamic)
+
+APACHE_MODPATH_FINISH
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
new file mode 100644
index 00000000..859d5670
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.c
@@ -0,0 +1,1760 @@
+/* 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 */
+};
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.dsp b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.dsp
new file mode 100644
index 00000000..08cbda21
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.dsp
@@ -0,0 +1,132 @@
+# Microsoft Developer Studio Project File - Name="mod_isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_isapi - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_isapi.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_isapi.mak" CFG="mod_isapi - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_isapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_isapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_isapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_isapi_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_isapi.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_isapi.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_isapi.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_isapi.so /opt:ref
+
+!ELSEIF "$(CFG)" == "mod_isapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_isapi_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_isapi.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_isapi.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_isapi.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_isapi.so
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_isapi - Win32 Release"
+# Name "mod_isapi - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_isapi.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_isapi.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_isapi.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\build\win32\win32ver.awk
+
+!IF "$(CFG)" == "mod_isapi - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\..\build\win32\win32ver.awk
+
+".\mod_isapi.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ awk -f ../../../build/win32/win32ver.awk mod_isapi.so "isapi_module for Apache" ../../../include/ap_release.h > .\mod_isapi.rc
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "mod_isapi - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\..\build\win32\win32ver.awk
+
+".\mod_isapi.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ awk -f ../../../build/win32/win32ver.awk mod_isapi.so "isapi_module for Apache" ../../../include/ap_release.h > .\mod_isapi.rc
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.h b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.h
new file mode 100644
index 00000000..33524bcb
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_isapi.h
@@ -0,0 +1,271 @@
+/* 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.
+ */
+
+/**
+ * @file mod_isapi.h
+ * @brief ISAPI module extension to Apache
+ *
+ * @defgroup MOD_ISAPI mod_isapi
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#ifndef MOD_ISAPI_H
+#define MOD_ISAPI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The Version Information storage passed to a module on startup
+ * via the GetExtensionVersion() entry point.
+ */
+typedef struct HSE_VERSION_INFO {
+ apr_uint32_t dwExtensionVersion;
+ char lpszExtensionDesc[256];
+} HSE_VERSION_INFO;
+
+/* The startup entry point that must be exported by every ISAPI handler
+ */
+int APR_THREAD_FUNC GetExtensionVersion(HSE_VERSION_INFO *ver_info);
+typedef int (APR_THREAD_FUNC *PFN_GETEXTENSIONVERSION)(HSE_VERSION_INFO *ver_info);
+
+/* Our internal 'HCONN' representation, always opaque to the user.
+ */
+typedef struct isapi_cid isapi_cid;
+typedef struct isapi_cid *HCONN;
+
+/* Prototypes of the essential functions exposed by mod_isapi
+ * for the module to communicate with Apache.
+ */
+typedef int (APR_THREAD_FUNC
+ *PFN_GETSERVERVARIABLE)(HCONN cid,
+ char *variable_name,
+ void *buf_data,
+ apr_uint32_t *buf_size);
+typedef int (APR_THREAD_FUNC
+ *PFN_WRITECLIENT)(HCONN cid,
+ void *buf_data,
+ apr_uint32_t *buf_size,
+ apr_uint32_t flags);
+typedef int (APR_THREAD_FUNC
+ *PFN_READCLIENT)(HCONN cid,
+ void *buf_data,
+ apr_uint32_t *buf_size);
+typedef int (APR_THREAD_FUNC
+ *PFN_SERVERSUPPORTFUNCTION)(HCONN cid,
+ apr_uint32_t HSE_code,
+ void *buf_data,
+ apr_uint32_t *buf_size,
+ apr_uint32_t *flags);
+
+/* The ecb structure is passed on each invocation of the module
+ */
+typedef struct EXTENSION_CONTROL_BLOCK {
+ apr_uint32_t cbSize;
+ apr_uint32_t dwVersion;
+ HCONN ConnID;
+ apr_uint32_t dwHttpStatusCode;
+ char lpszLogData[80];
+ char *lpszMethod;
+ char *lpszQueryString;
+ char *lpszPathInfo;
+ char *lpszPathTranslated;
+ apr_uint32_t cbTotalBytes;
+ apr_uint32_t cbAvailable;
+ unsigned char *lpbData;
+ char *lpszContentType;
+
+ PFN_GETSERVERVARIABLE GetServerVariable;
+ PFN_WRITECLIENT WriteClient;
+ PFN_READCLIENT ReadClient;
+ PFN_SERVERSUPPORTFUNCTION ServerSupportFunction;
+} EXTENSION_CONTROL_BLOCK;
+
+/* Status/Headers structure to pass to HSE_SEND_HEADER_EX,
+ * an MS extension to ServerSupportFunction
+ */
+typedef struct HSE_SEND_HEADER_EX_INFO {
+ const char * pszStatus; /* HTTP status text, such as "200 OK" */
+ const char * pszHeader; /* HTTP header lines text, such as
+ * "Content-type: text/plain\r\n"
+ * "Content-Language: en\r\n"
+ * Note that (in spite of cchFoo lengths below)
+ * NULL characters will interfere in headers.
+ */
+ apr_uint32_t cchStatus; /* length of pszStatus text */
+ apr_uint32_t cchHeader; /* length of pszHeader text */
+ int fKeepConn; /* Ignored: used to set keep-alive status,
+ * but Apache follows the client's negotiated
+ * HTTP contract to decide.
+ */
+} HSE_SEND_HEADER_EX_INFO;
+
+/* Our only 'supported' MS extended flag bit for TransmitFile,
+ * HSE_IO_SEND_HEADERS indicates that Status+Headers are present
+ * in the pszStatusCode member of the HSE_TF_INFO structure.
+ */
+#define HSE_IO_SEND_HEADERS 8
+
+/* The remaining flags are MS extended flag bits that bear little
+ * relation to Apache; the rules that the Apache server obeys follow
+ * its own design and HTTP protocol filter rules.
+ *
+ * We do not support async, however, we fake it. If HSE_IO_SYNC is
+ * not passed, and a completion context was defined, we will invoke the
+ * completion function immediately following the transfer, and then
+ * return to the caller. If HSE_IO_SYNC is passed, there is no call
+ * neccessary to the completion context.
+ */
+#define HSE_IO_SYNC 1
+#define HSE_IO_ASYNC 2
+#define HSE_IO_DISCONNECT_AFTER_SEND 4
+#define HSE_IO_NODELAY 4096
+
+/* The Completion function prototype. This callback may be fixed with
+ * the HSE_REQ_IO_COMPLETION ServerSupportFunction call, or overriden
+ * for the HSE_REQ_TRANSMIT_FILE call.
+ */
+typedef void (APR_THREAD_FUNC *PFN_HSE_IO_COMPLETION)
+ (EXTENSION_CONTROL_BLOCK *ecb,
+ void *ctxt,
+ apr_uint32_t cbIO,
+ apr_uint32_t dwError);
+
+/* TransmitFile structure to pass to HSE_REQ_TRANSMIT_FILE, an MS extension
+ */
+typedef struct HSE_TF_INFO {
+ PFN_HSE_IO_COMPLETION pfnHseIO; /* Overrides the default setting of
+ * HSE_REQ_IO_COMPLETION if not NULL
+ */
+ void *pContext;
+ apr_os_file_t hFile; /* HANDLE/fd to transmit */
+ const char *pszStatusCode; /* Ignored if HSE_IO_SEND_HEADERS is
+ * not set. Includes HTTP status text
+ * plus header text lines, such as
+ * "200 OK\r\n"
+ * "Content-type: text/plain\r\n"
+ */
+ apr_uint32_t BytesToWrite; /* 0 is write-all */
+ apr_uint32_t Offset; /* File Offset */
+ void *pHead; /* Prefix with *pHead body text */
+ apr_uint32_t HeadLength; /* Length of *pHead body text */
+ void *pTail; /* Prefix with *pTail body text */
+ apr_uint32_t TailLength; /* Length of *pTail body text */
+ apr_uint32_t dwFlags; /* bit flags described above */
+} HSE_TF_INFO;
+
+typedef struct HSE_URL_MAPEX_INFO {
+ char lpszPath[260];
+ apr_uint32_t dwFlags;
+ apr_uint32_t cchMatchingPath;
+ apr_uint32_t cchMatchingURL;
+ apr_uint32_t dwReserved1;
+ apr_uint32_t dwReserved2;
+} HSE_URL_MAPEX_INFO;
+
+/* Original ISAPI ServerSupportFunction() HSE_code methods */
+#define HSE_REQ_SEND_URL_REDIRECT_RESP 1
+#define HSE_REQ_SEND_URL 2
+#define HSE_REQ_SEND_RESPONSE_HEADER 3
+#define HSE_REQ_DONE_WITH_SESSION 4
+
+/* MS Extented methods to ISAPI ServerSupportFunction() HSE_code */
+#define HSE_REQ_MAP_URL_TO_PATH 1001 /* Emulated */
+#define HSE_REQ_GET_SSPI_INFO 1002 /* Not Supported */
+#define HSE_APPEND_LOG_PARAMETER 1003 /* Supported */
+#define HSE_REQ_IO_COMPLETION 1005 /* Emulated */
+#define HSE_REQ_TRANSMIT_FILE 1006 /* Async Emulated */
+#define HSE_REQ_REFRESH_ISAPI_ACL 1007 /* Not Supported */
+#define HSE_REQ_IS_KEEP_CONN 1008 /* Supported */
+#define HSE_REQ_ASYNC_READ_CLIENT 1010 /* Emulated */
+/* Added with ISAPI 4.0 */
+#define HSE_REQ_GET_IMPERSONATION_TOKEN 1011 /* Not Supported */
+#define HSE_REQ_MAP_URL_TO_PATH_EX 1012 /* Emulated */
+#define HSE_REQ_ABORTIVE_CLOSE 1014 /* Ignored */
+/* Added after ISAPI 4.0 in IIS 5.0 */
+#define HSE_REQ_GET_CERT_INFO_EX 1015 /* Not Supported */
+#define HSE_REQ_SEND_RESPONSE_HEADER_EX 1016 /* Supported (no nulls!) */
+#define HSE_REQ_CLOSE_CONNECTION 1017 /* Ignored */
+#define HSE_REQ_IS_CONNECTED 1018 /* Supported */
+#define HSE_REQ_EXTENSION_TRIGGER 1020 /* Not Supported */
+
+/* The request entry point that must be exported by every ISAPI handler
+ */
+apr_uint32_t APR_THREAD_FUNC HttpExtensionProc(EXTENSION_CONTROL_BLOCK *ecb);
+typedef apr_uint32_t (APR_THREAD_FUNC
+ *PFN_HTTPEXTENSIONPROC)(EXTENSION_CONTROL_BLOCK *ecb);
+
+/* Allowable return values from HttpExtensionProc (apparently 0 is also
+ * accepted by MS IIS, and we will respect it as Success.)
+ * If the HttpExtensionProc returns HSE_STATUS_PENDING, we will create
+ * a wait mutex and lock on it, until HSE_REQ_DONE_WITH_SESSION is called.
+ */
+#define HSE_STATUS_SUCCESS 1
+#define HSE_STATUS_SUCCESS_AND_KEEP_CONN 2 /* 1 vs 2 Ignored, we choose */
+#define HSE_STATUS_PENDING 3 /* Emulated (thread lock) */
+#define HSE_STATUS_ERROR 4
+
+/* Anticipated error code for common faults within mod_isapi itself
+ */
+#ifndef ERROR_INSUFFICIENT_BUFFER
+#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
+#endif
+#ifndef ERROR_INVALID_INDEX
+#define ERROR_INVALID_INDEX EINVAL
+#endif
+#ifndef ERROR_INVALID_PARAMETER
+#define ERROR_INVALID_PARAMETER EINVAL
+#endif
+#ifndef ERROR_READ_FAULT
+#define ERROR_READ_FAULT EIO
+#endif
+#ifndef ERROR_WRITE_FAULT
+#define ERROR_WRITE_FAULT EIO
+#endif
+#ifndef ERROR_SUCCESS
+#define ERROR_SUCCESS 0
+#endif
+
+/* Valid flags passed with TerminateExtension()
+ */
+#define HSE_TERM_MUST_UNLOAD 1
+#define HSE_TERM_ADVISORY_UNLOAD 2
+
+/* The shutdown entry point óptionally exported by an ISAPI handler, passed
+ * HSE_TERM_MUST_UNLOAD or HSE_TERM_ADVISORY_UNLOAD. The module may return
+ * if passed HSE_TERM_ADVISORY_UNLOAD, and the module will remain loaded.
+ * If the module returns 1 to HSE_TERM_ADVISORY_UNLOAD it is immediately
+ * unloaded. If the module is passed HSE_TERM_MUST_UNLOAD, its return value
+ * is ignored.
+ */
+int APR_THREAD_FUNC TerminateExtension(apr_uint32_t flags);
+typedef int (APR_THREAD_FUNC *PFN_TERMINATEEXTENSION)(apr_uint32_t flags);
+
+/* Module may return 0 if passed HSE_TERM_ADVISORY_UNLOAD, and the module
+ * will remain loaded, or 1 if it consents to being unloaded. If the module
+ * is passed HSE_TERM_MUST_UNLOAD, it's return value is ignored.
+ */
+#define HSE_TERM_MUST_UNLOAD 1
+#define HSE_TERM_ADVISORY_UNLOAD 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !MOD_ISAPI_H */
+/** @} */
+
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_win32.c b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_win32.c
new file mode 100644
index 00000000..38b7b3b0
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/mod_win32.c
@@ -0,0 +1,553 @@
+/* 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.
+ */
+
+#ifdef WIN32
+
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_buckets.h"
+#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 "mod_cgi.h"
+#include "apr_lib.h"
+#include "ap_regkey.h"
+
+extern OSVERSIONINFO osver; /* hiding in mpm_winnt.c */
+static int win_nt;
+
+/*
+ * CGI Script stuff for Win32...
+ */
+typedef enum { eFileTypeUNKNOWN, eFileTypeBIN, eFileTypeEXE16, eFileTypeEXE32,
+ eFileTypeSCRIPT } file_type_e;
+typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY_STRICT,
+ INTERPRETER_SOURCE_REGISTRY, INTERPRETER_SOURCE_SHEBANG
+ } interpreter_source_e;
+AP_DECLARE(file_type_e) ap_get_win32_interpreter(const request_rec *,
+ char **interpreter,
+ char **arguments);
+
+module AP_MODULE_DECLARE_DATA win32_module;
+
+typedef struct {
+ /* Where to find interpreter to run scripts */
+ interpreter_source_e script_interpreter_source;
+} win32_dir_conf;
+
+static void *create_win32_dir_config(apr_pool_t *p, char *dir)
+{
+ win32_dir_conf *conf;
+ conf = (win32_dir_conf*)apr_palloc(p, sizeof(win32_dir_conf));
+ conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
+ return conf;
+}
+
+static void *merge_win32_dir_configs(apr_pool_t *p, void *basev, void *addv)
+{
+ win32_dir_conf *new;
+ win32_dir_conf *base = (win32_dir_conf *) basev;
+ win32_dir_conf *add = (win32_dir_conf *) addv;
+
+ new = (win32_dir_conf *) apr_pcalloc(p, sizeof(win32_dir_conf));
+ new->script_interpreter_source = (add->script_interpreter_source
+ != INTERPRETER_SOURCE_UNSET)
+ ? add->script_interpreter_source
+ : base->script_interpreter_source;
+ return new;
+}
+
+static const char *set_interpreter_source(cmd_parms *cmd, void *dv,
+ char *arg)
+{
+ win32_dir_conf *d = (win32_dir_conf *)dv;
+ if (!strcasecmp(arg, "registry")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+ }
+ else if (!strcasecmp(arg, "registry-strict")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY_STRICT;
+ }
+ else if (!strcasecmp(arg, "script")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ }
+ else {
+ return apr_pstrcat(cmd->temp_pool, "ScriptInterpreterSource \"", arg,
+ "\" must be \"registry\", \"registry-strict\" or "
+ "\"script\"", NULL);
+ }
+ return NULL;
+}
+
+/* XXX: prep_string should translate the string into unicode,
+ * such that it is compatible with whatever codepage the client
+ * will read characters 80-ff. For the moment, use the unicode
+ * values 0080-00ff. This isn't trivial, since the code page
+ * varies between msdos and Windows applications.
+ * For subsystem 2 [GUI] the default is the system Ansi CP.
+ * For subsystem 3 [CLI] the default is the system OEM CP.
+ */
+static void prep_string(const char ** str, apr_pool_t *p)
+{
+ const char *ch = *str;
+ char *ch2;
+ int widen = 0;
+
+ if (!ch) {
+ return;
+ }
+ while (*ch) {
+ if (*(ch++) & 0x80) {
+ ++widen;
+ }
+ }
+ if (!widen) {
+ return;
+ }
+ widen += (ch - *str) + 1;
+ ch = *str;
+ *str = ch2 = apr_palloc(p, widen);
+ while (*ch) {
+ if (*ch & 0x80) {
+ /* sign extension won't hurt us here */
+ *(ch2++) = 0xC0 | ((*ch >> 6) & 0x03);
+ *(ch2++) = 0x80 | (*(ch++) & 0x3f);
+ }
+ else {
+ *(ch2++) = *(ch++);
+ }
+ }
+ *(ch2++) = '\0';
+}
+
+/* Somewhat more exciting ... figure out where the registry has stashed the
+ * ExecCGI or Open command - it may be nested one level deep (or more???)
+ */
+static char* get_interpreter_from_win32_registry(apr_pool_t *p,
+ const char* ext,
+ int strict)
+{
+ apr_status_t rv;
+ ap_regkey_t *name_key = NULL;
+ ap_regkey_t *type_key;
+ ap_regkey_t *key;
+ char execcgi_path[] = "SHELL\\EXECCGI\\COMMAND";
+ char execopen_path[] = "SHELL\\OPEN\\COMMAND";
+ char *type_name;
+ char *buffer;
+
+ if (!ext) {
+ return NULL;
+ }
+ /*
+ * Future optimization:
+ * When the registry is successfully searched, store the strings for
+ * interpreter and arguments in an ext hash to speed up subsequent look-ups
+ */
+
+ /* Open the key associated with the script filetype extension */
+ rv = ap_regkey_open(&type_key, AP_REGKEY_CLASSES_ROOT, ext, APR_READ, p);
+
+ if (rv != APR_SUCCESS) {
+ return NULL;
+ }
+
+ /* Retrieve the name of the script filetype extension */
+ rv = ap_regkey_value_get(&type_name, type_key, "", p);
+
+ if (rv == APR_SUCCESS && type_name[0]) {
+ /* Open the key associated with the script filetype extension */
+ rv = ap_regkey_open(&name_key, AP_REGKEY_CLASSES_ROOT, type_name,
+ APR_READ, p);
+ }
+
+ /* Open the key for the script command path by:
+ *
+ * 1) the 'named' filetype key for ExecCGI/Command
+ * 2) the extension's type key for ExecCGI/Command
+ *
+ * and if the strict arg is false, then continue trying:
+ *
+ * 3) the 'named' filetype key for Open/Command
+ * 4) the extension's type key for Open/Command
+ */
+
+ if (name_key) {
+ if ((rv = ap_regkey_open(&key, name_key, execcgi_path, APR_READ, p))
+ == APR_SUCCESS) {
+ rv = ap_regkey_value_get(&buffer, key, "", p);
+ ap_regkey_close(name_key);
+ }
+ }
+
+ if (!name_key || (rv != APR_SUCCESS)) {
+ if ((rv = ap_regkey_open(&key, type_key, execcgi_path, APR_READ, p))
+ == APR_SUCCESS) {
+ rv = ap_regkey_value_get(&buffer, key, "", p);
+ ap_regkey_close(type_key);
+ }
+ }
+
+ if (!strict && name_key && (rv != APR_SUCCESS)) {
+ if ((rv = ap_regkey_open(&key, name_key, execopen_path, APR_READ, p))
+ == APR_SUCCESS) {
+ rv = ap_regkey_value_get(&buffer, key, "", p);
+ ap_regkey_close(name_key);
+ }
+ }
+
+ if (!strict && (rv != APR_SUCCESS)) {
+ if ((rv = ap_regkey_open(&key, type_key, execopen_path, APR_READ, p))
+ == APR_SUCCESS) {
+ rv = ap_regkey_value_get(&buffer, key, "", p);
+ ap_regkey_close(type_key);
+ }
+ }
+
+ if (name_key) {
+ ap_regkey_close(name_key);
+ }
+
+ ap_regkey_close(type_key);
+
+ if (rv != APR_SUCCESS || !buffer[0]) {
+ return NULL;
+ }
+
+ return buffer;
+}
+
+
+static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
+ const char *cgiprg, const char *cgiargs)
+{
+ apr_array_header_t *args = apr_array_make(p, 8, sizeof(char*));
+ char *d = apr_palloc(p, strlen(interp)+1);
+ const char *ch = interp;
+ const char **arg;
+ int prgtaken = 0;
+ int argtaken = 0;
+ int inquo;
+ int sl;
+
+ while (*ch) {
+ /* Skip on through Deep Space */
+ if (apr_isspace(*ch)) {
+ ++ch; continue;
+ }
+ /* One Arg */
+ if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '*')) {
+ const char *cgiarg = cgiargs;
+ argtaken = 1;
+ for (;;) {
+ char *w = ap_getword_nulls(p, &cgiarg, '+');
+ if (!*w) {
+ break;
+ }
+ ap_unescape_url(w);
+ if (win_nt) {
+ prep_string(&w, p);
+ }
+ arg = (const char**)apr_array_push(args);
+ *arg = ap_escape_shell_cmd(p, w);
+ }
+ ch += 2;
+ continue;
+ }
+ if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '1')) {
+ /* Todo: Make short name!!! */
+ prgtaken = 1;
+ arg = (const char**)apr_array_push(args);
+ if (*ch == '%') {
+ char *repl = apr_pstrdup(p, cgiprg);
+ *arg = repl;
+ while ((repl = strchr(repl, '/'))) {
+ *repl++ = '\\';
+ }
+ }
+ else {
+ *arg = cgiprg;
+ }
+ ch += 2;
+ continue;
+ }
+ if ((*ch == '\"') && ((*(ch + 1) == '$')
+ || (*(ch + 1) == '%')) && (*(ch + 2) == '1')
+ && (*(ch + 3) == '\"')) {
+ prgtaken = 1;
+ arg = (const char**)apr_array_push(args);
+ if (*(ch + 1) == '%') {
+ char *repl = apr_pstrdup(p, cgiprg);
+ *arg = repl;
+ while ((repl = strchr(repl, '/'))) {
+ *repl++ = '\\';
+ }
+ }
+ else {
+ *arg = cgiprg;
+ }
+ ch += 4;
+ continue;
+ }
+ arg = (const char**)apr_array_push(args);
+ *arg = d;
+ inquo = 0;
+ while (*ch) {
+ if (apr_isspace(*ch) && !inquo) {
+ ++ch; break;
+ }
+ /* Get 'em backslashes */
+ for (sl = 0; *ch == '\\'; ++sl) {
+ *d++ = *ch++;
+ }
+ if (sl & 1) {
+ /* last unmatched '\' + '"' sequence is a '"' */
+ if (*ch == '\"') {
+ *(d - 1) = *ch++;
+ }
+ continue;
+ }
+ if (*ch == '\"') {
+ /* '""' sequence within quotes is a '"' */
+ if (*++ch == '\"' && inquo) {
+ *d++ = *ch++; continue;
+ }
+ /* Flip quote state */
+ inquo = !inquo;
+ if (apr_isspace(*ch) && !inquo) {
+ ++ch; break;
+ }
+ /* All other '"'s are Munched */
+ continue;
+ }
+ /* Anything else is, well, something else */
+ *d++ = *ch++;
+ }
+ /* Term that arg, already pushed on args */
+ *d++ = '\0';
+ }
+
+ if (!prgtaken) {
+ arg = (const char**)apr_array_push(args);
+ *arg = cgiprg;
+ }
+
+ if (!argtaken) {
+ const char *cgiarg = cgiargs;
+ for (;;) {
+ char *w = ap_getword_nulls(p, &cgiarg, '+');
+ if (!*w) {
+ break;
+ }
+ ap_unescape_url(w);
+ if (win_nt) {
+ prep_string(&w, p);
+ }
+ arg = (const char**)apr_array_push(args);
+ *arg = ap_escape_shell_cmd(p, w);
+ }
+ }
+
+ arg = (const char**)apr_array_push(args);
+ *arg = NULL;
+
+ return args;
+}
+
+
+static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
+ request_rec *r, apr_pool_t *p,
+ cgi_exec_info_t *e_info)
+{
+ const apr_array_header_t *elts_arr = apr_table_elts(r->subprocess_env);
+ const apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts;
+ const char *ext = NULL;
+ const char *interpreter = NULL;
+ win32_dir_conf *d;
+ apr_file_t *fh;
+ const char *args = "";
+ int i;
+
+ d = (win32_dir_conf *)ap_get_module_config(r->per_dir_config,
+ &win32_module);
+
+ if (e_info->cmd_type) {
+ /* We have to consider that the client gets any QUERY_ARGS
+ * without any charset interpretation, use prep_string to
+ * create a string of the literal QUERY_ARGS bytes.
+ */
+ *cmd = r->filename;
+ if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
+ args = r->args;
+ }
+ }
+ /* Handle the complete file name, we DON'T want to follow suexec, since
+ * an unrooted command is as predictable as shooting craps in Win32.
+ * Notice that unlike most mime extension parsing, we have to use the
+ * win32 parsing here, therefore the final extension is the only one
+ * we will consider.
+ */
+ ext = strrchr(apr_filename_of_pathname(*cmd), '.');
+
+ /* If the file has an extension and it is not .com and not .exe and
+ * we've been instructed to search the registry, then do so.
+ * Let apr_proc_create do all of the .bat/.cmd dirty work.
+ */
+ if (ext && (!strcasecmp(ext,".exe") || !strcasecmp(ext,".com")
+ || !strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
+ interpreter = "";
+ }
+ if (!interpreter && ext
+ && (d->script_interpreter_source
+ == INTERPRETER_SOURCE_REGISTRY
+ || d->script_interpreter_source
+ == INTERPRETER_SOURCE_REGISTRY_STRICT)) {
+ /* Check the registry */
+ int strict = (d->script_interpreter_source
+ == INTERPRETER_SOURCE_REGISTRY_STRICT);
+ interpreter = get_interpreter_from_win32_registry(r->pool, ext,
+ strict);
+ if (interpreter && e_info->cmd_type != APR_SHELLCMD) {
+ e_info->cmd_type = APR_PROGRAM_PATH;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
+ strict ? "No ExecCGI verb found for files of type '%s'."
+ : "No ExecCGI or Open verb found for files of type '%s'.",
+ ext);
+ }
+ }
+ if (!interpreter) {
+ apr_status_t rv;
+ char buffer[1024];
+ apr_size_t bytes = sizeof(buffer);
+ int i;
+
+ /* Need to peek into the file figure out what it really is...
+ * ### aught to go back and build a cache for this one of these days.
+ */
+ if (((rv = apr_file_open(&fh, *cmd, APR_READ | APR_BUFFERED,
+ APR_OS_DEFAULT, r->pool)) != APR_SUCCESS)
+ || ((rv = apr_file_read(fh, buffer, &bytes)) != APR_SUCCESS)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "Failed to read cgi file %s for testing", *cmd);
+ return rv;
+ }
+ apr_file_close(fh);
+
+ /* Script or executable, that is the question... */
+ if ((buffer[0] == '#') && (buffer[1] == '!')) {
+ /* Assuming file is a script since it starts with a shebang */
+ for (i = 2; i < sizeof(buffer); i++) {
+ if ((buffer[i] == '\r') || (buffer[i] == '\n')) {
+ buffer[i] = '\0';
+ break;
+ }
+ }
+ if (i < sizeof(buffer)) {
+ interpreter = buffer + 2;
+ while (apr_isspace(*interpreter)) {
+ ++interpreter;
+ }
+ if (e_info->cmd_type != APR_SHELLCMD) {
+ e_info->cmd_type = APR_PROGRAM_PATH;
+ }
+ }
+ }
+ else {
+ /* Not a script, is it an executable? */
+ IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;
+ if ((bytes >= sizeof(IMAGE_DOS_HEADER))
+ && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) {
+ if (hdr->e_lfarlc < 0x40) {
+ /* Ought to invoke this 16 bit exe by a stub, (cmd /c?) */
+ interpreter = "";
+ }
+ else {
+ interpreter = "";
+ }
+ }
+ }
+ }
+ if (!interpreter) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "%s is not executable; ensure interpreted scripts have "
+ "\"#!\" first line", *cmd);
+ return APR_EBADF;
+ }
+
+ *argv = (const char **)(split_argv(p, interpreter, *cmd,
+ args)->elts);
+ *cmd = (*argv)[0];
+
+ e_info->detached = 1;
+
+ /* XXX: Must fix r->subprocess_env to follow utf-8 conventions from
+ * the client's octets so that win32 apr_proc_create is happy.
+ * The -best- way is to determine if the .exe is unicode aware
+ * (using 0x0080-0x00ff) or is linked as a command or windows
+ * application (following the OEM or Ansi code page in effect.)
+ */
+ for (i = 0; i < elts_arr->nelts; ++i) {
+ if (win_nt && elts[i].key && *elts[i].key
+ && (strncmp(elts[i].key, "HTTP_", 5) == 0
+ || strncmp(elts[i].key, "SERVER_", 7) == 0
+ || strncmp(elts[i].key, "REQUEST_", 8) == 0
+ || strcmp(elts[i].key, "QUERY_STRING") == 0
+ || strcmp(elts[i].key, "PATH_INFO") == 0
+ || strcmp(elts[i].key, "PATH_TRANSLATED") == 0)) {
+ prep_string((const char**) &elts[i].val, r->pool);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static int win32_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+ win_nt = (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
+ return OK;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ APR_REGISTER_OPTIONAL_FN(ap_cgi_build_command);
+ ap_hook_pre_config(win32_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+static const command_rec win32_cmds[] = {
+AP_INIT_TAKE1("ScriptInterpreterSource", set_interpreter_source, NULL,
+ OR_FILEINFO,
+ "Where to find interpreter to run Win32 scripts "
+ "(Registry or script shebang line)"),
+{ NULL }
+};
+
+module AP_MODULE_DECLARE_DATA win32_module = {
+ STANDARD20_MODULE_STUFF,
+ create_win32_dir_config, /* create per-dir config */
+ merge_win32_dir_configs, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ win32_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
+
+#endif /* defined WIN32 */
diff --git a/rubbos/app/httpd-2.0.64/modules/arch/win32/modules.mk b/rubbos/app/httpd-2.0.64/modules/arch/win32/modules.mk
new file mode 100644
index 00000000..ceb52a1b
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/arch/win32/modules.mk
@@ -0,0 +1,3 @@
+DISTCLEAN_TARGETS = modules.mk
+static =
+shared =