1/*
2   URI handling tests
3   Copyright (C) 2001-2006, Joe Orton <joe@manyfish.co.uk>
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19*/
20
21#include "config.h"
22
23#ifdef HAVE_STDLIB_H
24#include <stdlib.h>
25#endif
26#ifdef HAVE_STRING_H
27#include <string.h>
28#endif
29
30#include "ne_uri.h"
31#include "ne_alloc.h"
32
33#include "tests.h"
34
35static int simple(void)
36{
37    ne_uri p = {0};
38    ON(ne_uri_parse("http://www.webdav.org/foo", &p));
39    ON(strcmp(p.host, "www.webdav.org"));
40    ON(strcmp(p.path, "/foo"));
41    ON(strcmp(p.scheme, "http"));
42    ON(p.port);
43    ON(p.userinfo != NULL);
44    ne_uri_free(&p);
45    return 0;
46}
47
48static int simple_ssl(void)
49{
50    ne_uri p = {0};
51    ON(ne_uri_parse("https://webdav.org/", &p));
52    ON(strcmp(p.scheme, "https"));
53    ON(p.port);
54    ne_uri_free(&p);
55    return OK;
56}
57
58static int no_path(void)
59{
60    ne_uri p = {0};
61    ON(ne_uri_parse("https://webdav.org", &p));
62    ON(strcmp(p.path, "/"));
63    ne_uri_free(&p);
64    return OK;
65}
66
67static int escapes(void)
68{
69    const struct {
70        const char *plain, *escaped;
71    } paths[] = {
72        { "/foo%", "/foo%25" },
73        { "/foo bar", "/foo%20bar" },
74        { "/foo_bar", "/foo_bar" },
75        { "/foobar", "/foobar" },
76        { "/a\xb9\xb2\xb3\xbc\xbd/", "/a%b9%b2%b3%bc%bd/" },
77        { NULL, NULL}
78    };
79    size_t n;
80
81    for (n = 0; paths[n].plain; n++) {
82        char *esc = ne_path_escape(paths[n].plain), *un;
83
84        ONCMP(paths[n].escaped, esc, paths[n].plain, "escape");
85
86        un = ne_path_unescape(esc);
87
88        ONCMP(paths[n].plain, un, paths[n].plain, "unescape");
89        ne_free(un);
90        ne_free(esc);
91    }
92
93    ONN("unescape accepted invalid URI",
94        ne_path_unescape("/foo%zzbar") != NULL);
95    ONN("unescape accepted invalid URI",
96        ne_path_unescape("/foo%1zbar") != NULL);
97
98    return OK;
99}
100
101static int parents(void)
102{
103    static const struct {
104	const char *path, *parent;
105    } ps[] = {
106	{ "/a/b/c", "/a/b/" },
107	{ "/a/b/c/", "/a/b/" },
108	{ "/alpha/beta", "/alpha/" },
109	{ "/foo", "/" },
110	{ "norman", NULL },
111	{ "/", NULL },
112	{ "", NULL },
113	{ NULL, NULL }
114    };
115    int n;
116
117    for (n = 0; ps[n].path != NULL; n++) {
118	char *p = ne_path_parent(ps[n].path);
119	if (ps[n].parent == NULL) {
120	    ONV(p != NULL, ("parent of `%s' was `%s' not NULL",
121			    ps[n].path, p));
122	} else {
123	    ONV(p == NULL, ("parent of `%s' was NULL", ps[n].path));
124	    ONV(strcmp(p, ps[n].parent),
125		("parent of `%s' was `%s' not `%s'",
126		 ps[n].path, p, ps[n].parent));
127	    ne_free(p);
128	}
129    }
130
131    return OK;
132}
133
134static int compares(void)
135{
136    const char *alpha = "/alpha";
137
138    ON(ne_path_compare("/a", "/a/") != 0);
139    ON(ne_path_compare("/a/", "/a") != 0);
140    ON(ne_path_compare("/ab", "/a/") == 0);
141    ON(ne_path_compare("/a/", "/ab") == 0);
142    ON(ne_path_compare("/a/", "/a/") != 0);
143    ON(ne_path_compare("/alpha/", "/beta/") == 0);
144    ON(ne_path_compare("/alpha", "/b") == 0);
145    ON(ne_path_compare("/alpha/", "/alphash") == 0);
146    ON(ne_path_compare("/fish/", "/food") == 0);
147    ON(ne_path_compare(alpha, alpha) != 0);
148    ON(ne_path_compare("/a/b/c/d", "/a/b/c/") == 0);
149    return OK;
150}
151
152static int cmp(void)
153{
154    static const struct {
155        const char *left, *right;
156    } eq[] = {
157        { "http://example.com/alpha", "http://example.com/alpha" },
158        { "//example.com/alpha", "//example.com/alpha" },
159        { "http://example.com/alpha#foo", "http://example.com/alpha#foo" },
160        { "http://example.com/alpha?bar", "http://example.com/alpha?bar" },
161        { "http://jim@example.com/alpha", "http://jim@example.com/alpha" },
162        { "HTTP://example.com/alpha", "http://example.com/alpha" },
163        { "http://example.com/", "http://example.com" },
164        { "http://Example.Com/", "http://example.com" },
165        { NULL, NULL}
166    }, diff[] = {
167        { "http://example.com/alpha", "http://example.com/beta" },
168        { "http://example.com/alpha", "https://example.com/alpha" },
169        { "http://example.com/alpha", "http://www.example.com/alpha" },
170        { "http://example.com:443/alpha", "http://example.com:8080/alpha" },
171        { "http://example.com/alpha", "http://jim@example.com/alpha" },
172        { "http://bob@example.com/alpha", "http://jim@example.com/alpha" },
173        { "http://example.com/alpha", "http://example.com/alpha?fish" },
174        { "http://example.com/alpha?fish", "http://example.com/alpha?food" },
175        { "http://example.com/alpha", "http://example.com/alpha#foo" },
176        { "http://example.com/alpha#bar", "http://example.com/alpha#foo" },
177        { "http://example.com/alpha", "//example.com/alpha" },
178        { "http://example.com/alpha", "///alpha" },
179        { NULL, NULL}
180    };
181    size_t n;
182
183    for (n = 0; eq[n].left; n++) {
184        ne_uri alpha, beta;
185        int r1, r2;
186
187        ONV(ne_uri_parse(eq[n].left, &alpha),
188            ("could not parse left URI '%s'", eq[n].left));
189
190        ONV(ne_uri_parse(eq[n].right, &beta),
191            ("could not parse right URI '%s'", eq[n].right));
192
193        r1 = ne_uri_cmp(&alpha, &beta);
194        r2 = ne_uri_cmp(&beta, &alpha);
195
196        ONV(r1 != 0,
197            ("cmp('%s', '%s') = %d not zero",
198             eq[n].left, eq[n].right, r1));
199
200        ONV(r2 != 0,
201            ("cmp('%s', '%s') = %d not zero",
202             eq[n].right, eq[n].left, r2));
203
204        ne_uri_free(&alpha);
205        ne_uri_free(&beta);
206    }
207
208    for (n = 0; diff[n].left; n++) {
209        ne_uri alpha, beta;
210        int r1, r2;
211
212        ONV(ne_uri_parse(diff[n].left, &alpha),
213            ("could not parse left URI '%s'", diff[n].left));
214
215        ONV(ne_uri_parse(diff[n].right, &beta),
216            ("could not parse right URI '%s'", diff[n].right));
217
218        r1 = ne_uri_cmp(&alpha, &beta);
219        r2 = ne_uri_cmp(&beta, &alpha);
220
221        ONV(r1 == 0,
222            ("'%s' and '%s' did not compare as different",
223             diff[n].left, diff[n].right));
224
225        ONV(r1 + r2 != 0,
226            ("'%s' and '%s' did not compare reflexively (%d vs %d)",
227             diff[n].left, diff[n].right, r1, r2));
228
229        ne_uri_free(&alpha);
230        ne_uri_free(&beta);
231    }
232
233    return OK;
234}
235
236static int children(void)
237{
238    ON(ne_path_childof("/a", "/a/b") == 0);
239    ON(ne_path_childof("/a/", "/a/b") == 0);
240    ON(ne_path_childof("/aa/b/c", "/a/b/c/d/e") != 0);
241    ON(ne_path_childof("////", "/a") != 0);
242    return OK;
243}
244
245static int slash(void)
246{
247    ON(ne_path_has_trailing_slash("/a/") == 0);
248    ON(ne_path_has_trailing_slash("/a") != 0);
249    {
250	/* check the uri == "" case. */
251	char *foo = "/";
252	ON(ne_path_has_trailing_slash(&foo[1]));
253    }
254    return OK;
255}
256
257static int default_port(void)
258{
259    ONN("default http: port incorrect", ne_uri_defaultport("http") != 80);
260    ONN("default https: port incorrect", ne_uri_defaultport("https") != 443);
261    ONN("unspecified scheme: port incorrect", ne_uri_defaultport("ldap") != 0);
262    return OK;
263}
264
265static int parse(void)
266{
267    static const struct test_uri {
268	const char *uri, *scheme, *host;
269	unsigned int port;
270	const char *path, *userinfo, *query, *fragment;
271    } uritests[] = {
272        { "http://webdav.org/norman", "http", "webdav.org", 0, "/norman", NULL, NULL, NULL },
273        { "http://webdav.org:/norman", "http", "webdav.org", 0, "/norman", NULL, NULL, NULL },
274        { "https://webdav.org/foo", "https", "webdav.org", 0, "/foo", NULL, NULL, NULL },
275        { "http://webdav.org:8080/bar", "http", "webdav.org", 8080, "/bar", NULL, NULL, NULL },
276        { "http://a/b", "http", "a", 0, "/b", NULL, NULL, NULL },
277        { "http://webdav.org/bar:fish", "http", "webdav.org", 0, "/bar:fish", NULL, NULL, NULL },
278        { "http://webdav.org", "http", "webdav.org", 0, "/", NULL, NULL, NULL },
279        { "http://webdav.org/fish@food", "http", "webdav.org", 0, "/fish@food", NULL, NULL, NULL },
280
281        /* query/fragments */
282        { "http://foo/bar?alpha", "http", "foo", 0, "/bar", NULL, "alpha", NULL },
283        { "http://foo/bar?alpha#beta", "http", "foo", 0, "/bar", NULL, "alpha", "beta" },
284        { "http://foo/bar#alpha?beta", "http", "foo", 0, "/bar", NULL, NULL, "alpha?beta" },
285        { "http://foo/bar#beta", "http", "foo", 0, "/bar", NULL, NULL, "beta" },
286        { "http://foo/bar?#beta", "http", "foo", 0, "/bar", NULL, "", "beta" },
287        { "http://foo/bar?alpha?beta", "http", "foo", 0, "/bar", NULL, "alpha?beta", NULL },
288
289        /* Examples from RFC3986�1.1.2: */
290        { "ftp://ftp.is.co.za/rfc/rfc1808.txt", "ftp", "ftp.is.co.za", 0, "/rfc/rfc1808.txt", NULL, NULL, NULL },
291        { "http://www.ietf.org/rfc/rfc2396.txt", "http", "www.ietf.org", 0, "/rfc/rfc2396.txt", NULL, NULL, NULL },
292        { "ldap://[2001:db8::7]/c=GB?objectClass?one", "ldap", "[2001:db8::7]", 0, "/c=GB", NULL, "objectClass?one", NULL },
293        { "mailto:John.Doe@example.com", "mailto", NULL, 0, "John.Doe@example.com", NULL, NULL, NULL },
294        { "news:comp.infosystems.www.servers.unix", "news", NULL, 0, "comp.infosystems.www.servers.unix", NULL, NULL, NULL },
295        { "tel:+1-816-555-1212", "tel", NULL, 0, "+1-816-555-1212", NULL, NULL, NULL },
296        { "telnet://192.0.2.16:80/", "telnet", "192.0.2.16", 80, "/", NULL, NULL, NULL },
297        { "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", "urn", NULL, 0,
298          "oasis:names:specification:docbook:dtd:xml:4.1.2", NULL},
299
300        /* userinfo */
301        { "ftp://jim:bob@jim.com", "ftp", "jim.com", 0, "/", "jim:bob", NULL, NULL },
302        { "ldap://fred:bloggs@fish.com/foobar", "ldap", "fish.com", 0,
303          "/foobar", "fred:bloggs", NULL, NULL },
304
305        /* IPv6 literals: */
306        { "http://[::1]/foo", "http", "[::1]", 0, "/foo", NULL, NULL, NULL },
307        { "http://[a:a:a:a::0]/foo", "http", "[a:a:a:a::0]", 0, "/foo", NULL, NULL, NULL },
308        { "http://[::1]:8080/bar", "http", "[::1]", 8080, "/bar", NULL, NULL, NULL },
309        { "ftp://[feed::cafe]:555", "ftp", "[feed::cafe]", 555, "/", NULL, NULL, NULL },
310
311        { "DAV:", "DAV", NULL, 0, "", NULL, NULL, NULL },
312
313        /* Some odd cases: heir-part and relative-ref will both match
314         * with a zero-length expansion of "authority" (since *
315         * reg-name can be zero-length); so a triple-slash URI-ref
316         * will be matched as "//" followed by a zero-length authority
317         * followed by a path of "/". */
318        { "foo:///", "foo", "", 0, "/", NULL, NULL, NULL },
319        { "///", NULL, "", 0, "/", NULL, NULL, NULL },
320        /* port grammar is "*DIGIT" so may be empty: */
321        { "ftp://[feed::cafe]:", "ftp", "[feed::cafe]", 0, "/", NULL, NULL, NULL },
322        { "ftp://[feed::cafe]:/", "ftp", "[feed::cafe]", 0, "/", NULL, NULL, NULL },
323        { "http://foo:/", "http", "foo", 0, "/", NULL, NULL, NULL },
324
325        /* URI-references: */
326        { "//foo.com/bar", NULL, "foo.com", 0, "/bar", NULL, NULL, NULL },
327        { "//foo.com", NULL, "foo.com", 0, "/", NULL, NULL, NULL },
328        { "//[::1]/foo", NULL, "[::1]", 0, "/foo", NULL, NULL, NULL },
329        { "/bar", NULL, NULL, 0, "/bar", NULL, NULL, NULL }, /* path-absolute */
330        { "foo/bar", NULL, NULL, 0, "foo/bar", NULL, NULL, NULL }, /* path-noscheme */
331        { "", NULL, NULL, 0, "", NULL, NULL, NULL }, /* path-empty */
332
333        /* CVE-2007-0157: buffer under-read in 0.26.[012]. */
334        { "http://webdav.org\xFF", "http", "webdav.org\xFF", 0, "/", NULL, NULL, NULL },
335
336	{ NULL }
337    };
338    int n;
339
340    for (n = 0; uritests[n].uri != NULL; n++) {
341	ne_uri res;
342	const struct test_uri *e = &uritests[n];
343	ONV(ne_uri_parse(e->uri, &res) != 0,
344	    ("'%s': parse failed", e->uri));
345	ONV(res.port != e->port,
346	    ("'%s': parsed port was %d not %d", e->uri, res.port, e->port));
347	ONCMP(e->scheme, res.scheme, e->uri, "scheme");
348	ONCMP(e->host, res.host, e->uri, "host");
349	ONV(strcmp(res.path, e->path),
350	    ("'%s': parsed path was '%s' not '%s'", e->uri, res.path, e->path));
351        ONCMP(e->userinfo, res.userinfo, e->uri, "userinfo");
352        ONCMP(e->query, res.query, e->uri, "query");
353        ONCMP(e->fragment, res.fragment, e->uri, "fragment");
354	ne_uri_free(&res);
355    }
356
357    return OK;
358}
359
360static int failparse(void)
361{
362    static const char *uris[] = {
363	"http://[::1/",
364	"http://[::1]f:80/",
365	"http://[::1]]:80/",
366        "http://foo/bar asda",
367        "http://fish/[foo]/bar",
368	NULL
369    };
370    int n;
371
372    for (n = 0; uris[n] != NULL; n++) {
373	ne_uri p;
374	ONV(ne_uri_parse(uris[n], &p) == 0,
375	    ("`%s' did not fail to parse", uris[n]));
376	ne_uri_free(&p);
377    }
378
379    return 0;
380}
381
382static int unparse(void)
383{
384    const char *uris[] = {
385	"http://foo.com/bar",
386	"https://bar.com/foo/wishbone",
387	"http://www.random.com:8000/",
388	"http://[::1]:8080/",
389	"ftp://ftp.foo.bar/abc/def",
390	"ftp://joe@bar.com/abc/def",
391	"http://a/b?c#d",
392	"http://a/b?c",
393	"http://a/b#d",
394        "mailto:foo@bar.com",
395        "//foo.com/bar",
396        "//foo.com:8080/bar",
397	NULL
398    };
399    int n;
400
401    for (n = 0; uris[n] != NULL; n++) {
402	ne_uri parsed;
403	char *unp;
404
405	ONV(ne_uri_parse(uris[n], &parsed),
406	    ("failed to parse %s", uris[n]));
407
408        if (parsed.port == 0 && parsed.scheme)
409            parsed.port = ne_uri_defaultport(parsed.scheme);
410
411	unp = ne_uri_unparse(&parsed);
412
413	ONV(strcmp(unp, uris[n]),
414	    ("unparse got %s from %s", unp, uris[n]));
415
416        ne_uri_free(&parsed);
417	ne_free(unp);
418    }
419
420    return OK;
421}
422
423#define BASE "http://a/b/c/d;p?q"
424
425static int resolve(void)
426{
427    static const struct {
428        const char *base, *relative, *expected;
429    } ts[] = {
430        /* Examples from RFC3986�5.4: */
431        { BASE, "g:h", "g:h" },
432        { BASE, "g", "http://a/b/c/g" },
433        { BASE, "./g", "http://a/b/c/g" },
434        { BASE, "g/", "http://a/b/c/g/" },
435        { BASE, "/g", "http://a/g" },
436        { BASE, "//g", "http://g/" }, /* NOTE: modified to mandate non-empty path */
437        { BASE, "?y", "http://a/b/c/d;p?y" },
438        { BASE, "g?y", "http://a/b/c/g?y" },
439        { BASE, "#s", "http://a/b/c/d;p?q#s" },
440        { BASE, "g#s", "http://a/b/c/g#s" },
441        { BASE, "g?y#s", "http://a/b/c/g?y#s" },
442        { BASE, ";x", "http://a/b/c/;x" },
443        { BASE, "g;x", "http://a/b/c/g;x" },
444        { BASE, "g;x?y#s", "http://a/b/c/g;x?y#s" },
445        { BASE, "", "http://a/b/c/d;p?q" },
446        { BASE, ".", "http://a/b/c/" },
447        { BASE, "./", "http://a/b/c/" },
448        { BASE, "..", "http://a/b/" },
449        { BASE, "../", "http://a/b/" },
450        { BASE, "../g", "http://a/b/g" },
451        { BASE, "../..", "http://a/" },
452        { BASE, "../../", "http://a/" },
453        { BASE, "../../g", "http://a/g" },
454        { BASE, "../../../g", "http://a/g" },
455        { BASE, "../../../../g", "http://a/g" },
456        { BASE, "/./g", "http://a/g" },
457        { BASE, "/../g", "http://a/g" },
458        { BASE, "g.", "http://a/b/c/g." },
459        { BASE, ".g", "http://a/b/c/.g" },
460        { BASE, "g..", "http://a/b/c/g.." },
461        { BASE, "..g", "http://a/b/c/..g" },
462        { BASE, "./../g", "http://a/b/g" },
463        { BASE, "./g/.", "http://a/b/c/g/" },
464        { BASE, "g/./h", "http://a/b/c/g/h" },
465        { BASE, "g/../h", "http://a/b/c/h" },
466        { BASE, "g;x=1/./y", "http://a/b/c/g;x=1/y" },
467        { BASE, "g;x=1/../y", "http://a/b/c/y" },
468        { BASE, "g?y/./x", "http://a/b/c/g?y/./x" },
469        { BASE, "g?y/../x", "http://a/b/c/g?y/../x" },
470        { BASE, "g#s/./x", "http://a/b/c/g#s/./x" },
471        { BASE, "g#s/../x", "http://a/b/c/g#s/../x" },
472        { BASE, "http:g", "http:g" },
473        /* Additional examples: */
474        { BASE, ".", "http://a/b/c/" },
475        { "http://foo.com/alpha/beta", "../gamma", "http://foo.com/gamma" },
476        { "http://foo.com/alpha//beta", "../gamma", "http://foo.com/alpha/gamma" },
477
478        { "http://foo.com", "../gamma", "http://foo.com/gamma" },
479        { "", "zzz:.", "zzz:" },
480        { "", "zzz:./foo", "zzz:foo" },
481        { "", "zzz:../foo", "zzz:foo" },
482        { "", "zzz:/./foo", "zzz:/foo" },
483        { "", "zzz:/.", "zzz:/" },
484        { "", "zzz:/../", "zzz:/" },
485        { "", "zzz:.", "zzz:" },
486        { "", "zzz:..", "zzz:" },
487        { "", "zzz://foo@bar/", "zzz://foo@bar/" },
488        { "", "zzz://foo/?bar", "zzz://foo/?bar" },
489        { "zzz://foo/?bar", "//baz/?jam", "zzz://baz/?jam" },
490        { "zzz://foo/baz?biz", "", "zzz://foo/baz?biz" },
491        { "zzz://foo/baz", "", "zzz://foo/baz" },
492        { "//foo/baz", "", "//foo/baz" },
493
494
495        { NULL, NULL, NULL }
496    };
497    size_t n;
498
499    for (n = 0; ts[n].base; n++) {
500        ne_uri base, relative, resolved;
501        char *actual;
502
503        ONV(ne_uri_parse(ts[n].base, &base),
504            ("could not parse base URI '%s'", ts[n].base));
505
506        ONV(ne_uri_parse(ts[n].relative, &relative),
507            ("could not parse input URI '%s'", ts[n].relative));
508
509        ONN("bad pointer was returned",
510            ne_uri_resolve(&base, &relative, &resolved) != &resolved);
511
512        ONN("NULL path after resolve", resolved.path == NULL);
513
514        actual = ne_uri_unparse(&resolved);
515
516        ONCMP(ts[n].expected, actual, ts[n].relative, "output mismatch");
517
518        ne_uri_free(&relative);
519        ne_uri_free(&resolved);
520        ne_uri_free(&base);
521        ne_free(actual);
522    }
523
524    return OK;
525}
526
527static int copy(void)
528{
529    static const char *ts[] = {
530        "http://jim@foo.com:8080/bar?baz#bee",
531        "",
532        NULL,
533    };
534    size_t n;
535
536    for (n = 0; ts[n]; n++) {
537        ne_uri parsed, parsed2;
538        char *actual;
539
540        ONV(ne_uri_parse(ts[n], &parsed), ("could not parse URI '%s'", ts[n]));
541        ONN("ne_uri_copy returned wrong pointer",
542            ne_uri_copy(&parsed2, &parsed) != &parsed2);
543
544        actual = ne_uri_unparse(&parsed2);
545
546        ONCMP(ts[n], actual, "copied URI", "unparsed URI");
547
548        ne_uri_free(&parsed2);
549        ne_uri_free(&parsed);
550        ne_free(actual);
551    }
552
553    return OK;
554}
555
556ne_test tests[] = {
557    T(simple),
558    T(simple_ssl),
559    T(no_path),
560    T(escapes),
561    T(parents),
562    T(compares),
563    T(cmp),
564    T(children),
565    T(slash),
566    T(default_port),
567    T(parse),
568    T(failparse),
569    T(unparse),
570    T(resolve),
571    T(copy),
572    T(NULL)
573};
574