]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Modified mod_mime and mod_negotiation to prevent mod_negotiation
authorBill Stoddard <stoddard@apache.org>
Tue, 2 Oct 2001 21:01:31 +0000 (21:01 +0000)
committerBill Stoddard <stoddard@apache.org>
Tue, 2 Oct 2001 21:01:31 +0000 (21:01 +0000)
from serving any multiview variant containing one or more
'unknown' filename extensions. In PR #8130, mod_negotiation was
incorrectly serving index.html.zh.Big5 when better variants were
available. The httpd.conf file on the failing server did not have
an AddLanguage directive for .zh, which caused mod_mime to loose
the file_type information it gleened from parsing the .html
extension. The absence of any language preferences, either in
the browser or configured on the server, caused mod_negotiation
to consider all the variants equivalent. When that occurs,
mod_negotiation picks the 'smallest' variant available, which
just happened to be  index.html.zh.Big5.
[Bill Stoddard, Bill Rowe] PR #8130

PR: 8130

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@91243 13f79535-47bb-0310-9956-ffa450edef68

src/CHANGES
src/modules/standard/mod_mime.c
src/modules/standard/mod_negotiation.c

index 584af9fb7932c02f54a6b0c0c06a30574cc63a93..c938de24b37f94c9a5bfce4682df7808b3123e7e 100644 (file)
@@ -1,4 +1,18 @@
 Changes with Apache 1.3.21
+  *) Modified mod_mime and mod_negotiation to prevent mod_negotiation 
+     from serving any multiview variant containing one or more 
+     'unknown' filename extensions. In PR #8130, mod_negotiation was 
+     incorrectly serving index.html.zh.Big5 when better variants were 
+     available. The httpd.conf file on the failing server did not have 
+     an AddLanguage directive for .zh, which caused mod_mime to loose
+     the file_type information it gleened from parsing the .html
+     extension. The absence of any language preferences, either in
+     the browser or configured on the server, caused mod_negotiation
+     to consider all the variants equivalent. When that occurs, 
+     mod_negotiation picks the 'smallest' variant available, which
+     just happened to be  index.html.zh.Big5.
+     [Bill Stoddard, Bill Rowe] PR #8130
+
   *) Security: Close autoindex /?M=D directory listing hole reported
      in bugtraq id 3009.  In some configurations where multiviews and 
      indexes are enabled for a directory, requesting URI /?M=D could
index e996ad905e3afb217a8a2bd04e6488737bdbf4a4..c6c254fbdca1628071ed2ec50855af446cc43974 100644 (file)
@@ -615,27 +615,38 @@ static content_type *analyze_ct(pool *p, char *s)
 
 static int find_ct(request_rec *r)
 {
-    const char *fn = strrchr(r->filename, '/');
-    mime_dir_config *conf =
-    (mime_dir_config *) ap_get_module_config(r->per_dir_config, &mime_module);
+    mime_dir_config *conf;
+    array_header *exception_list;
+    const char *fn;
     char *ext;
-    const char *orighandler = r->handler;
     const char *type;
     const char *charset = NULL;
+    int found_metadata = 0;
 
     if (S_ISDIR(r->finfo.st_mode)) {
         r->content_type = DIR_MAGIC_TYPE;
         return OK;
     }
 
-    /* TM -- FIXME
-     * if r->filename does not contain a '/', the following passes a null
-     * pointer to getword, causing a SEGV ..
-     */
+    conf = (mime_dir_config *) ap_get_module_config(r->per_dir_config, 
+                                                    &mime_module);
+
+    exception_list = ap_make_array(r->pool, 2, sizeof(char *));
 
-    if (fn == NULL) {
+    /* Always drop the leading element */
+    fn = strrchr(r->filename, '/');
+    if (fn == NULL)
        fn = r->filename;
-    }
+    else
+        ++fn;
+
+    /* The exception list keeps track of those filename components that
+     * are not associated with extensions indicating metadata.
+     * The base name is always the first exception (i.e., "txt.html" has
+     * a basename of "txt" even though it might look like an extension).
+     */
+    ext = ap_getword(r->pool, &fn, '.');
+    *((const char **) ap_push_array(exception_list)) = ext;
 
     /* Parse filename extensions, which can be in any order */
     while ((ext = ap_getword(r->pool, &fn, '.')) && *ext) {
@@ -683,19 +694,19 @@ static int find_ct(request_rec *r)
             found = 1;
         }
 
-        /* This is to deal with cases such as foo.gif.bak, which we want
-         * to not have a type. So if we find an unknown extension, we
-         * zap the type/language/encoding and reset the handler
-         */
-
-        if (!found) {
-            r->content_type = NULL;
-            r->content_language = NULL;
-            r->content_languages = NULL;
-            r->content_encoding = NULL;
-            r->handler = orighandler;
-           charset = NULL;
-       }
+        if (found)
+            found_metadata = 1;
+        else
+            *((const char **) ap_push_array(exception_list)) = ext;
+    }
+
+    /* Need to see a notes entry on r for unrecognized elements.
+     * Somebody better claim them! If we did absolutly nothing,
+     * skip the notes to alert mod_negotiation we are clueless.
+     */
+    if (found_metadata) {
+        ap_table_setn(r->notes, "ap-mime-exceptions-list",
+                      (void *) exception_list);
     }
 
     if (r->content_type) {
index 629660b8f04843454adcaf292bcc0161ba7e2b0e..44863b076da4786f1f04909b85c175b8602cab36 100644 (file)
@@ -922,10 +922,10 @@ static int read_types_multi(negotiation_state *neg)
     forbidden.all = 1;
 
     while ((dir_entry = readdir(dirp))) {
+        array_header *exception_list;
         request_rec *sub_req;
 
         /* Do we have a match? */
-
         if (strncmp(dir_entry->d_name, filp, prefix_len)) {
             continue;
         }
@@ -955,7 +955,55 @@ static int read_types_multi(negotiation_state *neg)
         else if (sub_req->status == HTTP_FORBIDDEN)
             forbidden.any = 1;
 
-        if (sub_req->status != HTTP_OK || !sub_req->content_type) {
+        /* 
+         * mod_mime will _always_ provide us the base name in the
+         * ap-mime-exception-list, if it processed anything.  If
+         * this list is empty, give up immediately, there was
+         * nothing interesting.  For example, looking at the files
+         * readme.txt and readme.foo, we will throw away .foo if
+         * it's an insignificant file (e.g. did not identify a 
+         * language, charset, encoding, content type or handler,)
+         */
+        exception_list = 
+            (array_header *) ap_table_get(sub_req->notes,
+                                          "ap-mime-exceptions-list");
+        if (!exception_list) {
+            ap_destroy_sub_req(sub_req);
+            continue;
+        }
+
+        /*
+         * Simple enough for now, every unreconized bit better match
+         * our base name.  When we break up our base name and allow
+         * index.en to match index.html.en, this gets tricker.
+         * XXX: index.html.foo won't be caught by testing index.html
+         * since the exceptions result is index.foo - this should be
+         * fixed as part of a new match-parts logic here.
+         */
+        {
+            char *base = ap_array_pstrcat(sub_req->pool, exception_list, '.');
+            int base_len = strlen(base);
+            if (base_len > prefix_len 
+#ifdef CASE_BLIND_FILESYSTEM
+                || strncasecmp(base, filp, base_len)
+#else
+                || strncmp(base, filp, base_len)
+#endif
+                || (prefix_len > base_len && filp[base_len] != '.')) {
+                /* 
+                 * Something you don't know is, something you don't know...
+                 */
+                ap_destroy_sub_req(sub_req);
+                continue;
+            }
+        }
+
+        /* 
+         * ###: be warned, the _default_ content type is already
+         * picked up here!  If we failed the subrequest, or don't 
+         * know what we are serving, then continue.
+         */
+        if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
             ap_destroy_sub_req(sub_req);
             continue;
         }
@@ -963,7 +1011,6 @@ static int read_types_multi(negotiation_state *neg)
         /* If it's a map file, we use that instead of the map
          * we're building...
          */
-
         if (((sub_req->content_type) &&
              !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
             ((sub_req->handler) &&