~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/arch/win32/mod_win32.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
    apr_size_t 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_filepath_name_get(*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
        apr_size_t 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
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
450
                          "Failed to open cgi file %s for testing", *cmd);
 
451
            return rv;
 
452
        }
 
453
        if ((rv = apr_file_read(fh, buffer, &bytes)) != APR_SUCCESS) {
 
454
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
455
                          "Failed to read cgi file %s for testing", *cmd);
 
456
            return rv;
 
457
        }
 
458
        apr_file_close(fh);
 
459
 
 
460
        /* Some twisted character [no pun intended] at MS decided that a
 
461
         * zero width joiner as the lead wide character would be ideal for
 
462
         * describing Unicode text files.  This was further convoluted to
 
463
         * another MSism that the same character mapped into utf-8, EF BB BF
 
464
         * would signify utf-8 text files.
 
465
         *
 
466
         * Since MS configuration files are all protecting utf-8 encoded
 
467
         * Unicode path, file and resource names, we already have the correct
 
468
         * WinNT encoding.  But at least eat the stupid three bytes up front.
 
469
         *
 
470
         * ### A more thorough check would also allow UNICODE text in buf, and
 
471
         * convert it to UTF-8 for invoking unicode scripts.  Those are few
 
472
         * and far between, so leave that code an enterprising soul with a need.
 
473
         */
 
474
        if ((bytes >= 3) && memcmp(buffer, "\xEF\xBB\xBF", 3) == 0) {
 
475
            memmove(buffer, buffer + 3, bytes -= 3);
 
476
        }
 
477
 
 
478
        /* Script or executable, that is the question... */
 
479
        if ((bytes >= 2) && (buffer[0] == '#') && (buffer[1] == '!')) {
 
480
            /* Assuming file is a script since it starts with a shebang */
 
481
            for (i = 2; i < bytes; i++) {
 
482
                if ((buffer[i] == '\r') || (buffer[i] == '\n')) {
 
483
                    buffer[i] = '\0';
 
484
                    break;
 
485
                }
 
486
            }
 
487
            if (i < bytes) {
 
488
                interpreter = buffer + 2;
 
489
                while (apr_isspace(*interpreter)) {
 
490
                    ++interpreter;
 
491
                }
 
492
                if (e_info->cmd_type != APR_SHELLCMD) {
 
493
                    e_info->cmd_type = APR_PROGRAM_PATH;
 
494
                }
 
495
            }
 
496
        }
 
497
        else if (bytes >= sizeof(IMAGE_DOS_HEADER)) {
 
498
            /* Not a script, is it an executable? */
 
499
            IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;
 
500
            if (hdr->e_magic == IMAGE_DOS_SIGNATURE) {
 
501
                if (hdr->e_lfarlc < 0x40) {
 
502
                    /* Ought to invoke this 16 bit exe by a stub, (cmd /c?) */
 
503
                    interpreter = "";
 
504
                }
 
505
                else {
 
506
                    interpreter = "";
 
507
                }
 
508
            }
 
509
        }
 
510
    }
 
511
    if (!interpreter) {
 
512
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
513
                      "%s is not executable; ensure interpreted scripts have "
 
514
                      "\"#!\" first line", *cmd);
 
515
        return APR_EBADF;
 
516
    }
 
517
 
 
518
    *argv = (const char **)(split_argv(p, interpreter, *cmd,
 
519
                                       args)->elts);
 
520
    *cmd = (*argv)[0];
 
521
 
 
522
    e_info->detached = 1;
 
523
 
 
524
    /* XXX: Must fix r->subprocess_env to follow utf-8 conventions from
 
525
     * the client's octets so that win32 apr_proc_create is happy.
 
526
     * The -best- way is to determine if the .exe is unicode aware
 
527
     * (using 0x0080-0x00ff) or is linked as a command or windows
 
528
     * application (following the OEM or Ansi code page in effect.)
 
529
     */
 
530
    for (i = 0; i < elts_arr->nelts; ++i) {
 
531
        if (win_nt && elts[i].key && *elts[i].key
 
532
                && (strncmp(elts[i].key, "HTTP_", 5) == 0
 
533
                 || strncmp(elts[i].key, "SERVER_", 7) == 0
 
534
                 || strncmp(elts[i].key, "REQUEST_", 8) == 0
 
535
                 || strcmp(elts[i].key, "QUERY_STRING") == 0
 
536
                 || strcmp(elts[i].key, "PATH_INFO") == 0
 
537
                 || strcmp(elts[i].key, "PATH_TRANSLATED") == 0)) {
 
538
            prep_string((const char**) &elts[i].val, r->pool);
 
539
        }
 
540
    }
 
541
    return APR_SUCCESS;
 
542
}
 
543
 
 
544
static int win32_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp)
 
545
{
 
546
    win_nt = (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
 
547
    return OK;
 
548
}
 
549
 
 
550
static void register_hooks(apr_pool_t *p)
 
551
{
 
552
    APR_REGISTER_OPTIONAL_FN(ap_cgi_build_command);
 
553
    ap_hook_pre_config(win32_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
 
554
}
 
555
 
 
556
static const command_rec win32_cmds[] = {
 
557
AP_INIT_TAKE1("ScriptInterpreterSource", set_interpreter_source, NULL,
 
558
              OR_FILEINFO,
 
559
              "Where to find interpreter to run Win32 scripts "
 
560
              "(Registry or script shebang line)"),
 
561
{ NULL }
 
562
};
 
563
 
 
564
module AP_MODULE_DECLARE_DATA win32_module = {
 
565
   STANDARD20_MODULE_STUFF,
 
566
   create_win32_dir_config,     /* create per-dir config */
 
567
   merge_win32_dir_configs,     /* merge per-dir config */
 
568
   NULL,                        /* server config */
 
569
   NULL,                        /* merge server config */
 
570
   win32_cmds,                  /* command apr_table_t */
 
571
   register_hooks               /* register hooks */
 
572
};
 
573
 
 
574
#endif /* defined WIN32 */