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

« back to all changes in this revision

Viewing changes to modules/http/mod_mime.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
 * http_mime.c: Sends/gets MIME headers for requests
 
19
 *
 
20
 * Rob McCool
 
21
 *
 
22
 */
 
23
 
 
24
#include "apr.h"
 
25
#include "apr_strings.h"
 
26
#include "apr_lib.h"
 
27
#include "apr_hash.h"
 
28
 
 
29
#define APR_WANT_STRFUNC
 
30
#include "apr_want.h"
 
31
 
 
32
#include "ap_config.h"
 
33
#include "httpd.h"
 
34
#include "http_config.h"
 
35
#include "http_log.h"
 
36
#include "http_request.h"
 
37
#include "http_protocol.h"
 
38
 
 
39
/* XXXX - fix me / EBCDIC
 
40
 *        there was a cludge here which would use its
 
41
 *        own version apr_isascii(). Indicating that
 
42
 *        on some platforms that might be needed.
 
43
 *
 
44
 *        #define OS_ASC(c) (c)             -- for mere mortals
 
45
 *     or
 
46
 *        #define OS_ASC(c) (ebcdic2ascii[c]) -- for dino's
 
47
 *
 
48
 *        #define apr_isascii(c) ((OS_ASC(c) & 0x80) == 0)
 
49
 */
 
50
 
 
51
/* XXXXX - fix me - See note with NOT_PROXY
 
52
 */
 
53
 
 
54
typedef struct attrib_info {
 
55
    char *name;
 
56
    int   offset;
 
57
} attrib_info;
 
58
 
 
59
/* Information to which an extension can be mapped
 
60
 */
 
61
typedef struct extension_info {
 
62
    char *forced_type;                /* Additional AddTyped stuff */
 
63
    char *encoding_type;              /* Added with AddEncoding... */
 
64
    char *language_type;              /* Added with AddLanguage... */
 
65
    char *handler;                    /* Added with AddHandler... */
 
66
    char *charset_type;               /* Added with AddCharset... */
 
67
    char *input_filters;              /* Added with AddInputFilter... */
 
68
    char *output_filters;             /* Added with AddOutputFilter... */
 
69
} extension_info;
 
70
 
 
71
#define MULTIMATCH_UNSET      0
 
72
#define MULTIMATCH_ANY        1
 
73
#define MULTIMATCH_NEGOTIATED 2
 
74
#define MULTIMATCH_HANDLERS   4
 
75
#define MULTIMATCH_FILTERS    8
 
76
 
 
77
typedef struct {
 
78
    apr_hash_t *extension_mappings;  /* Map from extension name to
 
79
                                      * extension_info structure */
 
80
 
 
81
    apr_array_header_t *remove_mappings; /* A simple list, walked once */
 
82
 
 
83
    char *default_language;     /* Language if no AddLanguage ext found */
 
84
 
 
85
    int multimatch;       /* Extensions to include in multiview matching
 
86
                           * for filenames, e.g. Filters and Handlers
 
87
                           */
 
88
    int use_path_info;    /* If set to 0, only use filename.
 
89
                           * If set to 1, append PATH_INFO to filename for
 
90
                           *   lookups.
 
91
                           * If set to 2, this value is unset and is
 
92
                           *   effectively 0.
 
93
                           */
 
94
} mime_dir_config;
 
95
 
 
96
typedef struct param_s {
 
97
    char *attr;
 
98
    char *val;
 
99
    struct param_s *next;
 
100
} param;
 
101
 
 
102
typedef struct {
 
103
    const char *type;
 
104
    apr_size_t type_len;
 
105
    const char *subtype;
 
106
    apr_size_t subtype_len;
 
107
    param *param;
 
108
} content_type;
 
109
 
 
110
static char tspecial[] = {
 
111
    '(', ')', '<', '>', '@', ',', ';', ':',
 
112
    '\\', '"', '/', '[', ']', '?', '=',
 
113
    '\0'
 
114
};
 
115
 
 
116
module AP_MODULE_DECLARE_DATA mime_module;
 
117
 
 
118
static void *create_mime_dir_config(apr_pool_t *p, char *dummy)
 
119
{
 
120
    mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
 
121
 
 
122
    new->extension_mappings = NULL;
 
123
    new->remove_mappings = NULL;
 
124
 
 
125
    new->default_language = NULL;
 
126
 
 
127
    new->multimatch = MULTIMATCH_UNSET;
 
128
 
 
129
    new->use_path_info = 2;
 
130
 
 
131
    return new;
 
132
}
 
133
/*
 
134
 * Overlay one hash table of extension_mappings onto another
 
135
 */
 
136
static void *overlay_extension_mappings(apr_pool_t *p,
 
137
                                        const void *key,
 
138
                                        apr_ssize_t klen,
 
139
                                        const void *overlay_val,
 
140
                                        const void *base_val,
 
141
                                        const void *data)
 
142
{
 
143
    extension_info *new_info = apr_palloc(p, sizeof(extension_info));
 
144
    const extension_info *overlay_info = (const extension_info *)overlay_val;
 
145
    const extension_info *base_info = (const extension_info *)base_val;
 
146
 
 
147
    memcpy(new_info, base_info, sizeof(extension_info));
 
148
    if (overlay_info->forced_type) {
 
149
        new_info->forced_type = overlay_info->forced_type;
 
150
    }
 
151
    if (overlay_info->encoding_type) {
 
152
        new_info->encoding_type = overlay_info->encoding_type;
 
153
    }
 
154
    if (overlay_info->language_type) {
 
155
        new_info->language_type = overlay_info->language_type;
 
156
    }
 
157
    if (overlay_info->handler) {
 
158
        new_info->handler = overlay_info->handler;
 
159
    }
 
160
    if (overlay_info->charset_type) {
 
161
        new_info->charset_type = overlay_info->charset_type;
 
162
    }
 
163
    if (overlay_info->input_filters) {
 
164
        new_info->input_filters = overlay_info->input_filters;
 
165
    }
 
166
    if (overlay_info->output_filters) {
 
167
        new_info->output_filters = overlay_info->output_filters;
 
168
    }
 
169
 
 
170
    return new_info;
 
171
}
 
172
 
 
173
/* Member is the offset within an extension_info of the pointer to reset
 
174
 */
 
175
static void remove_items(apr_pool_t *p, apr_array_header_t *remove,
 
176
                         apr_hash_t *mappings)
 
177
{
 
178
    attrib_info *suffix = (attrib_info *) remove->elts;
 
179
    int i;
 
180
    for (i = 0; i < remove->nelts; i++) {
 
181
        extension_info *exinfo = apr_hash_get(mappings,
 
182
                                              suffix[i].name,
 
183
                                              APR_HASH_KEY_STRING);
 
184
        if (exinfo && *(const char**)((char *)exinfo + suffix[i].offset)) {
 
185
            extension_info *copyinfo = exinfo;
 
186
            exinfo = (extension_info*)apr_palloc(p, sizeof(*exinfo));
 
187
            apr_hash_set(mappings, suffix[i].name,
 
188
                         APR_HASH_KEY_STRING, exinfo);
 
189
            memcpy(exinfo, copyinfo, sizeof(*exinfo));
 
190
            *(const char**)((char *)exinfo + suffix[i].offset) = NULL;
 
191
        }
 
192
    }
 
193
}
 
194
 
 
195
static void *merge_mime_dir_configs(apr_pool_t *p, void *basev, void *addv)
 
196
{
 
197
    mime_dir_config *base = (mime_dir_config *)basev;
 
198
    mime_dir_config *add = (mime_dir_config *)addv;
 
199
    mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
 
200
 
 
201
    if (base->extension_mappings && add->extension_mappings) {
 
202
        new->extension_mappings = apr_hash_merge(p, add->extension_mappings,
 
203
                                                 base->extension_mappings,
 
204
                                                 overlay_extension_mappings,
 
205
                                                 NULL);
 
206
    }
 
207
    else {
 
208
        if (base->extension_mappings == NULL) {
 
209
            new->extension_mappings = add->extension_mappings;
 
210
        }
 
211
        else {
 
212
            new->extension_mappings = base->extension_mappings;
 
213
        }
 
214
        /* We may not be merging the tables, but if we potentially will change
 
215
         * an exinfo member, then we are about to trounce it anyways.
 
216
         * We must have a copy for safety.
 
217
         */
 
218
        if (new->extension_mappings && add->remove_mappings) {
 
219
            new->extension_mappings =
 
220
                apr_hash_copy(p, new->extension_mappings);
 
221
        }
 
222
    }
 
223
 
 
224
    if (new->extension_mappings) {
 
225
        if (add->remove_mappings)
 
226
            remove_items(p, add->remove_mappings, new->extension_mappings);
 
227
    }
 
228
    new->remove_mappings = NULL;
 
229
 
 
230
    new->default_language = add->default_language ?
 
231
        add->default_language : base->default_language;
 
232
 
 
233
    new->multimatch = (add->multimatch != MULTIMATCH_UNSET) ?
 
234
        add->multimatch : base->multimatch;
 
235
 
 
236
    if ((add->use_path_info & 2) == 0) {
 
237
        new->use_path_info = add->use_path_info;
 
238
    }
 
239
    else {
 
240
        new->use_path_info = base->use_path_info;
 
241
    }
 
242
 
 
243
    return new;
 
244
}
 
245
 
 
246
static const char *add_extension_info(cmd_parms *cmd, void *m_,
 
247
                                      const char *value_, const char* ext)
 
248
{
 
249
    mime_dir_config *m=m_;
 
250
    extension_info *exinfo;
 
251
    int offset = (int) (long) cmd->info;
 
252
    char *key = apr_pstrdup(cmd->temp_pool, ext);
 
253
    char *value = apr_pstrdup(cmd->pool, value_);
 
254
    ap_str_tolower(value);
 
255
    ap_str_tolower(key);
 
256
 
 
257
    if (*key == '.') {
 
258
        ++key;
 
259
    }
 
260
    if (!m->extension_mappings) {
 
261
        m->extension_mappings = apr_hash_make(cmd->pool);
 
262
        exinfo = NULL;
 
263
    }
 
264
    else {
 
265
        exinfo = (extension_info*)apr_hash_get(m->extension_mappings, key,
 
266
                                               APR_HASH_KEY_STRING);
 
267
    }
 
268
    if (!exinfo) {
 
269
        exinfo = apr_pcalloc(cmd->pool, sizeof(extension_info));
 
270
        key = apr_pstrdup(cmd->pool, key);
 
271
        apr_hash_set(m->extension_mappings, key, APR_HASH_KEY_STRING, exinfo);
 
272
    }
 
273
    *(const char**)((char *)exinfo + offset) = value;
 
274
    return NULL;
 
275
}
 
276
 
 
277
/*
 
278
 * Note handler names are un-added with each per_dir_config merge.
 
279
 * This keeps the association from being inherited, but not
 
280
 * from being re-added at a subordinate level.
 
281
 */
 
282
static const char *remove_extension_info(cmd_parms *cmd, void *m_,
 
283
                                         const char *ext)
 
284
{
 
285
    mime_dir_config *m = (mime_dir_config *) m_;
 
286
    attrib_info *suffix;
 
287
    if (*ext == '.') {
 
288
        ++ext;
 
289
    }
 
290
    if (!m->remove_mappings) {
 
291
        m->remove_mappings = apr_array_make(cmd->pool, 4, sizeof(*suffix));
 
292
    }
 
293
    suffix = (attrib_info *)apr_array_push(m->remove_mappings);
 
294
    suffix->name = apr_pstrdup(cmd->pool, ext);
 
295
    ap_str_tolower(suffix->name);
 
296
    suffix->offset = (int) (long) cmd->info;
 
297
    return NULL;
 
298
}
 
299
 
 
300
/* The sole bit of server configuration that the MIME module has is
 
301
 * the name of its config file, so...
 
302
 */
 
303
 
 
304
static const char *set_types_config(cmd_parms *cmd, void *dummy,
 
305
                                    const char *arg)
 
306
{
 
307
    ap_set_module_config(cmd->server->module_config, &mime_module,
 
308
                         (void *)arg);
 
309
    return NULL;
 
310
}
 
311
 
 
312
static const char *multiviews_match(cmd_parms *cmd, void *m_,
 
313
                                    const char *include)
 
314
{
 
315
    mime_dir_config *m = (mime_dir_config *) m_;
 
316
 
 
317
    if (strcasecmp(include, "Any") == 0) {
 
318
        if (m->multimatch && (m->multimatch & ~MULTIMATCH_ANY)) {
 
319
            return "Any is incompatible with NegotiatedOnly, "
 
320
                   "Filters and Handlers";
 
321
        }
 
322
        m->multimatch |= MULTIMATCH_ANY;
 
323
    }
 
324
    else if (strcasecmp(include, "NegotiatedOnly") == 0) {
 
325
        if (m->multimatch && (m->multimatch & ~MULTIMATCH_NEGOTIATED)) {
 
326
            return "NegotiatedOnly is incompatible with Any, "
 
327
                   "Filters and Handlers";
 
328
        }
 
329
        m->multimatch |= MULTIMATCH_NEGOTIATED;
 
330
    }
 
331
    else if (strcasecmp(include, "Filters") == 0) {
 
332
        if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
 
333
                                             | MULTIMATCH_ANY))) {
 
334
            return "Filters is incompatible with Any and NegotiatedOnly";
 
335
        }
 
336
        m->multimatch |= MULTIMATCH_FILTERS;
 
337
    }
 
338
    else if (strcasecmp(include, "Handlers") == 0) {
 
339
        if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
 
340
                                             | MULTIMATCH_ANY))) {
 
341
            return "Handlers is incompatible with Any and NegotiatedOnly";
 
342
        }
 
343
        m->multimatch |= MULTIMATCH_HANDLERS;
 
344
    }
 
345
    else {
 
346
        return apr_psprintf(cmd->pool, "Unrecognized option '%s'", include);
 
347
    }
 
348
 
 
349
    return NULL;
 
350
}
 
351
 
 
352
static const command_rec mime_cmds[] =
 
353
{
 
354
    AP_INIT_ITERATE2("AddCharset", add_extension_info,
 
355
        (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
 
356
        "a charset (e.g., iso-2022-jp), followed by one or more "
 
357
        "file extensions"),
 
358
    AP_INIT_ITERATE2("AddEncoding", add_extension_info,
 
359
        (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
 
360
        "an encoding (e.g., gzip), followed by one or more file extensions"),
 
361
    AP_INIT_ITERATE2("AddHandler", add_extension_info,
 
362
        (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
 
363
        "a handler name followed by one or more file extensions"),
 
364
    AP_INIT_ITERATE2("AddInputFilter", add_extension_info,
 
365
        (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
 
366
        "input filter name (or ; delimited names) followed by one or "
 
367
        "more file extensions"),
 
368
    AP_INIT_ITERATE2("AddLanguage", add_extension_info,
 
369
        (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
 
370
        "a language (e.g., fr), followed by one or more file extensions"),
 
371
    AP_INIT_ITERATE2("AddOutputFilter", add_extension_info,
 
372
        (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
 
373
        "output filter name (or ; delimited names) followed by one or "
 
374
        "more file extensions"),
 
375
    AP_INIT_ITERATE2("AddType", add_extension_info,
 
376
        (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
 
377
        "a mime type followed by one or more file extensions"),
 
378
    AP_INIT_TAKE1("DefaultLanguage", ap_set_string_slot,
 
379
        (void*)APR_OFFSETOF(mime_dir_config, default_language), OR_FILEINFO,
 
380
        "language to use for documents with no other language file extension"),
 
381
    AP_INIT_ITERATE("MultiviewsMatch", multiviews_match, NULL, OR_FILEINFO,
 
382
        "NegotiatedOnly (default), Handlers and/or Filters, or Any"),
 
383
    AP_INIT_ITERATE("RemoveCharset", remove_extension_info,
 
384
        (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
 
385
        "one or more file extensions"),
 
386
    AP_INIT_ITERATE("RemoveEncoding", remove_extension_info,
 
387
        (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
 
388
        "one or more file extensions"),
 
389
    AP_INIT_ITERATE("RemoveHandler", remove_extension_info,
 
390
        (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
 
391
        "one or more file extensions"),
 
392
    AP_INIT_ITERATE("RemoveInputFilter", remove_extension_info,
 
393
        (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
 
394
        "one or more file extensions"),
 
395
    AP_INIT_ITERATE("RemoveLanguage", remove_extension_info,
 
396
        (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
 
397
        "one or more file extensions"),
 
398
    AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info,
 
399
        (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
 
400
        "one or more file extensions"),
 
401
    AP_INIT_ITERATE("RemoveType", remove_extension_info,
 
402
        (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
 
403
        "one or more file extensions"),
 
404
    AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
 
405
        "the MIME types config file"),
 
406
    AP_INIT_FLAG("ModMimeUsePathInfo", ap_set_flag_slot,
 
407
        (void *)APR_OFFSETOF(mime_dir_config, use_path_info), ACCESS_CONF,
 
408
        "Set to 'yes' to allow mod_mime to use path info for type checking"),
 
409
    {NULL}
 
410
};
 
411
 
 
412
static apr_hash_t *mime_type_extensions;
 
413
 
 
414
static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
 
415
{
 
416
    ap_configfile_t *f;
 
417
    char l[MAX_STRING_LEN];
 
418
    const char *types_confname = ap_get_module_config(s->module_config,
 
419
                                                      &mime_module);
 
420
    apr_status_t status;
 
421
 
 
422
    if (!types_confname) {
 
423
        types_confname = AP_TYPES_CONFIG_FILE;
 
424
    }
 
425
 
 
426
    types_confname = ap_server_root_relative(p, types_confname);
 
427
    if (!types_confname) {
 
428
        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
 
429
                     "Invalid mime types config path %s",
 
430
                     (const char *)ap_get_module_config(s->module_config,
 
431
                                                        &mime_module));
 
432
        return HTTP_INTERNAL_SERVER_ERROR;
 
433
    }
 
434
    if ((status = ap_pcfg_openfile(&f, ptemp, types_confname))
 
435
                != APR_SUCCESS) {
 
436
        ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
 
437
                     "could not open mime types config file %s.",
 
438
                     types_confname);
 
439
        return HTTP_INTERNAL_SERVER_ERROR;
 
440
    }
 
441
 
 
442
    mime_type_extensions = apr_hash_make(p);
 
443
 
 
444
    while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
 
445
        const char *ll = l, *ct;
 
446
 
 
447
        if (l[0] == '#') {
 
448
            continue;
 
449
        }
 
450
        ct = ap_getword_conf(p, &ll);
 
451
 
 
452
        while (ll[0]) {
 
453
            char *ext = ap_getword_conf(p, &ll);
 
454
            ap_str_tolower(ext);
 
455
            apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
 
456
        }
 
457
    }
 
458
    ap_cfg_closefile(f);
 
459
    return OK;
 
460
}
 
461
 
 
462
static const char *zap_sp(const char *s)
 
463
{
 
464
    if (s == NULL) {
 
465
        return (NULL);
 
466
    }
 
467
    if (*s == '\0') {
 
468
        return (s);
 
469
    }
 
470
 
 
471
    /* skip prefixed white space */
 
472
    for (; *s == ' ' || *s == '\t' || *s == '\n'; s++)
 
473
        ;
 
474
 
 
475
    return (s);
 
476
}
 
477
 
 
478
static char *zap_sp_and_dup(apr_pool_t *p, const char *start,
 
479
                            const char *end, apr_size_t *len)
 
480
{
 
481
    while ((start < end) && apr_isspace(*start)) {
 
482
        start++;
 
483
    }
 
484
    while ((end > start) && apr_isspace(*(end - 1))) {
 
485
        end--;
 
486
    }
 
487
    if (len) {
 
488
        *len = end - start;
 
489
    }
 
490
    return apr_pstrmemdup(p, start, end - start);
 
491
}
 
492
 
 
493
static int is_token(char c)
 
494
{
 
495
    int res;
 
496
 
 
497
    res = (apr_isascii(c) && apr_isgraph(c)
 
498
           && (strchr(tspecial, c) == NULL)) ? 1 : -1;
 
499
    return res;
 
500
}
 
501
 
 
502
static int is_qtext(char c)
 
503
{
 
504
    int res;
 
505
 
 
506
    res = (apr_isascii(c) && (c != '"') && (c != '\\') && (c != '\n'))
 
507
        ? 1 : -1;
 
508
    return res;
 
509
}
 
510
 
 
511
static int is_quoted_pair(const char *s)
 
512
{
 
513
    int res = -1;
 
514
    int c;
 
515
 
 
516
    if (((s + 1) != NULL) && (*s == '\\')) {
 
517
        c = (int) *(s + 1);
 
518
        if (apr_isascii(c)) {
 
519
            res = 1;
 
520
        }
 
521
    }
 
522
    return (res);
 
523
}
 
524
 
 
525
static content_type *analyze_ct(request_rec *r, const char *s)
 
526
{
 
527
    const char *cp, *mp;
 
528
    char *attribute, *value;
 
529
    int quoted = 0;
 
530
    server_rec * ss = r->server;
 
531
    apr_pool_t * p = r->pool;
 
532
 
 
533
    content_type *ctp;
 
534
    param *pp, *npp;
 
535
 
 
536
    /* initialize ctp */
 
537
    ctp = (content_type *)apr_palloc(p, sizeof(content_type));
 
538
    ctp->type = NULL;
 
539
    ctp->subtype = NULL;
 
540
    ctp->param = NULL;
 
541
 
 
542
    mp = s;
 
543
 
 
544
    /* getting a type */
 
545
    cp = mp;
 
546
    while (apr_isspace(*cp)) {
 
547
        cp++;
 
548
    }
 
549
    if (!*cp) {
 
550
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
551
                     "mod_mime: analyze_ct: cannot get media type from '%s'",
 
552
                     (const char *) mp);
 
553
        return (NULL);
 
554
    }
 
555
    ctp->type = cp;
 
556
    do {
 
557
        cp++;
 
558
    } while (*cp && (*cp != '/') && !apr_isspace(*cp) && (*cp != ';'));
 
559
    if (!*cp || (*cp == ';')) {
 
560
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
561
                     "Cannot get media type from '%s'",
 
562
                     (const char *) mp);
 
563
        return (NULL);
 
564
    }
 
565
    while (apr_isspace(*cp)) {
 
566
        cp++;
 
567
    }
 
568
    if (*cp != '/') {
 
569
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
570
                     "mod_mime: analyze_ct: cannot get media type from '%s'",
 
571
                     (const char *) mp);
 
572
        return (NULL);
 
573
    }
 
574
    ctp->type_len = cp - ctp->type;
 
575
 
 
576
    cp++; /* skip the '/' */
 
577
 
 
578
    /* getting a subtype */
 
579
    while (apr_isspace(*cp)) {
 
580
        cp++;
 
581
    }
 
582
    if (!*cp) {
 
583
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
584
                     "Cannot get media subtype.");
 
585
        return (NULL);
 
586
    }
 
587
    ctp->subtype = cp;
 
588
    do {
 
589
        cp++;
 
590
    } while (*cp && !apr_isspace(*cp) && (*cp != ';'));
 
591
    ctp->subtype_len = cp - ctp->subtype;
 
592
    while (apr_isspace(*cp)) {
 
593
        cp++;
 
594
    }
 
595
 
 
596
    if (*cp == '\0') {
 
597
        return (ctp);
 
598
    }
 
599
 
 
600
    /* getting parameters */
 
601
    cp++; /* skip the ';' */
 
602
    cp = zap_sp(cp);
 
603
    if (cp == NULL || *cp == '\0') {
 
604
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
605
                     "Cannot get media parameter.");
 
606
        return (NULL);
 
607
    }
 
608
    mp = cp;
 
609
    attribute = NULL;
 
610
    value = NULL;
 
611
 
 
612
    while (cp != NULL && *cp != '\0') {
 
613
        if (attribute == NULL) {
 
614
            if (is_token(*cp) > 0) {
 
615
                cp++;
 
616
                continue;
 
617
            }
 
618
            else if (*cp == ' ' || *cp == '\t' || *cp == '\n') {
 
619
                cp++;
 
620
                continue;
 
621
            }
 
622
            else if (*cp == '=') {
 
623
                attribute = zap_sp_and_dup(p, mp, cp, NULL);
 
624
                if (attribute == NULL || *attribute == '\0') {
 
625
                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
626
                                 "Cannot get media parameter.");
 
627
                    return (NULL);
 
628
                }
 
629
                cp++;
 
630
                cp = zap_sp(cp);
 
631
                if (cp == NULL || *cp == '\0') {
 
632
                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
633
                                 "Cannot get media parameter.");
 
634
                    return (NULL);
 
635
                }
 
636
                mp = cp;
 
637
                continue;
 
638
            }
 
639
            else {
 
640
                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
641
                             "Cannot get media parameter.");
 
642
                return (NULL);
 
643
            }
 
644
        }
 
645
        else {
 
646
            if (mp == cp) {
 
647
                if (*cp == '"') {
 
648
                    quoted = 1;
 
649
                    cp++;
 
650
                }
 
651
                else {
 
652
                    quoted = 0;
 
653
                }
 
654
            }
 
655
            if (quoted > 0) {
 
656
                while (quoted && *cp != '\0') {
 
657
                    if (is_qtext(*cp) > 0) {
 
658
                        cp++;
 
659
                    }
 
660
                    else if (is_quoted_pair(cp) > 0) {
 
661
                        cp += 2;
 
662
                    }
 
663
                    else if (*cp == '"') {
 
664
                        cp++;
 
665
                        while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
 
666
                            cp++;
 
667
                        }
 
668
                        if (*cp != ';' && *cp != '\0') {
 
669
                            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
670
                                         "Cannot get media parameter.");
 
671
                            return(NULL);
 
672
                        }
 
673
                        quoted = 0;
 
674
                    }
 
675
                    else {
 
676
                        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
677
                                     "Cannot get media parameter.");
 
678
                        return (NULL);
 
679
                    }
 
680
                }
 
681
            }
 
682
            else {
 
683
                while (1) {
 
684
                    if (is_token(*cp) > 0) {
 
685
                        cp++;
 
686
                    }
 
687
                    else if (*cp == '\0' || *cp == ';') {
 
688
                        break;
 
689
                    }
 
690
                    else {
 
691
                        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
692
                                     "Cannot get media parameter.");
 
693
                        return (NULL);
 
694
                    }
 
695
                }
 
696
            }
 
697
            value = zap_sp_and_dup(p, mp, cp, NULL);
 
698
            if (value == NULL || *value == '\0') {
 
699
                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
 
700
                             "Cannot get media parameter.");
 
701
                return (NULL);
 
702
            }
 
703
 
 
704
            pp = apr_palloc(p, sizeof(param));
 
705
            pp->attr = attribute;
 
706
            pp->val = value;
 
707
            pp->next = NULL;
 
708
 
 
709
            if (ctp->param == NULL) {
 
710
                ctp->param = pp;
 
711
            }
 
712
            else {
 
713
                npp = ctp->param;
 
714
                while (npp->next) {
 
715
                    npp = npp->next;
 
716
                }
 
717
                npp->next = pp;
 
718
            }
 
719
            quoted = 0;
 
720
            attribute = NULL;
 
721
            value = NULL;
 
722
            if (*cp == '\0') {
 
723
                break;
 
724
            }
 
725
            cp++;
 
726
            mp = cp;
 
727
        }
 
728
    }
 
729
    return (ctp);
 
730
}
 
731
 
 
732
/*
 
733
 * find_ct is the hook routine for determining content-type and other
 
734
 * MIME-related metadata.  It assumes that r->filename has already been
 
735
 * set and stat has been called for r->finfo.  It also assumes that the
 
736
 * non-path base file name is not the empty string unless it is a dir.
 
737
 */
 
738
static int find_ct(request_rec *r)
 
739
{
 
740
    mime_dir_config *conf;
 
741
    apr_array_header_t *exception_list;
 
742
    char *ext;
 
743
    const char *fn, *type, *charset = NULL, *resource_name;
 
744
    int found_metadata = 0;
 
745
 
 
746
    if (r->finfo.filetype == APR_DIR) {
 
747
        ap_set_content_type(r, DIR_MAGIC_TYPE);
 
748
        return OK;
 
749
    }
 
750
 
 
751
    if (!r->filename) {
 
752
        return DECLINED;
 
753
    }
 
754
 
 
755
    conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
 
756
                                                   &mime_module);
 
757
    exception_list = apr_array_make(r->pool, 2, sizeof(char *));
 
758
 
 
759
    /* If use_path_info is explicitly set to on (value & 1 == 1), append. */
 
760
    if (conf->use_path_info & 1) {
 
761
        resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
 
762
    }
 
763
    else {
 
764
        resource_name = r->filename;
 
765
    }
 
766
 
 
767
    /* Always drop the path leading up to the file name.
 
768
     */
 
769
    if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
 
770
        fn = resource_name;
 
771
    }
 
772
    else {
 
773
        ++fn;
 
774
    }
 
775
 
 
776
    /* The exception list keeps track of those filename components that
 
777
     * are not associated with extensions indicating metadata.
 
778
     * The base name is always the first exception (i.e., "txt.html" has
 
779
     * a basename of "txt" even though it might look like an extension).
 
780
     */
 
781
    ext = ap_getword(r->pool, &fn, '.');
 
782
    *((const char **)apr_array_push(exception_list)) = ext;
 
783
 
 
784
    /* Parse filename extensions which can be in any order
 
785
     */
 
786
    while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
 
787
        const extension_info *exinfo = NULL;
 
788
        int found;
 
789
 
 
790
        if (*ext == '\0') {  /* ignore empty extensions "bad..html" */
 
791
            continue;
 
792
        }
 
793
 
 
794
        found = 0;
 
795
 
 
796
        ap_str_tolower(ext);
 
797
 
 
798
        if (conf->extension_mappings != NULL) {
 
799
            exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
 
800
                                                   ext, APR_HASH_KEY_STRING);
 
801
        }
 
802
 
 
803
        if (exinfo == NULL || !exinfo->forced_type) {
 
804
            if ((type = apr_hash_get(mime_type_extensions, ext,
 
805
                                     APR_HASH_KEY_STRING)) != NULL) {
 
806
                ap_set_content_type(r, (char*) type);
 
807
                found = 1;
 
808
            }
 
809
        }
 
810
 
 
811
        if (exinfo != NULL) {
 
812
 
 
813
            if (exinfo->forced_type) {
 
814
                ap_set_content_type(r, exinfo->forced_type);
 
815
                found = 1;
 
816
            }
 
817
 
 
818
            if (exinfo->charset_type) {
 
819
                charset = exinfo->charset_type;
 
820
                found = 1;
 
821
            }
 
822
            if (exinfo->language_type) {
 
823
                if (!r->content_languages) {
 
824
                    r->content_languages = apr_array_make(r->pool, 2,
 
825
                                                          sizeof(char *));
 
826
                }
 
827
                *((const char **)apr_array_push(r->content_languages))
 
828
                    = exinfo->language_type;
 
829
                found = 1;
 
830
            }
 
831
            if (exinfo->encoding_type) {
 
832
                if (!r->content_encoding) {
 
833
                    r->content_encoding = exinfo->encoding_type;
 
834
                }
 
835
                else {
 
836
                    /* XXX should eliminate duplicate entities
 
837
                     *
 
838
                     * ah no. Order is important and double encoding is neither
 
839
                     * forbidden nor impossible. -- nd
 
840
                     */
 
841
                    r->content_encoding = apr_pstrcat(r->pool,
 
842
                                                      r->content_encoding,
 
843
                                                      ", ",
 
844
                                                      exinfo->encoding_type,
 
845
                                                      NULL);
 
846
                }
 
847
                found = 1;
 
848
            }
 
849
            /* The following extensions are not 'Found'.  That is, they don't
 
850
             * make any contribution to metadata negotation, so they must have
 
851
             * been explicitly requested by name.
 
852
             */
 
853
            if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
 
854
                r->handler = exinfo->handler;
 
855
                if (conf->multimatch & MULTIMATCH_HANDLERS) {
 
856
                    found = 1;
 
857
                }
 
858
            }
 
859
            /* XXX Two significant problems; 1, we don't check to see if we are
 
860
             * setting redundant filters.    2, we insert these in the types config
 
861
             * hook, which may be too early (dunno.)
 
862
             */
 
863
            if (exinfo->input_filters && r->proxyreq == PROXYREQ_NONE) {
 
864
                const char *filter, *filters = exinfo->input_filters;
 
865
                while (*filters
 
866
                    && (filter = ap_getword(r->pool, &filters, ';'))) {
 
867
                    ap_add_input_filter(filter, NULL, r, r->connection);
 
868
                }
 
869
                if (conf->multimatch & MULTIMATCH_FILTERS) {
 
870
                    found = 1;
 
871
                }
 
872
            }
 
873
            if (exinfo->output_filters && r->proxyreq == PROXYREQ_NONE) {
 
874
                const char *filter, *filters = exinfo->output_filters;
 
875
                while (*filters
 
876
                    && (filter = ap_getword(r->pool, &filters, ';'))) {
 
877
                    ap_add_output_filter(filter, NULL, r, r->connection);
 
878
                }
 
879
                if (conf->multimatch & MULTIMATCH_FILTERS) {
 
880
                    found = 1;
 
881
                }
 
882
            }
 
883
        }
 
884
 
 
885
        if (found || (conf->multimatch & MULTIMATCH_ANY)) {
 
886
            found_metadata = 1;
 
887
        }
 
888
        else {
 
889
            *((const char **) apr_array_push(exception_list)) = ext;
 
890
        }
 
891
    }
 
892
 
 
893
    /*
 
894
     * Need to set a notes entry on r for unrecognized elements.
 
895
     * Somebody better claim them!  If we did absolutely nothing,
 
896
     * skip the notes to alert mod_negotiation we are clueless.
 
897
     */
 
898
    if (found_metadata) {
 
899
        apr_table_setn(r->notes, "ap-mime-exceptions-list",
 
900
                       (void *)exception_list);
 
901
    }
 
902
 
 
903
    if (r->content_type) {
 
904
        content_type *ctp;
 
905
        int override = 0;
 
906
 
 
907
        if ((ctp = analyze_ct(r, r->content_type))) {
 
908
            param *pp = ctp->param;
 
909
            char *base_content_type = apr_palloc(r->pool, ctp->type_len +
 
910
                                                 ctp->subtype_len +
 
911
                                                 sizeof("/"));
 
912
            char *tmp = base_content_type;
 
913
            memcpy(tmp, ctp->type, ctp->type_len);
 
914
            tmp += ctp->type_len;
 
915
            *tmp++ = '/';
 
916
            memcpy(tmp, ctp->subtype, ctp->subtype_len);
 
917
            tmp += ctp->subtype_len;
 
918
            *tmp = 0;
 
919
            ap_set_content_type(r, base_content_type);
 
920
            while (pp != NULL) {
 
921
                if (charset && !strcmp(pp->attr, "charset")) {
 
922
                    if (!override) {
 
923
                        ap_set_content_type(r,
 
924
                                            apr_pstrcat(r->pool,
 
925
                                                        r->content_type,
 
926
                                                        "; charset=",
 
927
                                                        charset,
 
928
                                                        NULL));
 
929
                        override = 1;
 
930
                    }
 
931
                }
 
932
                else {
 
933
                    ap_set_content_type(r,
 
934
                                        apr_pstrcat(r->pool,
 
935
                                                    r->content_type,
 
936
                                                    "; ", pp->attr,
 
937
                                                    "=", pp->val,
 
938
                                                    NULL));
 
939
                }
 
940
                pp = pp->next;
 
941
            }
 
942
            if (charset && !override) {
 
943
                ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
 
944
                                                   "; charset=", charset,
 
945
                                                   NULL));
 
946
            }
 
947
        }
 
948
    }
 
949
 
 
950
    /* Set default language, if none was specified by the extensions
 
951
     * and we have a DefaultLanguage setting in force
 
952
     */
 
953
 
 
954
    if (!r->content_languages && conf->default_language) {
 
955
        const char **new;
 
956
 
 
957
        if (!r->content_languages) {
 
958
            r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
 
959
        }
 
960
        new = (const char **)apr_array_push(r->content_languages);
 
961
        *new = conf->default_language;
 
962
    }
 
963
 
 
964
    if (!r->content_type) {
 
965
        return DECLINED;
 
966
    }
 
967
 
 
968
    return OK;
 
969
}
 
970
 
 
971
static void register_hooks(apr_pool_t *p)
 
972
{
 
973
    ap_hook_post_config(mime_post_config,NULL,NULL,APR_HOOK_MIDDLE);
 
974
    ap_hook_type_checker(find_ct,NULL,NULL,APR_HOOK_MIDDLE);
 
975
    /*
 
976
     * this hook seems redundant ... is there any reason a type checker isn't
 
977
     * allowed to do this already?  I'd think that fixups in general would be
 
978
     * the last opportunity to get the filters right.
 
979
     * ap_hook_insert_filter(mime_insert_filters,NULL,NULL,APR_HOOK_MIDDLE);
 
980
     */
 
981
}
 
982
 
 
983
module AP_MODULE_DECLARE_DATA mime_module = {
 
984
    STANDARD20_MODULE_STUFF,
 
985
    create_mime_dir_config,     /* create per-directory config structure */
 
986
    merge_mime_dir_configs,     /* merge per-directory config structures */
 
987
    NULL,                       /* create per-server config structure */
 
988
    NULL,                       /* merge per-server config structures */
 
989
    mime_cmds,                  /* command apr_table_t */
 
990
    register_hooks              /* register hooks */
 
991
};