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

Reply via email to