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 }, };