upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / arch / win32 / mod_win32.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifdef WIN32
18
19 #include "apr_strings.h"
20 #include "apr_portable.h"
21 #include "apr_buckets.h"
22 #include "ap_config.h"
23 #include "httpd.h"
24 #include "http_config.h"
25 #include "http_core.h"
26 #include "http_protocol.h"
27 #include "http_request.h"
28 #include "http_log.h"
29 #include "util_script.h"
30 #include "mod_core.h"
31 #include "mod_cgi.h"
32 #include "apr_lib.h"
33 #include "ap_regkey.h"
34
35 extern OSVERSIONINFO osver; /* hiding in mpm_winnt.c */
36 static int win_nt;
37
38 /* 
39  * CGI Script stuff for Win32...
40  */
41 typedef enum { eFileTypeUNKNOWN, eFileTypeBIN, eFileTypeEXE16, eFileTypeEXE32, 
42                eFileTypeSCRIPT } file_type_e;
43 typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY_STRICT, 
44                INTERPRETER_SOURCE_REGISTRY, INTERPRETER_SOURCE_SHEBANG 
45              } interpreter_source_e;
46 AP_DECLARE(file_type_e) ap_get_win32_interpreter(const request_rec *, 
47                                                  char **interpreter,
48                                                  char **arguments);
49
50 module AP_MODULE_DECLARE_DATA win32_module;
51
52 typedef struct {
53     /* Where to find interpreter to run scripts */
54     interpreter_source_e script_interpreter_source;
55 } win32_dir_conf;
56
57 static void *create_win32_dir_config(apr_pool_t *p, char *dir)
58 {
59     win32_dir_conf *conf;
60     conf = (win32_dir_conf*)apr_palloc(p, sizeof(win32_dir_conf));
61     conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
62     return conf;
63 }
64
65 static void *merge_win32_dir_configs(apr_pool_t *p, void *basev, void *addv)
66 {
67     win32_dir_conf *new;
68     win32_dir_conf *base = (win32_dir_conf *) basev;
69     win32_dir_conf *add = (win32_dir_conf *) addv;
70
71     new = (win32_dir_conf *) apr_pcalloc(p, sizeof(win32_dir_conf));
72     new->script_interpreter_source = (add->script_interpreter_source 
73                                            != INTERPRETER_SOURCE_UNSET)
74                                    ? add->script_interpreter_source 
75                                    : base->script_interpreter_source;
76     return new;
77 }
78
79 static const char *set_interpreter_source(cmd_parms *cmd, void *dv,
80                                           char *arg)
81 {
82     win32_dir_conf *d = (win32_dir_conf *)dv;
83     if (!strcasecmp(arg, "registry")) {
84         d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
85     }
86     else if (!strcasecmp(arg, "registry-strict")) {
87         d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY_STRICT;
88     }
89     else if (!strcasecmp(arg, "script")) {
90         d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
91     }
92     else {
93         return apr_pstrcat(cmd->temp_pool, "ScriptInterpreterSource \"", arg, 
94                            "\" must be \"registry\", \"registry-strict\" or "
95                            "\"script\"", NULL);
96     }
97     return NULL;
98 }
99
100 /* XXX: prep_string should translate the string into unicode,
101  * such that it is compatible with whatever codepage the client
102  * will read characters 80-ff.  For the moment, use the unicode
103  * values 0080-00ff.  This isn't trivial, since the code page
104  * varies between msdos and Windows applications.
105  * For subsystem 2 [GUI] the default is the system Ansi CP.
106  * For subsystem 3 [CLI] the default is the system OEM CP.
107  */ 
108 static void prep_string(const char ** str, apr_pool_t *p)
109 {
110     const char *ch = *str;
111     char *ch2;
112     int widen = 0;
113
114     if (!ch) {
115         return;
116     }
117     while (*ch) {
118         if (*(ch++) & 0x80) {
119             ++widen;
120         }
121     }
122     if (!widen) {
123         return;
124     }
125     widen += (ch - *str) + 1;
126     ch = *str;
127     *str = ch2 = apr_palloc(p, widen);
128     while (*ch) {
129         if (*ch & 0x80) {
130             /* sign extension won't hurt us here */
131             *(ch2++) = 0xC0 | ((*ch >> 6) & 0x03);
132             *(ch2++) = 0x80 | (*(ch++) & 0x3f);
133         }
134         else {
135             *(ch2++) = *(ch++);
136         }
137     }
138     *(ch2++) = '\0';
139 }
140
141 /* Somewhat more exciting ... figure out where the registry has stashed the
142  * ExecCGI or Open command - it may be nested one level deep (or more???)
143  */
144 static char* get_interpreter_from_win32_registry(apr_pool_t *p, 
145                                                  const char* ext,
146                                                  int strict)
147 {
148     apr_status_t rv;
149     ap_regkey_t *name_key = NULL;
150     ap_regkey_t *type_key;
151     ap_regkey_t *key;
152     char execcgi_path[] = "SHELL\\EXECCGI\\COMMAND";
153     char execopen_path[] = "SHELL\\OPEN\\COMMAND";
154     char *type_name;
155     char *buffer;
156     
157     if (!ext) {
158         return NULL;
159     }
160     /* 
161      * Future optimization:
162      * When the registry is successfully searched, store the strings for
163      * interpreter and arguments in an ext hash to speed up subsequent look-ups
164      */
165
166     /* Open the key associated with the script filetype extension */
167     rv = ap_regkey_open(&type_key, AP_REGKEY_CLASSES_ROOT, ext, APR_READ, p);
168
169     if (rv != APR_SUCCESS) {
170         return NULL;
171     }
172
173     /* Retrieve the name of the script filetype extension */
174     rv = ap_regkey_value_get(&type_name, type_key, "", p);
175
176     if (rv == APR_SUCCESS && type_name[0]) {
177         /* Open the key associated with the script filetype extension */
178         rv = ap_regkey_open(&name_key, AP_REGKEY_CLASSES_ROOT, type_name, 
179                             APR_READ, p);
180     }
181
182     /* Open the key for the script command path by:
183      * 
184      *   1) the 'named' filetype key for ExecCGI/Command
185      *   2) the extension's type key for ExecCGI/Command
186      *
187      * and if the strict arg is false, then continue trying:
188      *
189      *   3) the 'named' filetype key for Open/Command
190      *   4) the extension's type key for Open/Command
191      */
192
193     if (name_key) {
194         if ((rv = ap_regkey_open(&key, name_key, execcgi_path, APR_READ, p))
195                 == APR_SUCCESS) {
196             rv = ap_regkey_value_get(&buffer, key, "", p);
197             ap_regkey_close(name_key);
198         }
199     }
200
201     if (!name_key || (rv != APR_SUCCESS)) {
202         if ((rv = ap_regkey_open(&key, type_key, execcgi_path, APR_READ, p))
203                 == APR_SUCCESS) {
204             rv = ap_regkey_value_get(&buffer, key, "", p);
205             ap_regkey_close(type_key);
206         }
207     }
208
209     if (!strict && name_key && (rv != APR_SUCCESS)) {
210         if ((rv = ap_regkey_open(&key, name_key, execopen_path, APR_READ, p))
211                 == APR_SUCCESS) {
212             rv = ap_regkey_value_get(&buffer, key, "", p);
213             ap_regkey_close(name_key);
214         }
215     }
216
217     if (!strict && (rv != APR_SUCCESS)) {
218         if ((rv = ap_regkey_open(&key, type_key, execopen_path, APR_READ, p))
219                 == APR_SUCCESS) {
220             rv = ap_regkey_value_get(&buffer, key, "", p);
221             ap_regkey_close(type_key);
222         }
223     }
224
225     if (name_key) {
226         ap_regkey_close(name_key);
227     }
228
229     ap_regkey_close(type_key);
230
231     if (rv != APR_SUCCESS || !buffer[0]) {
232         return NULL;
233     }
234
235     return buffer;
236 }
237
238
239 static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
240                                       const char *cgiprg, const char *cgiargs)
241 {
242     apr_array_header_t *args = apr_array_make(p, 8, sizeof(char*));
243     char *d = apr_palloc(p, strlen(interp)+1);
244     const char *ch = interp; 
245     const char **arg;
246     int prgtaken = 0;
247     int argtaken = 0;
248     int inquo;
249     int sl;
250
251     while (*ch) {
252         /* Skip on through Deep Space */
253         if (apr_isspace(*ch)) {
254             ++ch; continue;
255         }
256         /* One Arg */
257         if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '*')) {
258             const char *cgiarg = cgiargs;
259             argtaken = 1;
260             for (;;) {
261                 char *w = ap_getword_nulls(p, &cgiarg, '+');
262                 if (!*w) {
263                     break;
264                 }
265                 ap_unescape_url(w);
266                 if (win_nt) {
267                    prep_string(&w, p);
268                 }
269                 arg = (const char**)apr_array_push(args);
270                 *arg = ap_escape_shell_cmd(p, w);
271             }
272             ch += 2;
273             continue;
274         }
275         if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '1')) {
276             /* Todo: Make short name!!! */
277             prgtaken = 1;
278             arg = (const char**)apr_array_push(args);
279             if (*ch == '%') {
280                 char *repl = apr_pstrdup(p, cgiprg);
281                 *arg = repl;
282                 while ((repl = strchr(repl, '/'))) {
283                     *repl++ = '\\';
284                 }
285             }
286             else {
287                 *arg = cgiprg;
288             }
289             ch += 2;
290             continue;
291         }
292         if ((*ch == '\"') && ((*(ch + 1) == '$') 
293                               || (*(ch + 1) == '%')) && (*(ch + 2) == '1') 
294             && (*(ch + 3) == '\"')) {
295             prgtaken = 1;
296             arg = (const char**)apr_array_push(args);
297             if (*(ch + 1) == '%') {
298                 char *repl = apr_pstrdup(p, cgiprg);
299                 *arg = repl;
300                 while ((repl = strchr(repl, '/'))) {
301                     *repl++ = '\\';
302                 }
303             }
304             else {
305                 *arg = cgiprg;
306             }
307             ch += 4;
308             continue;
309         }
310         arg = (const char**)apr_array_push(args);
311         *arg = d;
312         inquo = 0;
313         while (*ch) {
314             if (apr_isspace(*ch) && !inquo) {
315                 ++ch; break;
316             }
317             /* Get 'em backslashes */
318             for (sl = 0; *ch == '\\'; ++sl) {
319                 *d++ = *ch++;
320             }
321             if (sl & 1) {
322                 /* last unmatched '\' + '"' sequence is a '"' */
323                 if (*ch == '\"') {
324                     *(d - 1) = *ch++;
325                 }
326                 continue;
327             }
328             if (*ch == '\"') {
329                 /* '""' sequence within quotes is a '"' */
330                 if (*++ch == '\"' && inquo) {
331                     *d++ = *ch++; continue;
332                 }
333                 /* Flip quote state */
334                 inquo = !inquo;
335                 if (apr_isspace(*ch) && !inquo) {
336                     ++ch; break;
337                 }
338                 /* All other '"'s are Munched */
339                 continue;
340             }
341             /* Anything else is, well, something else */
342             *d++ = *ch++;
343         }
344         /* Term that arg, already pushed on args */
345         *d++ = '\0';
346     }
347
348     if (!prgtaken) {
349         arg = (const char**)apr_array_push(args);
350         *arg = cgiprg;
351     }
352
353     if (!argtaken) {
354         const char *cgiarg = cgiargs;
355         for (;;) {
356             char *w = ap_getword_nulls(p, &cgiarg, '+');
357             if (!*w) {
358                 break;
359             }
360             ap_unescape_url(w);
361             if (win_nt) {
362                 prep_string(&w, p);
363             }
364             arg = (const char**)apr_array_push(args);
365             *arg = ap_escape_shell_cmd(p, w);
366         }
367     }
368
369     arg = (const char**)apr_array_push(args);
370     *arg = NULL;
371
372     return args;
373 }
374
375
376 static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
377                                          request_rec *r, apr_pool_t *p, 
378                                          cgi_exec_info_t *e_info)
379 {
380     const apr_array_header_t *elts_arr = apr_table_elts(r->subprocess_env);
381     const apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts;
382     const char *ext = NULL;
383     const char *interpreter = NULL;
384     win32_dir_conf *d;
385     apr_file_t *fh;
386     const char *args = "";
387     int i;
388
389     d = (win32_dir_conf *)ap_get_module_config(r->per_dir_config, 
390                                                &win32_module);
391
392     if (e_info->cmd_type) {
393         /* We have to consider that the client gets any QUERY_ARGS
394          * without any charset interpretation, use prep_string to
395          * create a string of the literal QUERY_ARGS bytes.
396          */
397         *cmd = r->filename;
398         if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
399             args = r->args;
400         }
401     }
402     /* Handle the complete file name, we DON'T want to follow suexec, since
403      * an unrooted command is as predictable as shooting craps in Win32.
404      * Notice that unlike most mime extension parsing, we have to use the
405      * win32 parsing here, therefore the final extension is the only one
406      * we will consider.
407      */
408     ext = strrchr(apr_filename_of_pathname(*cmd), '.');
409     
410     /* If the file has an extension and it is not .com and not .exe and
411      * we've been instructed to search the registry, then do so.
412      * Let apr_proc_create do all of the .bat/.cmd dirty work.
413      */
414     if (ext && (!strcasecmp(ext,".exe") || !strcasecmp(ext,".com")
415                 || !strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
416         interpreter = "";
417     }
418     if (!interpreter && ext 
419           && (d->script_interpreter_source 
420                      == INTERPRETER_SOURCE_REGISTRY
421            || d->script_interpreter_source 
422                      == INTERPRETER_SOURCE_REGISTRY_STRICT)) {
423          /* Check the registry */
424         int strict = (d->script_interpreter_source 
425                       == INTERPRETER_SOURCE_REGISTRY_STRICT);
426         interpreter = get_interpreter_from_win32_registry(r->pool, ext,
427                                                           strict);
428         if (interpreter && e_info->cmd_type != APR_SHELLCMD) {
429             e_info->cmd_type = APR_PROGRAM_PATH;
430         }
431         else {
432             ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
433                  strict ? "No ExecCGI verb found for files of type '%s'."
434                         : "No ExecCGI or Open verb found for files of type '%s'.", 
435                  ext);
436         }
437     }
438     if (!interpreter) {
439         apr_status_t rv;
440         char buffer[1024];
441         apr_size_t bytes = sizeof(buffer);
442         int i;
443
444         /* Need to peek into the file figure out what it really is... 
445          * ### aught to go back and build a cache for this one of these days.
446          */
447         if (((rv = apr_file_open(&fh, *cmd, APR_READ | APR_BUFFERED,
448                                  APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) 
449             || ((rv = apr_file_read(fh, buffer, &bytes)) != APR_SUCCESS)) {
450             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
451                           "Failed to read cgi file %s for testing", *cmd);
452             return rv;
453         }
454         apr_file_close(fh);
455
456         /* Script or executable, that is the question... */
457         if ((buffer[0] == '#') && (buffer[1] == '!')) {
458             /* Assuming file is a script since it starts with a shebang */
459             for (i = 2; i < sizeof(buffer); i++) {
460                 if ((buffer[i] == '\r') || (buffer[i] == '\n')) {
461                     buffer[i] = '\0';
462                     break;
463                 }
464             }
465             if (i < sizeof(buffer)) {
466                 interpreter = buffer + 2;
467                 while (apr_isspace(*interpreter)) {
468                     ++interpreter;
469                 }
470                 if (e_info->cmd_type != APR_SHELLCMD) {
471                     e_info->cmd_type = APR_PROGRAM_PATH;
472                 }
473             }
474         }
475         else {
476             /* Not a script, is it an executable? */
477             IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;    
478             if ((bytes >= sizeof(IMAGE_DOS_HEADER))
479                 && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) {
480                 if (hdr->e_lfarlc < 0x40) {
481                     /* Ought to invoke this 16 bit exe by a stub, (cmd /c?) */
482                     interpreter = "";
483                 }
484                 else {
485                     interpreter = "";
486                 }
487             }
488         }
489     }
490     if (!interpreter) {
491         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
492                       "%s is not executable; ensure interpreted scripts have "
493                       "\"#!\" first line", *cmd);
494         return APR_EBADF;
495     }
496
497     *argv = (const char **)(split_argv(p, interpreter, *cmd,
498                                        args)->elts);
499     *cmd = (*argv)[0];
500
501     e_info->detached = 1;
502
503     /* XXX: Must fix r->subprocess_env to follow utf-8 conventions from
504      * the client's octets so that win32 apr_proc_create is happy.
505      * The -best- way is to determine if the .exe is unicode aware
506      * (using 0x0080-0x00ff) or is linked as a command or windows
507      * application (following the OEM or Ansi code page in effect.)
508      */
509     for (i = 0; i < elts_arr->nelts; ++i) {
510         if (win_nt && elts[i].key && *elts[i].key 
511                 && (strncmp(elts[i].key, "HTTP_", 5) == 0
512                  || strncmp(elts[i].key, "SERVER_", 7) == 0
513                  || strncmp(elts[i].key, "REQUEST_", 8) == 0
514                  || strcmp(elts[i].key, "QUERY_STRING") == 0
515                  || strcmp(elts[i].key, "PATH_INFO") == 0
516                  || strcmp(elts[i].key, "PATH_TRANSLATED") == 0)) {
517             prep_string((const char**) &elts[i].val, r->pool);
518         }
519     }
520     return APR_SUCCESS;
521 }
522
523 static int win32_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp) 
524 {
525     win_nt = (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
526     return OK;
527 }
528
529 static void register_hooks(apr_pool_t *p)
530 {
531     APR_REGISTER_OPTIONAL_FN(ap_cgi_build_command);
532     ap_hook_pre_config(win32_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
533 }
534
535 static const command_rec win32_cmds[] = {
536 AP_INIT_TAKE1("ScriptInterpreterSource", set_interpreter_source, NULL,
537               OR_FILEINFO,
538               "Where to find interpreter to run Win32 scripts "
539               "(Registry or script shebang line)"),
540 { NULL }
541 };
542
543 module AP_MODULE_DECLARE_DATA win32_module = {
544    STANDARD20_MODULE_STUFF,
545    create_win32_dir_config,     /* create per-dir config */
546    merge_win32_dir_configs,     /* merge per-dir config */
547    NULL,                        /* server config */
548    NULL,                        /* merge server config */
549    win32_cmds,                  /* command apr_table_t */
550    register_hooks               /* register hooks */
551 };
552
553 #endif /* defined WIN32 */