stoddard 99/02/04 16:37:50
Modified: src/include http_core.h
src/main util_script.c http_core.c
Log:
Add capability to search Win32 registry for script interpreters.
Revision Changes Path
1.53 +15 -0 apache-1.3/src/include/http_core.h
Index: http_core.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/include/http_core.h,v
retrieving revision 1.52
retrieving revision 1.53
diff -u -r1.52 -r1.53
--- http_core.h 1999/01/01 19:04:40 1.52
+++ http_core.h 1999/02/05 00:37:48 1.53
@@ -152,6 +152,16 @@
API_EXPORT(int) ap_satisfies (request_rec *r);
API_EXPORT(const array_header *) ap_requires (request_rec *);
+#ifdef WIN32
+/*
+ * CGI Script stuff for Win32...
+ */
+typedef enum { FileTypeUNKNOWN, FileTypeBIN, FileTypeEXE, FileTypeSCRIPT }
file_type_e;
+typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY,
+ INTERPRETER_SOURCE_SHEBANG } interpreter_source_e;
+API_EXPORT(file_type_e) ap_get_win32_interpreter(const request_rec *, char*,
char **);
+#endif
+
#ifdef CORE_PRIVATE
/*
@@ -248,6 +258,11 @@
array_header *sec;
regex_t *r;
+#ifdef WIN32
+ /* Where to find interpreter to run scripts */
+ interpreter_source_e script_interpreter_source;
+#endif
+
} core_dir_config;
/* Per-server core configuration */
1.138 +104 -201 apache-1.3/src/main/util_script.c
Index: util_script.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/util_script.c,v
retrieving revision 1.137
retrieving revision 1.138
diff -u -r1.137 -r1.138
--- util_script.c 1999/01/01 19:04:54 1.137
+++ util_script.c 1999/02/05 00:37:48 1.138
@@ -820,31 +820,31 @@
}
#elif defined(WIN32)
{
- /* Adapted from Alec Kloss' work for OS/2 */
- int is_script = 0;
- int is_binary = 0;
- char interpreter[2048]; /* hope it's enough for the interpreter path */
- FILE *program;
- int i, sz;
- char *dot;
- char *exename;
+ /* Adapted from Alec Kloss' work for OS/2 */
+ char *interpreter = NULL;
+ char *arguments = NULL;
+ char *ext = NULL;
+ char *exename = NULL;
+ char *s = NULL;
char *quoted_filename;
- int is_exe = 0;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
char *pCommand;
char *pEnvBlock, *pNext;
+
+ int i;
int iEnvBlockLen;
+
+ file_type_e fileType;
- memset(&si, 0, sizeof(si));
- memset(&pi, 0, sizeof(pi));
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
- interpreter[0] = 0;
- pid = -1;
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
- quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL);
+ pid = -1;
if (!shellcmd) {
+ /* Find the file name */
exename = strrchr(r->filename, '/');
if (!exename) {
exename = strrchr(r->filename, '\\');
@@ -855,66 +855,88 @@
else {
exename++;
}
- dot = strrchr(exename, '.');
- if (dot) {
- if (!strcasecmp(dot, ".BAT")
- || !strcasecmp(dot, ".CMD")
- || !strcasecmp(dot, ".EXE")
- || !strcasecmp(dot, ".COM")) {
- is_exe = 1;
- }
+
+ ext = strrchr(exename, '.');
+ if ((ext) && (!strcasecmp(ext,".bat") ||
+ !strcasecmp(ext,".cmd"))) {
+ fileType = FileTypeEXE;
+ }
+ else if ((ext) && (!strcasecmp(ext,".exe") ||
+ !strcasecmp(ext,".com"))) {
+ /* 16 bit or 32 bit? */
+ fileType = FileTypeEXE;
+ }
+ else {
+ /* Maybe a script or maybe a binary.. */
+ fileType = ap_get_win32_interpreter(r, ext, &interpreter);
}
- if (!is_exe) {
- program = fopen(r->filename, "rb");
- if (!program) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "fopen(%s) failed", r->filename);
- return (pid);
- }
- sz = fread(interpreter, 1, sizeof(interpreter) - 1, program);
- if (sz < 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "fread of %s failed", r->filename);
- fclose(program);
- return (pid);
- }
- interpreter[sz] = 0;
- fclose(program);
- if (!strncmp(interpreter, "#!", 2)) {
- is_script = 1;
- for (i = 2; i < sizeof(interpreter); i++) {
- if ((interpreter[i] == '\r')
- || (interpreter[i] == '\n')) {
- break;
- }
- }
- interpreter[i] = 0;
- for (i = 2; interpreter[i] == ' '; ++i)
- ;
-
memmove(interpreter+2,interpreter+i,strlen(interpreter+i)+1);
- }
- else {
- /* Check to see if it's a executable */
- IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)interpreter;
- if (hdr->e_magic == IMAGE_DOS_SIGNATURE && hdr->e_cblp <
512) {
- is_binary = 1;
+ if (fileType == FileTypeUNKNOWN) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+ "%s is not executable; ensure interpreted
scripts have "
+ "\"#!\" first line",
+ r->filename);
+ return (pid);
+ }
+
+ /*
+ * Look at the arguments...
+ */
+ arguments = "";
+ if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) {
+ /* If we are in this leg, there are some other arguments
+ * that we must include in the execution of the CGI.
+ * Because CreateProcess is the way it is, we have to
+ * create a command line like format for the execution
+ * of the CGI. This means we need to create on long
+ * string with the executable and arguments.
+ *
+ * The arguments string comes in the request structure,
+ * and each argument is separated by a '+'. We'll replace
+ * these pluses with spaces.
+ */
+
+ int iStringSize = 0;
+ int x;
+
+ /*
+ * Duplicate the request structure string so we don't
change it.
+ */
+ arguments = ap_pstrdup(r->pool, r->args);
+
+ /*
+ * Change the '+' to ' '
+ */
+ for (x=0; arguments[x]; x++) {
+ if ('+' == arguments[x]) {
+ arguments[x] = ' ';
}
}
+
+ /*
+ * We need to unescape any characters that are
+ * in the arguments list.
+ */
+ ap_unescape_url(arguments);
+ arguments = ap_escape_shell_cmd(r->pool, arguments);
}
- /* Bail out if we haven't figured out what kind of
- * file this is by now..
+
+ /*
+ * We have the interpreter (if there is one) and we have
+ * the arguments (if there are any).
+ * Build the command string to pass to CreateProcess.
*/
- if (!is_exe && !is_script && !is_binary) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
- "%s is not executable; ensure interpreted
scripts have "
- "\"#!\" first line",
- r->filename);
- return (pid);
+ quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"",
NULL);
+ if (interpreter && *interpreter) {
+ pCommand = ap_pstrcat(r->pool, interpreter, " ",
+ quoted_filename, " ", arguments, NULL);
}
- }
+ else {
+ pCommand = ap_pstrcat(r->pool, quoted_filename, " ",
arguments, NULL);
+ }
- if (shellcmd) {
+ } else {
+
char *shell_cmd = "CMD.EXE /C ";
OSVERSIONINFO osver;
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
@@ -929,96 +951,17 @@
}
pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL);
}
- else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
- if (is_exe || is_binary) {
- /*
- * When the CGI is a straight binary executable,
- * we can run it as is
- */
- pCommand = quoted_filename;
- }
- else if (is_script) {
- /* When an interpreter is needed, we need to create
- * a command line that has the interpreter name
- * followed by the CGI script name.
- */
- pCommand = ap_pstrcat(r->pool, interpreter + 2, " ",
- quoted_filename, NULL);
- }
- else {
- /* If not an executable or script, just execute it
- * from a command prompt.
- */
- pCommand = ap_pstrcat(r->pool, SHELL_PATH, " /C ",
- quoted_filename, NULL);
- }
- }
- else {
-
- /* If we are in this leg, there are some other arguments
- * that we must include in the execution of the CGI.
- * Because CreateProcess is the way it is, we have to
- * create a command line like format for the execution
- * of the CGI. This means we need to create on long
- * string with the executable and arguments.
- *
- * The arguments string comes in the request structure,
- * and each argument is separated by a '+'. We'll replace
- * these pluses with spaces.
- */
- char *arguments=NULL;
- int iStringSize = 0;
- int x;
-
- /*
- * Duplicate the request structure string so we don't change it.
- */
- arguments = ap_pstrdup(r->pool, r->args);
-
- /*
- * Change the '+' to ' '
- */
- for (x=0; arguments[x]; x++) {
- if ('+' == arguments[x]) {
- arguments[x] = ' ';
- }
- }
-
- /*
- * We need to unescape any characters that are
- * in the arguments list.
- */
- ap_unescape_url(arguments);
- arguments = ap_escape_shell_cmd(r->pool, arguments);
-
- /*
- * The argument list should now be good to use,
- * so now build the command line.
- */
- if (is_exe || is_binary) {
- pCommand = ap_pstrcat(r->pool, quoted_filename, " ",
- arguments, NULL);
- }
- else if (is_script) {
- pCommand = ap_pstrcat(r->pool, interpreter + 2, " ",
- quoted_filename, " ", arguments, NULL);
- }
- else {
- pCommand = ap_pstrcat(r->pool, SHELL_PATH, " /C ",
- quoted_filename, " ", arguments, NULL);
- }
- }
-
- /*
- * Make child process use hPipeOutputWrite as standard out,
- * and make sure it does not show on screen.
- */
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
- si.wShowWindow = SW_HIDE;
- si.hStdInput = pinfo->hPipeInputRead;
- si.hStdOutput = pinfo->hPipeOutputWrite;
- si.hStdError = pinfo->hPipeErrorWrite;
+
+ /*
+ * Make child process use hPipeOutputWrite as standard out,
+ * and make sure it does not show on screen.
+ */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_HIDE;
+ si.hStdInput = pinfo->hPipeInputRead;
+ si.hStdOutput = pinfo->hPipeOutputWrite;
+ si.hStdError = pinfo->hPipeErrorWrite;
/*
* Win32's CreateProcess call requires that the environment
@@ -1052,50 +995,10 @@
*/
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
- } else {
- if (is_script) {
- /* since we are doing magic to find what we are executing
- * if running a script, log what we think we should have
- * executed
- */
- ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, r,
- "could not run script interpreter: %s", pCommand);
- }
- }
-#if 0
- if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
- if (is_exe || is_binary) {
- pid = spawnle(_P_NOWAIT, r->filename, r->filename, NULL, env);
- }
- else if (is_script) {
- pid = spawnle(_P_NOWAIT, interpreter + 2, interpreter + 2,
- r->filename, NULL, env);
- }
- else {
- pid = spawnle(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/C",
- r->filename, NULL, env);
- }
- }
- else {
- if (is_exe || is_binary) {
- pid = spawnve(_P_NOWAIT, r->filename,
- create_argv(r->pool, NULL, NULL, NULL, argv0,
- r->args), env);
- }
- else if (is_script) {
- pid = spawnve(_P_NOWAIT, interpreter + 2,
- create_argv(r->pool, interpreter + 2, NULL, NULL,
- r->filename, r->args), env);
- }
- else {
- pid = spawnve(_P_NOWAIT, SHELL_PATH,
- create_argv_cmd(r->pool, argv0, r->args,
- r->filename), env);
- }
- }
-#endif
- return (pid);
+ }
+ return (pid);
}
+
#else
if (ap_suexec_enabled
&& ((r->server->server_uid != ap_user_id)
1.245 +193 -1 apache-1.3/src/main/http_core.c
Index: http_core.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_core.c,v
retrieving revision 1.244
retrieving revision 1.245
diff -u -r1.244 -r1.245
--- http_core.c 1999/01/07 20:46:58 1.244
+++ http_core.c 1999/02/05 00:37:49 1.245
@@ -145,7 +145,9 @@
conf->limit_req_body = 0;
conf->sec = ap_make_array(a, 2, sizeof(void *));
-
+#ifdef WIN32
+ conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
+#endif
return (void *)conf;
}
@@ -262,6 +264,13 @@
if (new->satisfy != SATISFY_NOSPEC) {
conf->satisfy = new->satisfy;
}
+
+#ifdef WIN32
+ if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) {
+ conf->script_interpreter_source = new->script_interpreter_source;
+ }
+#endif
+
return (void*)conf;
}
@@ -725,6 +734,174 @@
return d->limit_req_body;
}
+#ifdef WIN32
+static char* get_interpreter_from_win32_registry(pool *p, const char* ext)
+{
+ char extension_path[] = "SOFTWARE\\Classes\\";
+ char executable_path[] = "\\SHELL\\OPEN\\COMMAND";
+
+ HKEY hkeyOpen;
+ DWORD type;
+ int size;
+ int result;
+ char *keyName;
+ char *buffer;
+ char *s;
+
+ if (!ext)
+ return NULL;
+ /*
+ * Future optimization:
+ * When the registry is successfully searched, store the interpreter
+ * string in a table to make subsequent look-ups faster
+ */
+
+ /* Open the key associated with the script extension */
+ keyName = ap_pstrcat(p, extension_path, ext, NULL);
+
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE,
+ &hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Read to NULL buffer to find value size */
+ size = 0;
+ result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size);
+
+ if (result == ERROR_SUCCESS) {
+ buffer = ap_palloc(p, size);
+ result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size);
+ }
+
+ RegCloseKey(hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Open the key associated with the interpreter path */
+ keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL);
+
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE,
+ &hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Read to NULL buffer to find value size */
+ size = 0;
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+
+ if (result == ERROR_SUCCESS) {
+ buffer = ap_palloc(p, size);
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size);
+ }
+
+ RegCloseKey(hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /*
+ * The canonical way shell command entries are entered in the Win32
+ * registry is as follows:
+ * shell [options] "%1"
+ * where
+ * shell - full path name to interpreter or shell to run.
+ * E.g., c:\usr\local\ntreskit\perl\bin\perl.exe
+ * options - optional switches
+ * E.g., \C
+ * "%1" - Place holder for file to run the shell against.
+ * Typically quoted.
+ *
+ * If we find a %1 or a quoted %1, lop it off.
+ */
+ if (buffer && *buffer) {
+ if ((s = strstr(buffer, "\"%1")))
+ *s = '\0';
+ else if ((s = strstr(buffer, "%1")))
+ *s = '\0';
+ }
+
+ return buffer;
+}
+
+API_EXPORT (file_type_e) ap_get_win32_interpreter(const request_rec *r,
+ char* ext,
+ char** interpreter )
+{
+ HANDLE hFile;
+ DWORD nBytesRead;
+ BOOLEAN bResult;
+ char buffer[1024];
+ core_dir_config *d;
+ file_type_e fileType = FileTypeUNKNOWN;
+ int i;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) {
+ /*
+ * Check the registry
+ */
+ *interpreter = get_interpreter_from_win32_registry(r->pool, ext);
+ if (*interpreter)
+ return FileTypeSCRIPT;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r->server,
+ "Win32InterpreterSource config directive set to
\"registry\".\n\t"
+ "Registry was searched but interpreter not found. Trying the
shebang line.");
+ }
+ }
+
+ /*
+ * Look for a #! line in the script
+ */
+ hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return FileTypeUNKNOWN;
+ }
+
+ bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer),
+ &nBytesRead, NULL);
+ if (!bResult || (nBytesRead == 0)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ReadFile(%s) failed", r->filename);
+ CloseHandle(hFile);
+ return (FileTypeUNKNOWN);
+ }
+ CloseHandle(hFile);
+
+ buffer[nBytesRead] = '\0';
+
+ if ((buffer[0] == '#') && (buffer[1] == '!')) {
+ fileType = FileTypeSCRIPT;
+ for (i = 2; i < sizeof(buffer); i++) {
+ if ((buffer[i] == '\r')
+ || (buffer[i] == '\n')) {
+ break;
+ }
+ }
+ buffer[i] = '\0';
+ for (i = 2; buffer[i] == ' '; ++i)
+ ;
+ *interpreter = ap_pstrdup(r->pool, buffer + i );
+ }
+ else {
+ /* Check to see if it's a executable */
+ IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)interpreter;
+ if (hdr->e_magic == IMAGE_DOS_SIGNATURE && hdr->e_cblp < 512) {
+ fileType = FileTypeEXE;
+ }
+ }
+
+ return fileType;
+}
+#endif
+
/*****************************************************************
*
* Commands... this module handles almost all of the NCSA httpd.conf
@@ -2452,6 +2629,19 @@
return NULL;
}
+static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ if (!strcasecmp(arg, "registry")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+ } else if (!strcasecmp(arg, "shebang")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ } else {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ }
+ return NULL;
+}
+
/* Note --- ErrorDocument will now work from .htaccess files.
* The AllowOverride of Fileinfo allows webmasters to turn it off
*/
@@ -2668,6 +2858,8 @@
(void*)XtOffsetOf(core_dir_config, limit_req_body),
OR_ALL, TAKE1,
"Limit (in bytes) on maximum size of request message body" },
+{ "Win32InterpreterSource", set_interpreter_source, NULL, OR_FILEINFO, TAKE1,
+ "Where to find interpreter to run Win32 scripts (Registry or script
shebang line)" },
{ NULL },
};