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

« back to all changes in this revision

Viewing changes to modules/arch/win32/mod_isapi.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
/*
 
18
 * mod_isapi.c - Internet Server Application (ISA) module for Apache
 
19
 * by Alexei Kosut <akosut@apache.org>, significant overhauls and
 
20
 * redesign by William Rowe <wrowe@covalent.net>, and hints from many
 
21
 * other developer/users who have hit on specific flaws.
 
22
 *
 
23
 * This module implements the ISAPI Handler architecture, allowing
 
24
 * Apache to load Internet Server Applications (ISAPI extensions),
 
25
 * similar to the support in IIS, Zope, O'Reilly's WebSite and others.
 
26
 *
 
27
 * It is a complete implementation of the ISAPI 2.0 specification,
 
28
 * except for "Microsoft extensions" to the API which provide
 
29
 * asynchronous I/O.  It is further extended to include additional
 
30
 * "Microsoft extentions" through IIS 5.0, with some deficiencies
 
31
 * where one-to-one mappings don't exist.
 
32
 *
 
33
 * Refer to /manual/mod/mod_isapi.html for additional details on
 
34
 * configuration and use, but check this source for specific support
 
35
 * of the API,
 
36
 */
 
37
 
 
38
#include "ap_config.h"
 
39
#include "httpd.h"
 
40
#include "http_config.h"
 
41
#include "http_core.h"
 
42
#include "http_protocol.h"
 
43
#include "http_request.h"
 
44
#include "http_log.h"
 
45
#include "util_script.h"
 
46
#include "mod_core.h"
 
47
#include "apr_lib.h"
 
48
#include "apr_strings.h"
 
49
#include "apr_portable.h"
 
50
#include "apr_buckets.h"
 
51
#include "apr_thread_mutex.h"
 
52
#include "apr_thread_rwlock.h"
 
53
#include "apr_hash.h"
 
54
#include "mod_isapi.h"
 
55
 
 
56
/* Retry frequency for a failed-to-load isapi .dll */
 
57
#define ISAPI_RETRY apr_time_from_sec(30)
 
58
 
 
59
/**********************************************************
 
60
 *
 
61
 *  ISAPI Module Configuration
 
62
 *
 
63
 **********************************************************/
 
64
 
 
65
module AP_MODULE_DECLARE_DATA isapi_module;
 
66
 
 
67
#define ISAPI_UNDEF -1
 
68
 
 
69
/* Our isapi per-dir config structure */
 
70
typedef struct isapi_dir_conf {
 
71
    int read_ahead_buflen;
 
72
    int log_unsupported;
 
73
    int log_to_errlog;
 
74
    int log_to_query;
 
75
    int fake_async;
 
76
} isapi_dir_conf;
 
77
 
 
78
typedef struct isapi_loaded isapi_loaded;
 
79
 
 
80
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
 
81
                          const char *fpath, isapi_loaded** isa);
 
82
 
 
83
static void *create_isapi_dir_config(apr_pool_t *p, char *dummy)
 
84
{
 
85
    isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
 
86
 
 
87
    dir->read_ahead_buflen = ISAPI_UNDEF;
 
88
    dir->log_unsupported   = ISAPI_UNDEF;
 
89
    dir->log_to_errlog     = ISAPI_UNDEF;
 
90
    dir->log_to_query      = ISAPI_UNDEF;
 
91
    dir->fake_async        = ISAPI_UNDEF;
 
92
 
 
93
    return dir;
 
94
}
 
95
 
 
96
static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_)
 
97
{
 
98
    isapi_dir_conf *base = (isapi_dir_conf *) base_;
 
99
    isapi_dir_conf *add = (isapi_dir_conf *) add_;
 
100
    isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
 
101
 
 
102
    dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF)
 
103
                                ? base->read_ahead_buflen
 
104
                                 : add->read_ahead_buflen;
 
105
    dir->log_unsupported   = (add->log_unsupported == ISAPI_UNDEF)
 
106
                                ? base->log_unsupported
 
107
                                 : add->log_unsupported;
 
108
    dir->log_to_errlog     = (add->log_to_errlog == ISAPI_UNDEF)
 
109
                                ? base->log_to_errlog
 
110
                                 : add->log_to_errlog;
 
111
    dir->log_to_query      = (add->log_to_query == ISAPI_UNDEF)
 
112
                                ? base->log_to_query
 
113
                                 : add->log_to_query;
 
114
    dir->fake_async        = (add->fake_async == ISAPI_UNDEF)
 
115
                                ? base->fake_async
 
116
                                 : add->fake_async;
 
117
 
 
118
    return dir;
 
119
}
 
120
 
 
121
static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
 
122
                                       const char *filename)
 
123
{
 
124
    isapi_loaded *isa;
 
125
    apr_finfo_t tmp;
 
126
    apr_status_t rv;
 
127
    char *fspec;
 
128
 
 
129
    /* ### Just an observation ... it would be terribly cool to be
 
130
     * able to use this per-dir, relative to the directory block being
 
131
     * defined.  The hash result remains global, but shorthand of
 
132
     * <Directory "c:/webapps/isapi">
 
133
     *     ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll
 
134
     * </Directory>
 
135
     * would be very convienent.
 
136
     */
 
137
    fspec = ap_server_root_relative(cmd->pool, filename);
 
138
    if (!fspec) {
 
139
        ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
 
140
                     "ISAPI: invalid module path, skipping %s", filename);
 
141
        return NULL;
 
142
    }
 
143
    if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE,
 
144
                      cmd->temp_pool)) != APR_SUCCESS) {
 
145
        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
 
146
            "ISAPI: unable to stat, skipping %s", fspec);
 
147
        return NULL;
 
148
    }
 
149
    if (tmp.filetype != APR_REG) {
 
150
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
151
            "ISAPI: not a regular file, skipping %s", fspec);
 
152
        return NULL;
 
153
    }
 
154
 
 
155
    /* Load the extention as cached (with null request_rec) */
 
156
    rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa);
 
157
    if (rv != APR_SUCCESS) {
 
158
        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
 
159
                     "ISAPI: unable to cache, skipping %s", fspec);
 
160
        return NULL;
 
161
    }
 
162
 
 
163
    return NULL;
 
164
}
 
165
 
 
166
static const command_rec isapi_cmds[] = {
 
167
    AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot,
 
168
        (void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen),
 
169
        OR_FILEINFO, "Maximum client request body to initially pass to the"
 
170
                     " ISAPI handler (default: 49152)"),
 
171
    AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot,
 
172
        (void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported),
 
173
        OR_FILEINFO, "Log requests not supported by the ISAPI server"
 
174
                     " on or off (default: off)"),
 
175
    AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot,
 
176
        (void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog),
 
177
        OR_FILEINFO, "Send all Append Log requests to the error log"
 
178
                     " on or off (default: off)"),
 
179
    AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot,
 
180
        (void *)APR_OFFSETOF(isapi_dir_conf, log_to_query),
 
181
        OR_FILEINFO, "Append Log requests are concatinated to the query args"
 
182
                     " on or off (default: on)"),
 
183
    AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot,
 
184
        (void *)APR_OFFSETOF(isapi_dir_conf, fake_async),
 
185
        OR_FILEINFO, "Fake Asynchronous support for isapi callbacks"
 
186
                     " on or off [Experimental] (default: off)"),
 
187
    AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL,
 
188
        RSRC_CONF, "Cache the specified ISAPI extension in-process"),
 
189
    {NULL}
 
190
};
 
191
 
 
192
/**********************************************************
 
193
 *
 
194
 *  ISAPI Module Cache handling section
 
195
 *
 
196
 **********************************************************/
 
197
 
 
198
/* Our isapi global config values */
 
199
static struct isapi_global_conf {
 
200
    apr_pool_t         *pool;
 
201
    apr_thread_mutex_t *lock;
 
202
    apr_hash_t         *hash;
 
203
} loaded;
 
204
 
 
205
/* Our loaded isapi module description structure */
 
206
struct isapi_loaded {
 
207
    const char          *filename;
 
208
    apr_thread_rwlock_t *in_progress;
 
209
    apr_status_t         last_load_rv;
 
210
    apr_time_t           last_load_time;
 
211
    apr_dso_handle_t    *handle;
 
212
    HSE_VERSION_INFO    *isapi_version;
 
213
    apr_uint32_t         report_version;
 
214
    apr_uint32_t         timeout;
 
215
    PFN_GETEXTENSIONVERSION GetExtensionVersion;
 
216
    PFN_HTTPEXTENSIONPROC   HttpExtensionProc;
 
217
    PFN_TERMINATEEXTENSION  TerminateExtension;
 
218
};
 
219
 
 
220
static apr_status_t isapi_unload(isapi_loaded *isa, int force)
 
221
{
 
222
    /* All done with the DLL... get rid of it...
 
223
     *
 
224
     * If optionally cached, and we weren't asked to force the unload,
 
225
     * pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload,
 
226
     * otherwise, leave it alone (it didn't choose to cooperate.)
 
227
     */
 
228
    if (!isa->handle) {
 
229
        return APR_SUCCESS;
 
230
    }
 
231
    if (isa->TerminateExtension) {
 
232
        if (force) {
 
233
            (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
 
234
        }
 
235
        else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) {
 
236
            return APR_EGENERAL;
 
237
        }
 
238
    }
 
239
    apr_dso_unload(isa->handle);
 
240
    isa->handle = NULL;
 
241
    return APR_SUCCESS;
 
242
}
 
243
 
 
244
static apr_status_t cleanup_isapi(void *isa_)
 
245
{
 
246
    isapi_loaded* isa = (isapi_loaded*) isa_;
 
247
 
 
248
    /* We must force the module to unload, we are about
 
249
     * to lose the isapi structure's allocation entirely.
 
250
     */
 
251
    return isapi_unload(isa, 1);
 
252
}
 
253
 
 
254
static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa)
 
255
{
 
256
    apr_status_t rv;
 
257
 
 
258
    isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
 
259
 
 
260
    /* TODO: These aught to become overrideable, so that we
 
261
     * assure a given isapi can be fooled into behaving well.
 
262
     *
 
263
     * The tricky bit, they aren't really a per-dir sort of
 
264
     * config, they will always be constant across every
 
265
     * reference to the .dll no matter what context (vhost,
 
266
     * location, etc) they apply to.
 
267
     */
 
268
    isa->report_version = 0x500; /* Revision 5.0 */
 
269
    isa->timeout = 300 * 1000000; /* microsecs, not used */
 
270
 
 
271
    rv = apr_dso_load(&isa->handle, isa->filename, p);
 
272
    if (rv)
 
273
    {
 
274
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
275
                     "ISAPI: failed to load %s", isa->filename);
 
276
        isa->handle = NULL;
 
277
        return rv;
 
278
    }
 
279
 
 
280
    rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle,
 
281
                     "GetExtensionVersion");
 
282
    if (rv)
 
283
    {
 
284
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
285
                     "ISAPI: missing GetExtensionVersion() in %s",
 
286
                     isa->filename);
 
287
        apr_dso_unload(isa->handle);
 
288
        isa->handle = NULL;
 
289
        return rv;
 
290
    }
 
291
 
 
292
    rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle,
 
293
                     "HttpExtensionProc");
 
294
    if (rv)
 
295
    {
 
296
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
297
                     "ISAPI: missing HttpExtensionProc() in %s",
 
298
                     isa->filename);
 
299
        apr_dso_unload(isa->handle);
 
300
        isa->handle = NULL;
 
301
        return rv;
 
302
    }
 
303
 
 
304
    /* TerminateExtension() is an optional interface */
 
305
    rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle,
 
306
                     "TerminateExtension");
 
307
    apr_set_os_error(0);
 
308
 
 
309
    /* Run GetExtensionVersion() */
 
310
    if (!(isa->GetExtensionVersion)(isa->isapi_version)) {
 
311
        apr_status_t rv = apr_get_os_error();
 
312
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
313
                     "ISAPI: failed call to GetExtensionVersion() in %s",
 
314
                     isa->filename);
 
315
        apr_dso_unload(isa->handle);
 
316
        isa->handle = NULL;
 
317
        return rv;
 
318
    }
 
319
 
 
320
    apr_pool_cleanup_register(p, isa, cleanup_isapi,
 
321
                              apr_pool_cleanup_null);
 
322
 
 
323
    return APR_SUCCESS;
 
324
}
 
325
 
 
326
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
 
327
                          const char *fpath, isapi_loaded** isa)
 
328
{
 
329
    apr_status_t rv;
 
330
    const char *key;
 
331
 
 
332
    if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) {
 
333
        return rv;
 
334
    }
 
335
 
 
336
    *isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING);
 
337
 
 
338
    if (*isa) {
 
339
 
 
340
        /* If we find this lock exists, use a set-aside copy of gainlock
 
341
         * to avoid race conditions on NULLing the in_progress variable
 
342
         * when the load has completed.  Release the global isapi hash
 
343
         * lock so other requests can proceed, then rdlock for completion
 
344
         * of loading our desired dll or wrlock if we would like to retry
 
345
         * loading the dll (because last_load_rv failed and retry is up.)
 
346
         */
 
347
        apr_thread_rwlock_t *gainlock = (*isa)->in_progress;
 
348
 
 
349
        /* gainlock is NULLed after the module loads successfully.
 
350
         * This free-threaded module can be used without any locking.
 
351
         */
 
352
        if (!gainlock) {
 
353
            rv = (*isa)->last_load_rv;
 
354
            apr_thread_mutex_unlock(loaded.lock);
 
355
            return rv;
 
356
        }
 
357
 
 
358
 
 
359
        if ((*isa)->last_load_rv == APR_SUCCESS) {
 
360
            apr_thread_mutex_unlock(loaded.lock);
 
361
            if ((rv = apr_thread_rwlock_rdlock(gainlock))
 
362
                    != APR_SUCCESS) {
 
363
                return rv;
 
364
            }
 
365
            rv = (*isa)->last_load_rv;
 
366
            apr_thread_rwlock_unlock(gainlock);
 
367
            return rv;
 
368
        }
 
369
 
 
370
        if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) {
 
371
 
 
372
            /* Remember last_load_time before releasing the global
 
373
             * hash lock to avoid colliding with another thread
 
374
             * that hit this exception at the same time as our
 
375
             * retry attempt, since we unlock the global mutex
 
376
             * before attempting a write lock for this module.
 
377
             */
 
378
            apr_time_t check_time = (*isa)->last_load_time;
 
379
            apr_thread_mutex_unlock(loaded.lock);
 
380
 
 
381
            if ((rv = apr_thread_rwlock_wrlock(gainlock))
 
382
                    != APR_SUCCESS) {
 
383
                return rv;
 
384
            }
 
385
 
 
386
            /* If last_load_time is unchanged, we still own this
 
387
             * retry, otherwise presume another thread provided
 
388
             * our retry (for good or ill).  Relock the global
 
389
             * hash for updating last_load_ vars, so their update
 
390
             * is always atomic to the global lock.
 
391
             */
 
392
            if (check_time == (*isa)->last_load_time) {
 
393
 
 
394
                rv = isapi_load(loaded.pool, s, *isa);
 
395
 
 
396
                apr_thread_mutex_lock(loaded.lock);
 
397
                (*isa)->last_load_rv = rv;
 
398
                (*isa)->last_load_time = apr_time_now();
 
399
                apr_thread_mutex_unlock(loaded.lock);
 
400
            }
 
401
            else {
 
402
                rv = (*isa)->last_load_rv;
 
403
            }
 
404
            apr_thread_rwlock_unlock(gainlock);
 
405
 
 
406
            return rv;
 
407
        }
 
408
 
 
409
        /* We haven't hit timeup on retry, let's grab the last_rv
 
410
         * within the hash mutex before unlocking.
 
411
         */
 
412
        rv = (*isa)->last_load_rv;
 
413
        apr_thread_mutex_unlock(loaded.lock);
 
414
 
 
415
        return rv;
 
416
    }
 
417
 
 
418
    /* If the module was not found, it's time to create a hash key entry
 
419
     * before releasing the hash lock to avoid multiple threads from
 
420
     * loading the same module.
 
421
     */
 
422
    key = apr_pstrdup(loaded.pool, fpath);
 
423
    *isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded));
 
424
    (*isa)->filename = key;
 
425
    if (r) {
 
426
        /* A mutex that exists only long enough to attempt to
 
427
         * load this isapi dll, the release this module to all
 
428
         * other takers that came along during the one-time
 
429
         * load process.  Short lifetime for this lock would
 
430
         * be great, however, using r->pool is nasty if those
 
431
         * blocked on the lock haven't all unlocked before we
 
432
         * attempt to destroy.  A nastier race condition than
 
433
         * I want to deal with at this moment...
 
434
         */
 
435
        apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
 
436
        apr_thread_rwlock_wrlock((*isa)->in_progress);
 
437
    }
 
438
 
 
439
    apr_hash_set(loaded.hash, key, APR_HASH_KEY_STRING, *isa);
 
440
 
 
441
    /* Now attempt to load the isapi on our own time,
 
442
     * allow other isapi processing to resume.
 
443
     */
 
444
    apr_thread_mutex_unlock(loaded.lock);
 
445
 
 
446
    rv = isapi_load(loaded.pool, s, *isa);
 
447
    (*isa)->last_load_time = apr_time_now();
 
448
    (*isa)->last_load_rv = rv;
 
449
 
 
450
    if (r && (rv == APR_SUCCESS)) {
 
451
        /* Let others who are blocked on this particular
 
452
         * module resume their requests, for better or worse.
 
453
         */
 
454
        apr_thread_rwlock_t *unlock = (*isa)->in_progress;
 
455
        (*isa)->in_progress = NULL;
 
456
        apr_thread_rwlock_unlock(unlock);
 
457
    }
 
458
    else if (!r && (rv != APR_SUCCESS)) {
 
459
        /* We must leave a rwlock around for requests to retry
 
460
         * loading this dll after timeup... since we were in
 
461
         * the setup code we had avoided creating this lock.
 
462
         */
 
463
        apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
 
464
    }
 
465
 
 
466
    return (*isa)->last_load_rv;
 
467
}
 
468
 
 
469
/**********************************************************
 
470
 *
 
471
 *  ISAPI Module request callbacks section
 
472
 *
 
473
 **********************************************************/
 
474
 
 
475
/* Our "Connection ID" structure */
 
476
struct isapi_cid {
 
477
    EXTENSION_CONTROL_BLOCK *ecb;
 
478
    isapi_dir_conf           dconf;
 
479
    isapi_loaded            *isa;
 
480
    request_rec             *r;
 
481
    int                      headers_set;
 
482
    int                      response_sent;
 
483
    PFN_HSE_IO_COMPLETION    completion;
 
484
    void                    *completion_arg;
 
485
    apr_thread_mutex_t      *completed;
 
486
};
 
487
 
 
488
int APR_THREAD_FUNC GetServerVariable (isapi_cid    *cid,
 
489
                                       char         *variable_name,
 
490
                                       void         *buf_ptr,
 
491
                                       apr_uint32_t *buf_size)
 
492
{
 
493
    request_rec *r = cid->r;
 
494
    const char *result;
 
495
    char *buf_data = (char*)buf_ptr;
 
496
    apr_uint32_t len;
 
497
 
 
498
    if (!strcmp(variable_name, "ALL_HTTP"))
 
499
    {
 
500
        /* crlf delimited, colon split, comma separated and
 
501
         * null terminated list of HTTP_ vars
 
502
         */
 
503
        const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
 
504
        const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
 
505
        int i;
 
506
 
 
507
        for (len = 0, i = 0; i < arr->nelts; i++) {
 
508
            if (!strncmp(elts[i].key, "HTTP_", 5)) {
 
509
                len += strlen(elts[i].key) + strlen(elts[i].val) + 3;
 
510
            }
 
511
        }
 
512
 
 
513
        if (*buf_size < len + 1) {
 
514
            *buf_size = len + 1;
 
515
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
 
516
            return 0;
 
517
        }
 
518
 
 
519
        for (i = 0; i < arr->nelts; i++) {
 
520
            if (!strncmp(elts[i].key, "HTTP_", 5)) {
 
521
                strcpy(buf_data, elts[i].key);
 
522
                buf_data += strlen(elts[i].key);
 
523
                *(buf_data++) = ':';
 
524
                strcpy(buf_data, elts[i].val);
 
525
                buf_data += strlen(elts[i].val);
 
526
                *(buf_data++) = '\r';
 
527
                *(buf_data++) = '\n';
 
528
            }
 
529
        }
 
530
 
 
531
        *(buf_data++) = '\0';
 
532
        *buf_size = len + 1;
 
533
        return 1;
 
534
    }
 
535
 
 
536
    if (!strcmp(variable_name, "ALL_RAW"))
 
537
    {
 
538
        /* crlf delimited, colon split, comma separated and
 
539
         * null terminated list of the raw request header
 
540
         */
 
541
        const apr_array_header_t *arr = apr_table_elts(r->headers_in);
 
542
        const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
 
543
        int i;
 
544
 
 
545
        for (len = 0, i = 0; i < arr->nelts; i++) {
 
546
            len += strlen(elts[i].key) + strlen(elts[i].val) + 4;
 
547
        }
 
548
 
 
549
        if (*buf_size < len + 1) {
 
550
            *buf_size = len + 1;
 
551
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
 
552
            return 0;
 
553
        }
 
554
 
 
555
        for (i = 0; i < arr->nelts; i++) {
 
556
            strcpy(buf_data, elts[i].key);
 
557
            buf_data += strlen(elts[i].key);
 
558
            *(buf_data++) = ':';
 
559
            *(buf_data++) = ' ';
 
560
            strcpy(buf_data, elts[i].val);
 
561
            buf_data += strlen(elts[i].val);
 
562
            *(buf_data++) = '\r';
 
563
            *(buf_data++) = '\n';
 
564
        }
 
565
        *(buf_data++) = '\0';
 
566
        *buf_size = len + 1;
 
567
        return 1;
 
568
    }
 
569
 
 
570
    /* Not a special case */
 
571
    result = apr_table_get(r->subprocess_env, variable_name);
 
572
 
 
573
    if (result) {
 
574
        len = strlen(result);
 
575
        if (*buf_size < len + 1) {
 
576
            *buf_size = len + 1;
 
577
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
 
578
            return 0;
 
579
        }
 
580
        strcpy(buf_data, result);
 
581
        *buf_size = len + 1;
 
582
        return 1;
 
583
    }
 
584
 
 
585
    /* Not Found */
 
586
    apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_INDEX));
 
587
    return 0;
 
588
}
 
589
 
 
590
int APR_THREAD_FUNC ReadClient(isapi_cid    *cid,
 
591
                               void         *buf_data,
 
592
                               apr_uint32_t *buf_size)
 
593
{
 
594
    request_rec *r = cid->r;
 
595
    apr_uint32_t read = 0;
 
596
    int res;
 
597
 
 
598
    if (r->remaining < *buf_size) {
 
599
        *buf_size = (apr_size_t)r->remaining;
 
600
    }
 
601
 
 
602
    while (read < *buf_size &&
 
603
           ((res = ap_get_client_block(r, (char*)buf_data + read,
 
604
                                       *buf_size - read)) > 0)) {
 
605
        read += res;
 
606
    }
 
607
 
 
608
    *buf_size = read;
 
609
    if (res < 0) {
 
610
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_READ_FAULT));
 
611
    }
 
612
    return (res >= 0);
 
613
}
 
614
 
 
615
/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and
 
616
 * the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s)
 
617
 * as well as other functions that write responses and presume that
 
618
 * the support functions above are optional.
 
619
 *
 
620
 * Other callers trying to split headers and body bytes should pass
 
621
 * head/headlen alone (leaving stat/statlen NULL/0), so that they
 
622
 * get a proper count of bytes consumed.  The argument passed to stat
 
623
 * isn't counted as the head bytes are.
 
624
 */
 
625
static apr_ssize_t send_response_header(isapi_cid *cid,
 
626
                                        const char *stat,
 
627
                                        const char *head,
 
628
                                        apr_size_t statlen,
 
629
                                        apr_size_t headlen)
 
630
{
 
631
    int head_present = 1;
 
632
    int termarg;
 
633
    const char *termch;
 
634
    apr_size_t ate = 0;
 
635
 
 
636
    if (!head || headlen == 0 || !*head) {
 
637
        head = stat;
 
638
        stat = NULL;
 
639
        headlen = statlen;
 
640
        statlen = 0;
 
641
        head_present = 0; /* Don't eat the header */
 
642
    }
 
643
 
 
644
    if (!stat || statlen == 0 || !*stat) {
 
645
        if (head && headlen && *head && ((stat = memchr(head, '\r', headlen))
 
646
                                      || (stat = memchr(head, '\n', headlen))
 
647
                                      || (stat = memchr(head, '\0', headlen))
 
648
                                      || (stat = head + headlen))) {
 
649
            statlen = stat - head;
 
650
            if (memchr(head, ':', statlen)) {
 
651
                stat = "Status: 200 OK";
 
652
                statlen = strlen(stat);
 
653
            }
 
654
            else {
 
655
                const char *flip = head;
 
656
                head = stat;
 
657
                stat = flip;
 
658
                headlen -= statlen;
 
659
                ate += statlen;
 
660
                if (*head == '\r' && headlen)
 
661
                    ++head, --headlen, ++ate;
 
662
                if (*head == '\n' && headlen)
 
663
                    ++head, --headlen, ++ate;
 
664
            }
 
665
        }
 
666
    }
 
667
 
 
668
    if (stat && (statlen > 0) && *stat) {
 
669
        char *newstat;
 
670
        if (!apr_isdigit(*stat)) {
 
671
            const char *stattok = stat;
 
672
            int toklen = statlen;
 
673
            while (toklen && *stattok && !apr_isspace(*stattok)) {
 
674
                ++stattok; --toklen;
 
675
            }
 
676
            while (toklen && apr_isspace(*stattok)) {
 
677
                ++stattok; --toklen;
 
678
            }
 
679
            /* Now decide if we follow the xxx message
 
680
             * or the http/x.x xxx message format
 
681
             */
 
682
            if (toklen && apr_isdigit(*stattok)) {
 
683
                statlen -= toklen;
 
684
                stat = stattok;
 
685
            }
 
686
        }
 
687
        newstat = apr_palloc(cid->r->pool, statlen + 9);
 
688
        strcpy(newstat, "Status: ");
 
689
        apr_cpystrn(newstat + 8, stat, statlen + 1);
 
690
        stat = newstat;
 
691
        statlen += 8;
 
692
    }
 
693
 
 
694
    if (!head || headlen == 0 || !*head) {
 
695
        head = "\r\n";
 
696
        headlen = 2;
 
697
    }
 
698
    else
 
699
    {
 
700
        if (head[headlen - 1] && head[headlen]) {
 
701
            /* Whoops... not NULL terminated */
 
702
            head = apr_pstrndup(cid->r->pool, head, headlen);
 
703
        }
 
704
    }
 
705
 
 
706
    /* Seems IIS does not enforce the requirement for \r\n termination
 
707
     * on HSE_REQ_SEND_RESPONSE_HEADER, but we won't panic...
 
708
     * ap_scan_script_header_err_strs handles this aspect for us.
 
709
     *
 
710
     * Parse them out, or die trying
 
711
     */
 
712
    if (stat) {
 
713
        cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL,
 
714
                                        &termch, &termarg, stat, head, NULL);
 
715
        cid->ecb->dwHttpStatusCode = cid->r->status;
 
716
    }
 
717
    else {
 
718
        cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL,
 
719
                                        &termch, &termarg, head, NULL);
 
720
        if (cid->ecb->dwHttpStatusCode && cid->r->status == HTTP_OK
 
721
                && cid->ecb->dwHttpStatusCode != HTTP_OK) {
 
722
            /* We tried every way to Sunday to get the status...
 
723
             * so now we fall back on dwHttpStatusCode if it appears
 
724
             * ap_scan_script_header fell back on the default code.
 
725
             * Any other results set dwHttpStatusCode to the decoded
 
726
             * status value.
 
727
             */
 
728
            cid->r->status = cid->ecb->dwHttpStatusCode;
 
729
            cid->r->status_line = ap_get_status_line(cid->r->status);
 
730
        }
 
731
        else {
 
732
            cid->ecb->dwHttpStatusCode = cid->r->status;
 
733
        }
 
734
    }
 
735
    if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR) {
 
736
        return -1;
 
737
    }
 
738
 
 
739
    /* If only Status was passed, we consumed nothing
 
740
     */
 
741
    if (!head_present)
 
742
        return 0;
 
743
 
 
744
    cid->headers_set = 1;
 
745
 
 
746
    /* If all went well, tell the caller we consumed the headers complete
 
747
     */
 
748
    if (!termch)
 
749
        return(ate + headlen);
 
750
 
 
751
    /* Any data left must be sent directly by the caller, all we
 
752
     * give back is the size of the headers we consumed (which only
 
753
     * happens if the parser got to the head arg, which varies based
 
754
     * on whether we passed stat+head to scan, or only head.
 
755
     */
 
756
    if (termch && (termarg == (stat ? 1 : 0))
 
757
               && head_present && head + headlen > termch) {
 
758
        return ate + termch - head;
 
759
    }
 
760
    return ate;
 
761
}
 
762
 
 
763
int APR_THREAD_FUNC WriteClient(isapi_cid    *cid,
 
764
                                void         *buf_ptr,
 
765
                                apr_uint32_t *size_arg,
 
766
                                apr_uint32_t  flags)
 
767
{
 
768
    request_rec *r = cid->r;
 
769
    conn_rec *c = r->connection;
 
770
    apr_uint32_t buf_size = *size_arg;
 
771
    char *buf_data = (char*)buf_ptr;
 
772
    apr_bucket_brigade *bb;
 
773
    apr_bucket *b;
 
774
    apr_status_t rv;
 
775
 
 
776
    if (!cid->headers_set) {
 
777
        /* It appears that the foxisapi module and other clients
 
778
         * presume that WriteClient("headers\n\nbody") will work.
 
779
         * Parse them out, or die trying.
 
780
         */
 
781
        apr_ssize_t ate;
 
782
        ate = send_response_header(cid, NULL, buf_data, 0, buf_size);
 
783
        if (ate < 0) {
 
784
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
785
            return 0;
 
786
        }
 
787
 
 
788
        buf_data += ate;
 
789
        buf_size -= ate;
 
790
    }
 
791
 
 
792
    if (buf_size) {
 
793
        bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
794
        b = apr_bucket_transient_create(buf_data, buf_size, c->bucket_alloc);
 
795
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
796
        b = apr_bucket_flush_create(c->bucket_alloc);
 
797
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
798
        rv = ap_pass_brigade(r->output_filters, bb);
 
799
        cid->response_sent = 1;
 
800
    }
 
801
 
 
802
    if ((flags & HSE_IO_ASYNC) && cid->completion) {
 
803
        if (rv == OK) {
 
804
            cid->completion(cid->ecb, cid->completion_arg,
 
805
                            *size_arg, ERROR_SUCCESS);
 
806
        }
 
807
        else {
 
808
            cid->completion(cid->ecb, cid->completion_arg,
 
809
                            *size_arg, ERROR_WRITE_FAULT);
 
810
        }
 
811
    }
 
812
    return (rv == OK);
 
813
}
 
814
 
 
815
int APR_THREAD_FUNC ServerSupportFunction(isapi_cid    *cid,
 
816
                                          apr_uint32_t  HSE_code,
 
817
                                          void         *buf_ptr,
 
818
                                          apr_uint32_t *buf_size,
 
819
                                          apr_uint32_t *data_type)
 
820
{
 
821
    request_rec *r = cid->r;
 
822
    conn_rec *c = r->connection;
 
823
    char *buf_data = (char*)buf_ptr;
 
824
    request_rec *subreq;
 
825
 
 
826
    switch (HSE_code) {
 
827
    case HSE_REQ_SEND_URL_REDIRECT_RESP:
 
828
        /* Set the status to be returned when the HttpExtensionProc()
 
829
         * is done.
 
830
         * WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP
 
831
         *          and HSE_REQ_SEND_URL as equivalant per the Jan 2000 SDK.
 
832
         *          They most definately are not, even in their own samples.
 
833
         */
 
834
        apr_table_set (r->headers_out, "Location", buf_data);
 
835
        cid->r->status = cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY;
 
836
        cid->r->status_line = ap_get_status_line(cid->r->status);
 
837
        cid->headers_set = 1;
 
838
        return 1;
 
839
 
 
840
    case HSE_REQ_SEND_URL:
 
841
        /* Soak up remaining input */
 
842
        if (r->remaining > 0) {
 
843
            char argsbuffer[HUGE_STRING_LEN];
 
844
            while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
 
845
        }
 
846
 
 
847
        /* Reset the method to GET */
 
848
        r->method = apr_pstrdup(r->pool, "GET");
 
849
        r->method_number = M_GET;
 
850
 
 
851
        /* Don't let anyone think there's still data */
 
852
        apr_table_unset(r->headers_in, "Content-Length");
 
853
 
 
854
        /* AV fault per PR3598 - redirected path is lost! */
 
855
        buf_data = apr_pstrdup(r->pool, (char*)buf_data);
 
856
        ap_internal_redirect(buf_data, r);
 
857
        return 1;
 
858
 
 
859
    case HSE_REQ_SEND_RESPONSE_HEADER:
 
860
    {
 
861
        /* Parse them out, or die trying */
 
862
        apr_size_t statlen = 0, headlen = 0;
 
863
        apr_ssize_t ate;
 
864
        if (buf_data)
 
865
            statlen = strlen((char*) buf_data);
 
866
        if (data_type)
 
867
            headlen = strlen((char*) data_type);
 
868
        ate = send_response_header(cid, (char*) buf_data,
 
869
                                   (char*) data_type,
 
870
                                   statlen, headlen);
 
871
        if (ate < 0) {
 
872
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
873
            return 0;
 
874
        }
 
875
        else if ((apr_size_t)ate < headlen) {
 
876
            apr_bucket_brigade *bb;
 
877
            apr_bucket *b;
 
878
            bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
 
879
            b = apr_bucket_transient_create((char*) data_type + ate,
 
880
                                           headlen - ate, c->bucket_alloc);
 
881
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
882
            b = apr_bucket_flush_create(c->bucket_alloc);
 
883
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
884
            ap_pass_brigade(cid->r->output_filters, bb);
 
885
            cid->response_sent = 1;
 
886
        }
 
887
        return 1;
 
888
    }
 
889
 
 
890
    case HSE_REQ_DONE_WITH_SESSION:
 
891
        /* Signal to resume the thread completing this request,
 
892
         * leave it to the pool cleanup to dispose of our mutex.
 
893
         */
 
894
        if (cid->completed) {
 
895
            (void)apr_thread_mutex_unlock(cid->completed);
 
896
            return 1;
 
897
        }
 
898
        else if (cid->dconf.log_unsupported) {
 
899
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
900
                          "ISAPI: ServerSupportFunction "
 
901
                          "HSE_REQ_DONE_WITH_SESSION is not supported: %s",
 
902
                          r->filename);
 
903
        }
 
904
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
905
        return 0;
 
906
 
 
907
    case HSE_REQ_MAP_URL_TO_PATH:
 
908
    {
 
909
        /* Map a URL to a filename */
 
910
        char *file = (char *)buf_data;
 
911
        apr_uint32_t len;
 
912
        subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *buf_size),
 
913
                                       r, NULL);
 
914
 
 
915
        len = apr_cpystrn(file, subreq->filename, *buf_size) - file;
 
916
 
 
917
 
 
918
        /* IIS puts a trailing slash on directories, Apache doesn't */
 
919
        if (subreq->finfo.filetype == APR_DIR) {
 
920
            if (len < *buf_size - 1) {
 
921
                file[len++] = '\\';
 
922
                file[len] = '\0';
 
923
            }
 
924
        }
 
925
        *buf_size = len;
 
926
        return 1;
 
927
    }
 
928
 
 
929
    case HSE_REQ_GET_SSPI_INFO:
 
930
        if (cid->dconf.log_unsupported)
 
931
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
932
                           "ISAPI: ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
 
933
                           "is not supported: %s", r->filename);
 
934
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
935
        return 0;
 
936
 
 
937
    case HSE_APPEND_LOG_PARAMETER:
 
938
        /* Log buf_data, of buf_size bytes, in the URI Query (cs-uri-query) field
 
939
         */
 
940
        apr_table_set(r->notes, "isapi-parameter", (char*) buf_data);
 
941
        if (cid->dconf.log_to_query) {
 
942
            if (r->args)
 
943
                r->args = apr_pstrcat(r->pool, r->args, (char*) buf_data, NULL);
 
944
            else
 
945
                r->args = apr_pstrdup(r->pool, (char*) buf_data);
 
946
        }
 
947
        if (cid->dconf.log_to_errlog)
 
948
            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
 
949
                          "ISAPI: %s: %s", cid->r->filename,
 
950
                          (char*) buf_data);
 
951
        return 1;
 
952
 
 
953
    case HSE_REQ_IO_COMPLETION:
 
954
        /* Emulates a completion port...  Record callback address and
 
955
         * user defined arg, we will call this after any async request
 
956
         * (e.g. transmitfile) as if the request executed async.
 
957
         * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
 
958
         * to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL.
 
959
         */
 
960
        if (cid->dconf.fake_async) {
 
961
            cid->completion = (PFN_HSE_IO_COMPLETION) buf_data;
 
962
            cid->completion_arg = (void *) data_type;
 
963
            return 1;
 
964
        }
 
965
        if (cid->dconf.log_unsupported)
 
966
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
967
                      "ISAPI: ServerSupportFunction HSE_REQ_IO_COMPLETION "
 
968
                      "is not supported: %s", r->filename);
 
969
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
970
        return 0;
 
971
 
 
972
    case HSE_REQ_TRANSMIT_FILE:
 
973
    {
 
974
        /* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND)
 
975
         */
 
976
        HSE_TF_INFO *tf = (HSE_TF_INFO*)buf_data;
 
977
        apr_uint32_t sent = 0;
 
978
        apr_ssize_t ate = 0;
 
979
        apr_status_t rv;
 
980
        apr_bucket_brigade *bb;
 
981
        apr_bucket *b;
 
982
        apr_file_t *fd;
 
983
        apr_off_t fsize;
 
984
 
 
985
        if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) {
 
986
            if (cid->dconf.log_unsupported)
 
987
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
988
                         "ISAPI: ServerSupportFunction HSE_REQ_TRANSMIT_FILE "
 
989
                         "as HSE_IO_ASYNC is not supported: %s", r->filename);
 
990
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
991
            return 0;
 
992
        }
 
993
 
 
994
        /* Presume the handle was opened with the CORRECT semantics
 
995
         * for TransmitFile
 
996
         */
 
997
        if ((rv = apr_os_file_put(&fd, &tf->hFile,
 
998
                                  APR_READ | APR_XTHREAD, r->pool))
 
999
                != APR_SUCCESS) {
 
1000
            return 0;
 
1001
        }
 
1002
        if (tf->BytesToWrite) {
 
1003
            fsize = tf->BytesToWrite;
 
1004
        }
 
1005
        else {
 
1006
            apr_finfo_t fi;
 
1007
            if (apr_file_info_get(&fi, APR_FINFO_SIZE, fd) != APR_SUCCESS) {
 
1008
                apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1009
                return 0;
 
1010
            }
 
1011
            fsize = fi.size - tf->Offset;
 
1012
        }
 
1013
 
 
1014
        /* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */
 
1015
        bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
1016
 
 
1017
        /* According to MS: if calling HSE_REQ_TRANSMIT_FILE with the
 
1018
         * HSE_IO_SEND_HEADERS flag, then you can't otherwise call any
 
1019
         * HSE_SEND_RESPONSE_HEADERS* fn, but if you don't use the flag,
 
1020
         * you must have done so.  They document that the pHead headers
 
1021
         * option is valid only for HSE_IO_SEND_HEADERS - we are a bit
 
1022
         * more flexible and assume with the flag, pHead are the
 
1023
         * response headers, and without, pHead simply contains text
 
1024
         * (handled after this case).
 
1025
         */
 
1026
        if ((tf->dwFlags & HSE_IO_SEND_HEADERS) && tf->pszStatusCode) {
 
1027
            ate = send_response_header(cid, tf->pszStatusCode,
 
1028
                                            (char*)tf->pHead,
 
1029
                                            strlen(tf->pszStatusCode),
 
1030
                                            tf->HeadLength);
 
1031
        }
 
1032
        else if (!cid->headers_set && tf->pHead && tf->HeadLength
 
1033
                                   && *(char*)tf->pHead) {
 
1034
            ate = send_response_header(cid, NULL, (char*)tf->pHead,
 
1035
                                            0, tf->HeadLength);
 
1036
            if (ate < 0)
 
1037
            {
 
1038
                apr_brigade_destroy(bb);
 
1039
                apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1040
                return 0;
 
1041
            }
 
1042
        }
 
1043
 
 
1044
        if (tf->pHead && (apr_size_t)ate < tf->HeadLength) {
 
1045
            b = apr_bucket_transient_create((char*)tf->pHead + ate,
 
1046
                                            tf->HeadLength - ate,
 
1047
                                            c->bucket_alloc);
 
1048
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
1049
            sent = tf->HeadLength;
 
1050
        }
 
1051
 
 
1052
        sent += (apr_uint32_t)fsize;
 
1053
#if APR_HAS_LARGE_FILES
 
1054
        if (r->finfo.size > AP_MAX_SENDFILE) {
 
1055
            /* APR_HAS_LARGE_FILES issue; must split into mutiple buckets,
 
1056
             * no greater than MAX(apr_size_t), and more granular than that
 
1057
             * in case the brigade code/filters attempt to read it directly.
 
1058
             */
 
1059
            b = apr_bucket_file_create(fd, tf->Offset, AP_MAX_SENDFILE,
 
1060
                                       r->pool, c->bucket_alloc);
 
1061
            while (fsize > AP_MAX_SENDFILE) {
 
1062
                apr_bucket *bc;
 
1063
                apr_bucket_copy(b, &bc);
 
1064
                APR_BRIGADE_INSERT_TAIL(bb, bc);
 
1065
                b->start += AP_MAX_SENDFILE;
 
1066
                fsize -= AP_MAX_SENDFILE;
 
1067
            }
 
1068
            b->length = (apr_size_t)fsize; /* Resize just the last bucket */
 
1069
        }
 
1070
        else
 
1071
#endif
 
1072
            b = apr_bucket_file_create(fd, tf->Offset, (apr_size_t)fsize,
 
1073
                                       r->pool, c->bucket_alloc);
 
1074
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
1075
 
 
1076
        if (tf->pTail && tf->TailLength) {
 
1077
            sent += tf->TailLength;
 
1078
            b = apr_bucket_transient_create((char*)tf->pTail,
 
1079
                                            tf->TailLength, c->bucket_alloc);
 
1080
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
1081
        }
 
1082
 
 
1083
        b = apr_bucket_flush_create(c->bucket_alloc);
 
1084
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
1085
        ap_pass_brigade(r->output_filters, bb);
 
1086
        cid->response_sent = 1;
 
1087
 
 
1088
        /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete
 
1089
         * pass pContect to the HseIO callback.
 
1090
         */
 
1091
        if (tf->dwFlags & HSE_IO_ASYNC) {
 
1092
            if (tf->pfnHseIO) {
 
1093
                if (rv == OK) {
 
1094
                    tf->pfnHseIO(cid->ecb, tf->pContext,
 
1095
                                 ERROR_SUCCESS, sent);
 
1096
                }
 
1097
                else {
 
1098
                    tf->pfnHseIO(cid->ecb, tf->pContext,
 
1099
                                 ERROR_WRITE_FAULT, sent);
 
1100
                }
 
1101
            }
 
1102
            else if (cid->completion) {
 
1103
                if (rv == OK) {
 
1104
                    cid->completion(cid->ecb, cid->completion_arg,
 
1105
                                    sent, ERROR_SUCCESS);
 
1106
                }
 
1107
                else {
 
1108
                    cid->completion(cid->ecb, cid->completion_arg,
 
1109
                                    sent, ERROR_WRITE_FAULT);
 
1110
                }
 
1111
            }
 
1112
        }
 
1113
        return (rv == OK);
 
1114
    }
 
1115
 
 
1116
    case HSE_REQ_REFRESH_ISAPI_ACL:
 
1117
        if (cid->dconf.log_unsupported)
 
1118
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1119
                          "ISAPI: ServerSupportFunction "
 
1120
                          "HSE_REQ_REFRESH_ISAPI_ACL "
 
1121
                          "is not supported: %s", r->filename);
 
1122
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1123
        return 0;
 
1124
 
 
1125
    case HSE_REQ_IS_KEEP_CONN:
 
1126
        *((int *)buf_data) = (r->connection->keepalive == AP_CONN_KEEPALIVE);
 
1127
        return 1;
 
1128
 
 
1129
    case HSE_REQ_ASYNC_READ_CLIENT:
 
1130
    {
 
1131
        apr_uint32_t read = 0;
 
1132
        int res;
 
1133
        if (!cid->dconf.fake_async) {
 
1134
            if (cid->dconf.log_unsupported)
 
1135
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1136
                            "ISAPI: asynchronous I/O not supported: %s",
 
1137
                            r->filename);
 
1138
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1139
            return 0;
 
1140
        }
 
1141
 
 
1142
        if (r->remaining < *buf_size) {
 
1143
            *buf_size = (apr_size_t)r->remaining;
 
1144
        }
 
1145
 
 
1146
        while (read < *buf_size &&
 
1147
            ((res = ap_get_client_block(r, (char*)buf_data + read,
 
1148
                                        *buf_size - read)) > 0)) {
 
1149
            read += res;
 
1150
        }
 
1151
 
 
1152
        if ((*data_type & HSE_IO_ASYNC) && cid->completion) {
 
1153
            if (res >= 0) {
 
1154
                cid->completion(cid->ecb, cid->completion_arg,
 
1155
                                read, ERROR_SUCCESS);
 
1156
            }
 
1157
            else {
 
1158
                cid->completion(cid->ecb, cid->completion_arg,
 
1159
                                read, ERROR_READ_FAULT);
 
1160
            }
 
1161
        }
 
1162
        return (res >= 0);
 
1163
    }
 
1164
 
 
1165
    case HSE_REQ_GET_IMPERSONATION_TOKEN:  /* Added in ISAPI 4.0 */
 
1166
        if (cid->dconf.log_unsupported)
 
1167
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1168
                          "ISAPI: ServerSupportFunction "
 
1169
                          "HSE_REQ_GET_IMPERSONATION_TOKEN "
 
1170
                          "is not supported: %s", r->filename);
 
1171
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1172
        return 0;
 
1173
 
 
1174
    case HSE_REQ_MAP_URL_TO_PATH_EX:
 
1175
    {
 
1176
        /* Map a URL to a filename */
 
1177
        HSE_URL_MAPEX_INFO *info = (HSE_URL_MAPEX_INFO*)data_type;
 
1178
        char* test_uri = apr_pstrndup(r->pool, (char *)buf_data, *buf_size);
 
1179
 
 
1180
        subreq = ap_sub_req_lookup_uri(test_uri, r, NULL);
 
1181
        info->cchMatchingURL = strlen(test_uri);
 
1182
        info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename,
 
1183
                                      sizeof(info->lpszPath)) - info->lpszPath;
 
1184
 
 
1185
        /* Mapping started with assuming both strings matched.
 
1186
         * Now roll on the path_info as a mismatch and handle
 
1187
         * terminating slashes for directory matches.
 
1188
         */
 
1189
        if (subreq->path_info && *subreq->path_info) {
 
1190
            apr_cpystrn(info->lpszPath + info->cchMatchingPath,
 
1191
                        subreq->path_info,
 
1192
                        sizeof(info->lpszPath) - info->cchMatchingPath);
 
1193
            info->cchMatchingURL -= strlen(subreq->path_info);
 
1194
            if (subreq->finfo.filetype == APR_DIR
 
1195
                 && info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
 
1196
                /* roll forward over path_info's first slash */
 
1197
                ++info->cchMatchingPath;
 
1198
                ++info->cchMatchingURL;
 
1199
            }
 
1200
        }
 
1201
        else if (subreq->finfo.filetype == APR_DIR
 
1202
                 && info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
 
1203
            /* Add a trailing slash for directory */
 
1204
            info->lpszPath[info->cchMatchingPath++] = '/';
 
1205
            info->lpszPath[info->cchMatchingPath] = '\0';
 
1206
        }
 
1207
 
 
1208
        /* If the matched isn't a file, roll match back to the prior slash */
 
1209
        if (subreq->finfo.filetype == APR_NOFILE) {
 
1210
            while (info->cchMatchingPath && info->cchMatchingURL) {
 
1211
                if (info->lpszPath[info->cchMatchingPath - 1] == '/')
 
1212
                    break;
 
1213
                --info->cchMatchingPath;
 
1214
                --info->cchMatchingURL;
 
1215
            }
 
1216
        }
 
1217
 
 
1218
        /* Paths returned with back slashes */
 
1219
        for (test_uri = info->lpszPath; *test_uri; ++test_uri)
 
1220
            if (*test_uri == '/')
 
1221
                *test_uri = '\\';
 
1222
 
 
1223
        /* is a combination of:
 
1224
         * HSE_URL_FLAGS_READ         0x001 Allow read
 
1225
         * HSE_URL_FLAGS_WRITE        0x002 Allow write
 
1226
         * HSE_URL_FLAGS_EXECUTE      0x004 Allow execute
 
1227
         * HSE_URL_FLAGS_SSL          0x008 Require SSL
 
1228
         * HSE_URL_FLAGS_DONT_CACHE   0x010 Don't cache (VRoot only)
 
1229
         * HSE_URL_FLAGS_NEGO_CERT    0x020 Allow client SSL cert
 
1230
         * HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert
 
1231
         * HSE_URL_FLAGS_MAP_CERT     0x080 Map client SSL cert to account
 
1232
         * HSE_URL_FLAGS_SSL128       0x100 Require 128-bit SSL cert
 
1233
         * HSE_URL_FLAGS_SCRIPT       0x200 Allow script execution
 
1234
         *
 
1235
         * XxX: As everywhere, EXEC flags could use some work...
 
1236
         *      and this could go further with more flags, as desired.
 
1237
         */
 
1238
        info->dwFlags = (subreq->finfo.protection & APR_UREAD    ? 0x001 : 0)
 
1239
                      | (subreq->finfo.protection & APR_UWRITE   ? 0x002 : 0)
 
1240
                      | (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
 
1241
        return 1;
 
1242
    }
 
1243
 
 
1244
    case HSE_REQ_ABORTIVE_CLOSE:
 
1245
        if (cid->dconf.log_unsupported)
 
1246
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1247
                          "ISAPI: ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
 
1248
                          " is not supported: %s", r->filename);
 
1249
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1250
        return 0;
 
1251
 
 
1252
    case HSE_REQ_GET_CERT_INFO_EX:  /* Added in ISAPI 4.0 */
 
1253
        if (cid->dconf.log_unsupported)
 
1254
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1255
                          "ISAPI: ServerSupportFunction "
 
1256
                          "HSE_REQ_GET_CERT_INFO_EX "
 
1257
                          "is not supported: %s", r->filename);
 
1258
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1259
        return 0;
 
1260
 
 
1261
    case HSE_REQ_SEND_RESPONSE_HEADER_EX:  /* Added in ISAPI 4.0 */
 
1262
    {
 
1263
        HSE_SEND_HEADER_EX_INFO *shi = (HSE_SEND_HEADER_EX_INFO*)buf_data;
 
1264
 
 
1265
        /*  Ignore shi->fKeepConn - we don't want the advise
 
1266
         */
 
1267
        apr_ssize_t ate = send_response_header(cid, shi->pszStatus,
 
1268
                                               shi->pszHeader,
 
1269
                                               shi->cchStatus,
 
1270
                                               shi->cchHeader);
 
1271
        if (ate < 0) {
 
1272
            apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1273
            return 0;
 
1274
        }
 
1275
        else if ((apr_size_t)ate < shi->cchHeader) {
 
1276
            apr_bucket_brigade *bb;
 
1277
            apr_bucket *b;
 
1278
            bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
 
1279
            b = apr_bucket_transient_create(shi->pszHeader + ate,
 
1280
                                            shi->cchHeader - ate,
 
1281
                                            c->bucket_alloc);
 
1282
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
1283
            b = apr_bucket_flush_create(c->bucket_alloc);
 
1284
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
1285
            ap_pass_brigade(cid->r->output_filters, bb);
 
1286
            cid->response_sent = 1;
 
1287
        }
 
1288
        return 1;
 
1289
    }
 
1290
 
 
1291
    case HSE_REQ_CLOSE_CONNECTION:  /* Added after ISAPI 4.0 */
 
1292
        if (cid->dconf.log_unsupported)
 
1293
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1294
                          "ISAPI: ServerSupportFunction "
 
1295
                          "HSE_REQ_CLOSE_CONNECTION "
 
1296
                          "is not supported: %s", r->filename);
 
1297
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1298
        return 0;
 
1299
 
 
1300
    case HSE_REQ_IS_CONNECTED:  /* Added after ISAPI 4.0 */
 
1301
        /* Returns True if client is connected c.f. MSKB Q188346
 
1302
         * assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN
 
1303
         */
 
1304
        *((int *)buf_data) = (r->connection->aborted == 0);
 
1305
        return 1;
 
1306
 
 
1307
    case HSE_REQ_EXTENSION_TRIGGER:  /* Added after ISAPI 4.0 */
 
1308
        /*  Undocumented - defined by the Microsoft Jan '00 Platform SDK
 
1309
         */
 
1310
        if (cid->dconf.log_unsupported)
 
1311
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1312
                          "ISAPI: ServerSupportFunction "
 
1313
                          "HSE_REQ_EXTENSION_TRIGGER "
 
1314
                          "is not supported: %s", r->filename);
 
1315
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1316
        return 0;
 
1317
 
 
1318
    default:
 
1319
        if (cid->dconf.log_unsupported)
 
1320
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1321
                          "ISAPI: ServerSupportFunction (%d) not supported: "
 
1322
                          "%s", HSE_code, r->filename);
 
1323
        apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
 
1324
        return 0;
 
1325
    }
 
1326
}
 
1327
 
 
1328
/**********************************************************
 
1329
 *
 
1330
 *  ISAPI Module request invocation section
 
1331
 *
 
1332
 **********************************************************/
 
1333
 
 
1334
apr_status_t isapi_handler (request_rec *r)
 
1335
{
 
1336
    isapi_dir_conf *dconf;
 
1337
    apr_table_t *e;
 
1338
    apr_status_t rv;
 
1339
    isapi_loaded *isa;
 
1340
    isapi_cid *cid;
 
1341
    const char *val;
 
1342
    apr_uint32_t read;
 
1343
    int res;
 
1344
 
 
1345
    if(strcmp(r->handler, "isapi-isa")
 
1346
        && strcmp(r->handler, "isapi-handler")) {
 
1347
        /* Hang on to the isapi-isa for compatibility with older docs
 
1348
         * (wtf did '-isa' mean in the first place?) but introduce
 
1349
         * a newer and clearer "isapi-handler" name.
 
1350
         */
 
1351
        return DECLINED;
 
1352
    }
 
1353
    dconf = ap_get_module_config(r->per_dir_config, &isapi_module);
 
1354
    e = r->subprocess_env;
 
1355
 
 
1356
    /* Use similar restrictions as CGIs
 
1357
     *
 
1358
     * If this fails, it's pointless to load the isapi dll.
 
1359
     */
 
1360
    if (!(ap_allow_options(r) & OPT_EXECCGI)) {
 
1361
        return HTTP_FORBIDDEN;
 
1362
    }
 
1363
    if (r->finfo.filetype == APR_NOFILE) {
 
1364
        return HTTP_NOT_FOUND;
 
1365
    }
 
1366
    if (r->finfo.filetype != APR_REG) {
 
1367
        return HTTP_FORBIDDEN;
 
1368
    }
 
1369
    if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
 
1370
        r->path_info && *r->path_info) {
 
1371
        /* default to accept */
 
1372
        return HTTP_NOT_FOUND;
 
1373
    }
 
1374
 
 
1375
    if (isapi_lookup(r->pool, r->server, r, r->filename, &isa)
 
1376
           != APR_SUCCESS) {
 
1377
        return HTTP_INTERNAL_SERVER_ERROR;
 
1378
    }
 
1379
    /* Set up variables */
 
1380
    ap_add_common_vars(r);
 
1381
    ap_add_cgi_vars(r);
 
1382
    apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
 
1383
    if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on"))
 
1384
        apr_table_setn(e, "SERVER_PORT_SECURE", "1");
 
1385
    else
 
1386
        apr_table_setn(e, "SERVER_PORT_SECURE", "0");
 
1387
    apr_table_setn(e, "URL", r->uri);
 
1388
 
 
1389
    /* Set up connection structure and ecb,
 
1390
     * NULL or zero out most fields.
 
1391
     */
 
1392
    cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
 
1393
 
 
1394
    /* Fixup defaults for dconf */
 
1395
    cid->dconf.read_ahead_buflen = (dconf->read_ahead_buflen == ISAPI_UNDEF)
 
1396
                                     ? 49152 : dconf->read_ahead_buflen;
 
1397
    cid->dconf.log_unsupported   = (dconf->log_unsupported == ISAPI_UNDEF)
 
1398
                                     ? 0 : dconf->log_unsupported;
 
1399
    cid->dconf.log_to_errlog     = (dconf->log_to_errlog == ISAPI_UNDEF)
 
1400
                                     ? 0 : dconf->log_to_errlog;
 
1401
    cid->dconf.log_to_query      = (dconf->log_to_query == ISAPI_UNDEF)
 
1402
                                     ? 1 : dconf->log_to_query;
 
1403
    cid->dconf.fake_async        = (dconf->fake_async == ISAPI_UNDEF)
 
1404
                                     ? 0 : dconf->fake_async;
 
1405
 
 
1406
    cid->ecb = apr_pcalloc(r->pool, sizeof(EXTENSION_CONTROL_BLOCK));
 
1407
    cid->ecb->ConnID = cid;
 
1408
    cid->isa = isa;
 
1409
    cid->r = r;
 
1410
    r->status = 0;
 
1411
 
 
1412
    cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
 
1413
    cid->ecb->dwVersion = isa->report_version;
 
1414
    cid->ecb->dwHttpStatusCode = 0;
 
1415
    strcpy(cid->ecb->lpszLogData, "");
 
1416
    /* TODO: are copies really needed here?
 
1417
     */
 
1418
    cid->ecb->lpszMethod = (char*) r->method;
 
1419
    cid->ecb->lpszQueryString = (char*) apr_table_get(e, "QUERY_STRING");
 
1420
    cid->ecb->lpszPathInfo = (char*) apr_table_get(e, "PATH_INFO");
 
1421
    cid->ecb->lpszPathTranslated = (char*) apr_table_get(e, "PATH_TRANSLATED");
 
1422
    cid->ecb->lpszContentType = (char*) apr_table_get(e, "CONTENT_TYPE");
 
1423
 
 
1424
    /* Set up the callbacks */
 
1425
    cid->ecb->GetServerVariable = GetServerVariable;
 
1426
    cid->ecb->WriteClient = WriteClient;
 
1427
    cid->ecb->ReadClient = ReadClient;
 
1428
    cid->ecb->ServerSupportFunction = ServerSupportFunction;
 
1429
 
 
1430
    /* Set up client input */
 
1431
    res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
 
1432
    if (res) {
 
1433
        isapi_unload(isa, 0);
 
1434
        return res;
 
1435
    }
 
1436
 
 
1437
    if (ap_should_client_block(r)) {
 
1438
        /* Time to start reading the appropriate amount of data,
 
1439
         * and allow the administrator to tweak the number
 
1440
         */
 
1441
        if (r->remaining) {
 
1442
            cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
 
1443
            if (cid->ecb->cbTotalBytes > (apr_uint32_t)cid->dconf.read_ahead_buflen)
 
1444
                cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
 
1445
            else
 
1446
                cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
 
1447
        }
 
1448
        else
 
1449
        {
 
1450
            cid->ecb->cbTotalBytes = 0xffffffff;
 
1451
            cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
 
1452
        }
 
1453
 
 
1454
        cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
 
1455
 
 
1456
        read = 0;
 
1457
        while (read < cid->ecb->cbAvailable &&
 
1458
               ((res = ap_get_client_block(r, (char*)cid->ecb->lpbData + read,
 
1459
                                        cid->ecb->cbAvailable - read)) > 0)) {
 
1460
            read += res;
 
1461
        }
 
1462
 
 
1463
        if (res < 0) {
 
1464
            isapi_unload(isa, 0);
 
1465
            return HTTP_INTERNAL_SERVER_ERROR;
 
1466
        }
 
1467
 
 
1468
        /* Although it's not to spec, IIS seems to null-terminate
 
1469
         * its lpdData string. So we will too.
 
1470
         */
 
1471
        if (res == 0)
 
1472
            cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
 
1473
        else
 
1474
            cid->ecb->cbAvailable = read;
 
1475
        cid->ecb->lpbData[read] = '\0';
 
1476
    }
 
1477
    else {
 
1478
        cid->ecb->cbTotalBytes = 0;
 
1479
        cid->ecb->cbAvailable = 0;
 
1480
        cid->ecb->lpbData = NULL;
 
1481
    }
 
1482
 
 
1483
    /* To emulate async behavior...
 
1484
     *
 
1485
     * We create a cid->completed mutex and lock on it so that the
 
1486
     * app can believe is it running async.
 
1487
     *
 
1488
     * This request completes upon a notification through
 
1489
     * ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION), which
 
1490
     * unlocks this mutex.  If the HttpExtensionProc() returns
 
1491
     * HSE_STATUS_PENDING, we will attempt to gain this lock again
 
1492
     * which may *only* happen once HSE_REQ_DONE_WITH_SESSION has
 
1493
     * unlocked the mutex.
 
1494
     */
 
1495
    if (cid->dconf.fake_async) {
 
1496
        rv = apr_thread_mutex_create(&cid->completed,
 
1497
                                     APR_THREAD_MUTEX_UNNESTED,
 
1498
                                     r->pool);
 
1499
        if (cid->completed && (rv == APR_SUCCESS)) {
 
1500
            rv = apr_thread_mutex_lock(cid->completed);
 
1501
        }
 
1502
 
 
1503
        if (!cid->completed || (rv != APR_SUCCESS)) {
 
1504
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1505
                          "ISAPI: Failed to create completion mutex");
 
1506
            return HTTP_INTERNAL_SERVER_ERROR;
 
1507
        }
 
1508
    }
 
1509
 
 
1510
    /* All right... try and run the sucker */
 
1511
    rv = (*isa->HttpExtensionProc)(cid->ecb);
 
1512
 
 
1513
    /* Check for a log message - and log it */
 
1514
    if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
 
1515
        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
 
1516
                      "ISAPI: %s: %s", r->filename, cid->ecb->lpszLogData);
 
1517
 
 
1518
    switch(rv) {
 
1519
        case 0:  /* Strange, but MS isapi accepts this as success */
 
1520
        case HSE_STATUS_SUCCESS:
 
1521
        case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
 
1522
            /* Ignore the keepalive stuff; Apache handles it just fine without
 
1523
             * the ISAPI Handler's "advice".
 
1524
             * Per Microsoft: "In IIS versions 4.0 and later, the return
 
1525
             * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
 
1526
             * are functionally identical: Keep-Alive connections are
 
1527
             * maintained, if supported by the client."
 
1528
             * ... so we were pat all this time
 
1529
             */
 
1530
            break;
 
1531
 
 
1532
        case HSE_STATUS_PENDING:
 
1533
            /* emulating async behavior...
 
1534
             */
 
1535
            if (cid->completed) {
 
1536
                /* The completion port was locked prior to invoking
 
1537
                 * HttpExtensionProc().  Once we can regain the lock,
 
1538
                 * when ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION)
 
1539
                 * is called by the extension to release the lock,
 
1540
                 * we may finally destroy the request.
 
1541
                 */
 
1542
                (void)apr_thread_mutex_lock(cid->completed);
 
1543
                break;
 
1544
            }
 
1545
            else if (cid->dconf.log_unsupported) {
 
1546
                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1547
                               "ISAPI: asynch I/O result HSE_STATUS_PENDING "
 
1548
                               "from HttpExtensionProc() is not supported: %s",
 
1549
                               r->filename);
 
1550
                 r->status = HTTP_INTERNAL_SERVER_ERROR;
 
1551
            }
 
1552
            break;
 
1553
 
 
1554
        case HSE_STATUS_ERROR:
 
1555
            /* end response if we have yet to do so.
 
1556
             */
 
1557
            r->status = HTTP_INTERNAL_SERVER_ERROR;
 
1558
            break;
 
1559
 
 
1560
        default:
 
1561
            /* TODO: log unrecognized retval for debugging
 
1562
             */
 
1563
             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
1564
                           "ISAPI: return code %d from HttpExtensionProc() "
 
1565
                           "was not not recognized", rv);
 
1566
            r->status = HTTP_INTERNAL_SERVER_ERROR;
 
1567
            break;
 
1568
    }
 
1569
 
 
1570
    /* Flush the response now, including headers-only responses */
 
1571
    if (cid->headers_set) {
 
1572
        conn_rec *c = r->connection;
 
1573
        apr_bucket_brigade *bb;
 
1574
        apr_bucket *b;
 
1575
        apr_status_t rv;
 
1576
 
 
1577
        bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
1578
        b = apr_bucket_eos_create(c->bucket_alloc);
 
1579
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
1580
        b = apr_bucket_flush_create(c->bucket_alloc);
 
1581
        APR_BRIGADE_INSERT_TAIL(bb, b);
 
1582
        rv = ap_pass_brigade(r->output_filters, bb);
 
1583
        cid->response_sent = 1;
 
1584
 
 
1585
        return OK;  /* NOT r->status or cid->r->status, even if it has changed. */
 
1586
    }
 
1587
 
 
1588
    /* As the client returned no error, and if we did not error out
 
1589
     * ourselves, trust dwHttpStatusCode to say something relevant.
 
1590
     */
 
1591
    if (!ap_is_HTTP_SERVER_ERROR(r->status) && cid->ecb->dwHttpStatusCode) {
 
1592
        r->status = cid->ecb->dwHttpStatusCode;
 
1593
    }
 
1594
 
 
1595
    /* For all missing-response situations simply return the status.
 
1596
     * and let the core deal respond to the client.
 
1597
     */
 
1598
    return r->status;
 
1599
}
 
1600
 
 
1601
/**********************************************************
 
1602
 *
 
1603
 *  ISAPI Module Setup Hooks
 
1604
 *
 
1605
 **********************************************************/
 
1606
 
 
1607
static int isapi_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
 
1608
{
 
1609
    apr_status_t rv;
 
1610
 
 
1611
    apr_pool_create_ex(&loaded.pool, pconf, NULL, NULL);
 
1612
    if (!loaded.pool) {
 
1613
        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, NULL,
 
1614
                     "ISAPI: could not create the isapi cache pool");
 
1615
        return APR_EGENERAL;
 
1616
    }
 
1617
 
 
1618
    loaded.hash = apr_hash_make(loaded.pool);
 
1619
    if (!loaded.hash) {
 
1620
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
 
1621
                     "ISAPI: Failed to create module cache");
 
1622
        return APR_EGENERAL;
 
1623
    }
 
1624
 
 
1625
    rv = apr_thread_mutex_create(&loaded.lock, APR_THREAD_MUTEX_DEFAULT,
 
1626
                                 loaded.pool);
 
1627
    if (rv != APR_SUCCESS) {
 
1628
        ap_log_error(APLOG_MARK, rv, 0, NULL,
 
1629
                     "ISAPI: Failed to create module cache lock");
 
1630
        return rv;
 
1631
    }
 
1632
    return OK;
 
1633
}
 
1634
 
 
1635
static void isapi_hooks(apr_pool_t *cont)
 
1636
{
 
1637
    ap_hook_pre_config(isapi_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
 
1638
    ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE);
 
1639
}
 
1640
 
 
1641
module AP_MODULE_DECLARE_DATA isapi_module = {
 
1642
   STANDARD20_MODULE_STUFF,
 
1643
   create_isapi_dir_config,     /* create per-dir config */
 
1644
   merge_isapi_dir_configs,     /* merge per-dir config */
 
1645
   NULL,                        /* server config */
 
1646
   NULL,                        /* merge server config */
 
1647
   isapi_cmds,                  /* command apr_table_t */
 
1648
   isapi_hooks                  /* register hooks */
 
1649
};