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