Please add this latest patch to the Apache 1.3 tree.  It fixes two
failures in the current 1.3.19 candidate:

1) the URI "#frag" would be emitted as "", because of a NULL path
2) sometimes an "@" was present in the site part even in the absence of
user info

For reference, the 1.3.17 version of this function could (aside from
dumoing core) emit bogosities such as:

http://hostpath
user@path?name=value#frag
http://@host/path?name=value#frag

Okay I guess that last one is valid but it still looks silly.  I have also
attached my updated battery of test cases.

Warm regards,
Jeffrey Baker
Index: util_uri.c
===================================================================
RCS file: /home/cvspublic/apache-1.3/src/main/util_uri.c,v
retrieving revision 1.31
diff -u -r1.31 util_uri.c
--- util_uri.c  2001/02/26 15:49:44     1.31
+++ util_uri.c  2001/02/28 23:36:54
@@ -207,7 +207,8 @@
             parts[j++] = "//";
             
             /* userinfo requires hostport */
-            if (uptr->hostname && (uptr->user || uptr->password)) {
+            if (uptr->hostname && (uptr->user || uptr->password)
+                && !(flags & UNP_OMITUSER && flags & UNP_OMITPASSWORD)) {
                 if (uptr->user && !(flags & UNP_OMITUSER))
                     parts[j++] = uptr->user;
                 
@@ -218,10 +219,10 @@
                         parts[j++] = uptr->password;
                     else
                         parts[j++] = "XXXXXXXX";
-                }    
+                }
 
                 parts[j++] = "@";
-            }                
+            }
             
             /* If we get here, there must be a hostname. */
             parts[j++] = uptr->hostname;
@@ -242,23 +243,26 @@
         
     if (!(flags & UNP_OMITPATHINFO)) {
         
-        
         /* We must ensure we don't put out a hier_part and a rel_path */
         if (j && uptr->path && *uptr->path != '/')
             parts[j++] = "/";
         
-        parts[j++] = uptr->path;
+        
+        /* Neither path nor query can exist without a path, but fragment can */
+        if (uptr->path != NULL) {
+                parts[j++] = uptr->path;
 
-        if (!(flags & UNP_OMITQUERY)) {
-            if (uptr->query) {
-                parts[j++] = "?";
-                parts[j++] = uptr->query;
+            if (!(flags & UNP_OMITQUERY)) {
+                if (uptr->query) {
+                    parts[j++] = "?";
+                    parts[j++] = uptr->query;
+                }
             }
-            
-            if (uptr->fragment) {
-                parts[j++] = "#";
-                parts[j++] = uptr->fragment;
-            }
+        }
+        
+        if (uptr->fragment) {
+            parts[j++] = "#";
+            parts[j++] = uptr->fragment;
         }
     }
 
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * Portions of this software are based upon public domain software
 * originally written at the National Center for Supercomputing Applications,
 * University of Illinois, Urbana-Champaign.
 */

/*
 * This module is intended to test the util_uri routines by parsing a
 * bunch of urls and comparing the results with what we expect to
 * see.
 *
 * Usage:
 *
 * <Location /test-util-uri>
 * SetHandler test-util-uri
 * </Location>
 *
 * Then make a request to /test-util-uri.  An html table of errors will
 * be output... and a total count of errors.
 */

#include "httpd.h"
#include "http_protocol.h"
#include "http_config.h"
#include "http_main.h"
#include "util_uri.h"
#include <stdlib.h>

static uri_components uri;

typedef struct {
    char *scheme;
    char *user;
    char *password;
    char *hostname;
    char *port;
    char *path;
    char *query;
    char *fragment;
} uri_init;

typedef struct {
    uri_init  init;
    unsigned  flags;
    char     *expect;
} job;

static const job jobs[] = {
    /* First the easy smoketest, then some more valid URIs */

    { { NULL, NULL, NULL, NULL, NULL, NULL, NULL, "frag" }, 0, "#frag" },
    { { NULL, NULL, NULL, NULL, NULL, "path", NULL, NULL }, 0, "path" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", NULL, NULL }, 0, "/path" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", "name=value", NULL }, 0, 
"/path?name=value" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", NULL, "frag" }, 0, "/path#frag" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", "name=value", "frag" }, 0, 
"/path?name=value#frag" },
    { { NULL, NULL, NULL, NULL, NULL, "path", "name=value", "frag" }, 0, 
"path?name=value#frag" },

    { { "http", NULL, NULL, "host", NULL, NULL, NULL, NULL }, 0, "http://host" },
    { { "https", NULL, NULL, "host", NULL, NULL, NULL, NULL }, 0, "https://host" },
    { { "http", NULL, NULL, "host", NULL, "/path", NULL, NULL }, 0, "http://host/path" 
},
    { { "http", NULL, NULL, "host", NULL, "path", NULL, NULL }, 0, "http://host/path" 
},
    { { "http", NULL, NULL, "host", "80", NULL, NULL, NULL }, 0, "http://host" },
    { { "http", NULL, NULL, "host", "81", NULL, NULL, NULL }, 0, "http://host:81" },
    { { "https", NULL, NULL, "host", "443", NULL, NULL, NULL }, 0, "https://host" },
    { { "https", NULL, NULL, "host", "444", NULL, NULL, NULL }, 0, "https://host:444" 
},
    { { "http", NULL, NULL, "host", "81", "/path", NULL, NULL }, 0, 
"http://host:81/path" },
    { { "http", NULL, NULL, "host", NULL, "/path", "name=value", NULL }, 0, 
"http://host/path?name=value" },
    { { "http", NULL, NULL, "host", NULL, "/path", "name=value", "fragment" }, 0, 
"http://host/path?name=value#fragment" },

    { { "http", "user", NULL, "host", NULL, "/path", "name=value", "fragment" }, 0, 
"http://user@host/path?name=value#fragment" },
    { { "http", NULL, "pass", "host", NULL, "/path", "name=value", "fragment" }, 0, 
"http://:XXXXXXXX@host/path?name=value#fragment" },
    { { "http", "user", "pass", "host", NULL, "/path", "name=value", "fragment" }, 0, 
"http://user:XXXXXXXX@host/path?name=value#fragment" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "fragment" }, 0, 
"http://user:XXXXXXXX@host/path?name=value#fragment" },
    { { "http", "user", "pass", "host", "81", "/path", "name=value", "fragment" }, 0, 
"http://user:XXXXXXXX@host:81/path?name=value#fragment" },
    { { "https", "user", "pass", "host", "443", "/path", "name=value", "fragment" }, 
0, "https://user:XXXXXXXX@host/path?name=value#fragment" },
    { { "https", "user", "pass", "host", "444", "/path", "name=value", "fragment" }, 
0, "https://user:XXXXXXXX@host:444/path?name=value#fragment" },

    /* These URIs are all bogus in some way */

    { { "http", NULL, NULL, NULL, NULL, "/path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },
    { { "http", NULL, NULL, NULL, NULL, "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },
    { { "http", NULL, NULL, NULL, "81", "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },

    /* XXX: these four currently fail, but they don't crash, and nobody writes URIs 
this way anyway */

    { { "http", "user", NULL, NULL, NULL, "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },
    { { "http", NULL, "pass", NULL, NULL, "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },
    { { "http", "user", "pass", NULL, NULL, "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },
    { { "http", "user", "pass", NULL, NULL, "path", "name=value", "frag" }, 0, 
"http:/path?name=value#frag" },

    { { NULL, NULL, NULL, "host", NULL, "/path", NULL, NULL }, 0, "http://host/path" 
},
    { { NULL, NULL, NULL, NULL, "80", "/path", NULL, NULL }, 0, "/path" },
    { { NULL, NULL, NULL, NULL, "81", "/path", NULL, NULL }, 0, "/path" },
    { { NULL, NULL, NULL, "host", "80", "/path", NULL, NULL }, 0, "http://host/path" 
},
    { { NULL, NULL, NULL, "host", "81", "/path", NULL, NULL }, 0, 
"http://host:81/path" },
        
    { { NULL, NULL, NULL, NULL, NULL, NULL, "name=value", NULL }, 0, "" },
    { { NULL, NULL, NULL, NULL, NULL, NULL, "name=value", "frag" }, 0, "#frag" },

    /* Now exercise the various options, starting with irrelevant ones */
    
    { { NULL, NULL, NULL, NULL, NULL, "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { "http", NULL, NULL, "host", NULL, "/path", "name=value", "frag" }, 
UNP_OMITUSER, "http://host/path?name=value#frag" },
    { { "http", NULL, NULL, "host", NULL, "/path", "name=value", "frag" }, 
UNP_OMITPASSWORD, "http://host/path?name=value#frag" },
    { { "http", NULL, NULL, "host", NULL, NULL, NULL, NULL }, UNP_OMITPATHINFO, 
"http://host" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", NULL, "frag" }, UNP_OMITQUERY, 
"/path#frag" },

    { { "http", "user", "pass", "host", "port", "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { "http", NULL, "pass", "host", "port", "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { "http", NULL, NULL, "host", "port", "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { "http", NULL, NULL, NULL, "port", "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { "http", NULL, NULL, NULL, NULL, "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    { { NULL, NULL, NULL, NULL, NULL, "/path", "name=value", "frag" }, 
UNP_OMITSITEPART, "/path?name=value#frag" },
    
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITUSERINFO, "http://host/path?name=value#frag" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITUSER, "http://:XXXXXXXX@host/path?name=value#frag" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITPASSWORD, "http://user@host/path?name=value#frag" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_REVEALPASSWORD, "http://user:pass@host/path?name=value#frag" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITUSER|UNP_REVEALPASSWORD, "http://:pass@host/path?name=value#frag" },
    
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITPATHINFO, "http://user:XXXXXXXX@host" },
    { { "http", "user", "pass", "host", "80", "path", NULL, NULL }, UNP_OMITPATHINFO, 
"http://user:XXXXXXXX@host" },

    { { "http", "user", "pass", "host", "80", "/path", "name=value", NULL }, 
UNP_OMITQUERY, "http://user:XXXXXXXX@host/path" },
    { { "http", "user", "pass", "host", "80", "/path", "name=value", "frag" }, 
UNP_OMITQUERY, "http://user:XXXXXXXX@host/path#frag" },
};

static void fill_uri(uri_components *uri, const uri_init *init)
{
    uri->scheme   = init->scheme;
    uri->user     = init->user;
    uri->password = init->password;
    uri->hostname = init->hostname;
    uri->port_str = init->port;
    uri->port     = init->port == NULL ? 0 : atoi(init->port);
    uri->path     = init->path;
    uri->query    = init->query;
    uri->fragment = init->fragment;
}

static int test_util_uri(request_rec *r)
{
    int i;
    int failures = 0;
    char *unparsed;    

    r->allowed |= (1 << M_GET);
    if (r->method_number != M_GET)
        return DECLINED;

    r->content_type = "text/html";
    ap_send_http_header(r);

    if(r->header_only)
        return 0;

    ap_hard_timeout("test_util_uri", r);

    ap_rputs(DOCTYPE_HTML_4_0T "<title>unparse test</title>", r);

    for (i = 0; i < sizeof(jobs) / sizeof(jobs[0]); i++) {
        fill_uri(&uri, &jobs[i].init);
        unparsed = ap_unparse_uri_components(r->pool, &uri, jobs[i].flags);
        
        if (strcasecmp(unparsed, jobs[i].expect)) {
            failures++;
            ap_rprintf(r, "<p style=\"color: red\">UNEXPECTED RESULT:");
        }
            
        ap_rprintf(r, "<p style=\"font-family: monospace\">ACTUAL: %s<br>EXPECT: 
%s\n", unparsed, jobs[i].expect);
    }
    
    ap_rprintf(r, "<p>%i failures", failures);
        
    return OK;
}


static const handler_rec test_util_uri_handlers[] =
{
    {"test-util-uri", test_util_uri},
    {NULL}
};

module test_util_uri_module = {
    STANDARD_MODULE_STUFF,
    NULL,                       /* initializer */
    NULL,                       /* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    NULL,                       /* server config */
    NULL,                       /* merge server config */
    NULL,                       /* command table */
    test_util_uri_handlers,     /* handlers */
    NULL,                       /* filename translation */
    NULL,                       /* check_user_id */
    NULL,                       /* check auth */
    NULL,                       /* check access */
    NULL,                       /* type_checker */
    NULL,                       /* fixups */
    NULL,                       /* logger */
    NULL                        /* header parser */
};

Reply via email to