1 /* GLIB - Library of useful routines for C programming 2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 * Boston, MA 02111-1307, USA. 18 */ 19 20 /* 21 * Modified by the GLib Team and others 1997-2000. See the AUTHORS 22 * file for a list of people on the GLib Team. See the ChangeLog 23 * files for a list of changes. These files are distributed with 24 * GLib at ftp://ftp.gtk.org/pub/gtk/. 25 */ 26 27 #undef G_DISABLE_ASSERT 28 #undef G_LOG_DOMAIN 29 30 #include "config.h" 31 32 #include <glib.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <stdlib.h> 36 37 typedef struct 38 { 39 char *filename; 40 char *hostname; 41 char *expected_result; 42 GConvertError expected_error; /* If failed */ 43 } ToUriTest; 44 45 ToUriTest 46 to_uri_tests[] = { 47 { "/etc", NULL, "file:///etc"}, 48 { "/etc", "", "file:///etc"}, 49 { "/etc", "otherhost", "file://otherhost/etc"}, 50 #ifdef G_OS_WIN32 51 { "/etc", "localhost", "file:///etc"}, 52 { "c:\\windows", NULL, "file:///c:/windows"}, 53 { "c:\\windows", "localhost", "file:///c:/windows"}, 54 { "c:\\windows", "otherhost", "file://otherhost/c:/windows"}, 55 { "\\\\server\\share\\dir", NULL, "file:////server/share/dir"}, 56 { "\\\\server\\share\\dir", "localhost", "file:////server/share/dir"}, 57 #else 58 { "/etc", "localhost", "file://localhost/etc"}, 59 { "c:\\windows", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, /* it's important to get this error on Unix */ 60 { "c:\\windows", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 61 { "c:\\windows", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 62 #endif 63 { "etc", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 64 #ifndef G_PLATFORM_WIN32 65 { "/etc/\xE5\xE4\xF6", NULL, "file:///etc/%E5%E4%F6" }, 66 { "/etc/\xC3\xB6\xC3\xA4\xC3\xA5", NULL, "file:///etc/%C3%B6%C3%A4%C3%A5"}, 67 #endif 68 { "/etc", "\xC3\xB6\xC3\xA4\xC3\xA5", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 69 { "/etc", "\xE5\xE4\xF6", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 70 { "/etc/file with #%", NULL, "file:///etc/file%20with%20%23%25"}, 71 { "", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 72 { "", "", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 73 { "", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 74 { "", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, 75 { "/0123456789", NULL, "file:///0123456789"}, 76 { "/ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL, "file:///ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 77 { "/abcdefghijklmnopqrstuvwxyz", NULL, "file:///abcdefghijklmnopqrstuvwxyz"}, 78 { "/-_.!~*'()", NULL, "file:///-_.!~*'()"}, 79 #ifdef G_OS_WIN32 80 /* As '\\' is a path separator on Win32, it gets turned into '/' in the URI */ 81 { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B/%5D%5E%60%7B%7C%7D%7F"}, 82 #else 83 /* On Unix, '\\' is a normal character in the file name */ 84 { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B%5C%5D%5E%60%7B%7C%7D%7F"}, 85 #endif 86 { "/;@+$,", NULL, "file:///%3B@+$,"}, 87 /* This and some of the following are of course as such illegal file names on Windows, 88 * and would not occur in real life. 89 */ 90 { "/:", NULL, "file:///:"}, 91 { "/?&=", NULL, "file:///%3F&="}, 92 { "/", "0123456789-", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 93 { "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/"}, 94 { "/", "abcdefghijklmnopqrstuvwxyz", "file://abcdefghijklmnopqrstuvwxyz/"}, 95 { "/", "_.!~*'()", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 96 { "/", "\"#%<>[\\]^`{|}\x7F", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 97 { "/", ";?&=+$,", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 98 { "/", "/", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 99 { "/", "@:", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 100 { "/", "\x80\xFF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 101 { "/", "\xC3\x80\xC3\xBF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE}, 102 }; 103 104 105 typedef struct 106 { 107 char *uri; 108 char *expected_filename; 109 char *expected_hostname; 110 GConvertError expected_error; /* If failed */ 111 } FromUriTest; 112 113 FromUriTest 114 from_uri_tests[] = { 115 { "file:///etc", "/etc"}, 116 { "file:/etc", "/etc"}, 117 #ifdef G_OS_WIN32 118 /* On Win32 we don't return "localhost" hostames, just in case 119 * it isn't recognized anyway. 120 */ 121 { "file://localhost/etc", "/etc", NULL}, 122 { "file://localhost/etc/%23%25%20file", "/etc/#% file", NULL}, 123 { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", NULL}, 124 { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", NULL}, 125 #else 126 { "file://localhost/etc", "/etc", "localhost"}, 127 { "file://localhost/etc/%23%25%20file", "/etc/#% file", "localhost"}, 128 { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", "localhost"}, 129 { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", "localhost"}, 130 #endif 131 { "file://otherhost/etc", "/etc", "otherhost"}, 132 { "file://otherhost/etc/%23%25%20file", "/etc/#% file", "otherhost"}, 133 { "file://%C3%B6%C3%A4%C3%A5/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 134 { "file:////etc/%C3%B6%C3%C3%C3%A5", "//etc/\xc3\xb6\xc3\xc3\xc3\xa5", NULL}, 135 { "file://\xE5\xE4\xF6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 136 { "file://%E5%E4%F6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 137 { "file:///some/file#bad", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 138 { "file://some", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 139 { "", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 140 { "file:test", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 141 { "http://www.yahoo.com/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 142 { "file:////etc", "//etc"}, 143 { "file://///etc", "///etc"}, 144 #ifdef G_OS_WIN32 145 /* URIs with backslashes come from some nonstandard application, but accept them anyhow */ 146 { "file:///c:\\foo", "c:\\foo"}, 147 { "file:///c:/foo\\bar", "c:\\foo\\bar"}, 148 /* Accept also the old Netscape drive-letter-and-vertical bar convention */ 149 { "file:///c|/foo", "c:\\foo"}, 150 { "file:////server/share/dir", "\\\\server\\share\\dir"}, 151 { "file://localhost//server/share/foo", "\\\\server\\share\\foo"}, 152 { "file://otherhost//server/share/foo", "\\\\server\\share\\foo", "otherhost"}, 153 #else 154 { "file:///c:\\foo", "/c:\\foo"}, 155 { "file:///c:/foo", "/c:/foo"}, 156 { "file:////c:/foo", "//c:/foo"}, 157 #endif 158 { "file://0123456789/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 159 { "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/", "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 160 { "file://abcdefghijklmnopqrstuvwxyz/", "/", "abcdefghijklmnopqrstuvwxyz"}, 161 { "file://-_.!~*'()/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 162 { "file://\"<>[\\]^`{|}\x7F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 163 { "file://;?&=+$,/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 164 { "file://%C3%80%C3%BF/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 165 { "file://@/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 166 { "file://:/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 167 { "file://#/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 168 { "file://%23/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 169 { "file://%2F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI}, 170 }; 171 172 173 static gboolean any_failed = FALSE; 174 175 static void 176 run_to_uri_tests (void) 177 { 178 int i; 179 gchar *res; 180 GError *error; 181 182 for (i = 0; i < G_N_ELEMENTS (to_uri_tests); i++) 183 { 184 error = NULL; 185 res = g_filename_to_uri (to_uri_tests[i].filename, 186 to_uri_tests[i].hostname, 187 &error); 188 189 if (to_uri_tests[i].expected_result == NULL) 190 { 191 if (res != NULL) 192 { 193 g_print ("\ng_filename_to_uri() test %d failed, expected to return NULL, actual result: %s\n", i, res); 194 any_failed = TRUE; 195 } 196 else 197 { 198 if (error == NULL) 199 { 200 g_print ("\ng_filename_to_uri() test %d failed, returned NULL, but didn't set error\n", i); 201 any_failed = TRUE; 202 } 203 else if (error->domain != G_CONVERT_ERROR) 204 { 205 g_print ("\ng_filename_to_uri() test %d failed, returned NULL, set non G_CONVERT_ERROR error\n", i); 206 any_failed = TRUE; 207 } 208 else if (error->code != to_uri_tests[i].expected_error) 209 { 210 g_print ("\ng_filename_to_uri() test %d failed as expected, but set wrong errorcode %d instead of expected %d \n", 211 i, error->code, to_uri_tests[i].expected_error); 212 any_failed = TRUE; 213 } 214 } 215 } 216 else if (res == NULL || strcmp (res, to_uri_tests[i].expected_result) != 0) 217 { 218 g_print ("\ng_filename_to_uri() test %d failed, expected result: %s, actual result: %s\n", 219 i, to_uri_tests[i].expected_result, (res) ? res : "NULL"); 220 if (error) 221 g_print ("Error message: %s\n", error->message); 222 any_failed = TRUE; 223 } 224 g_free (res); 225 } 226 } 227 228 static void 229 run_from_uri_tests (void) 230 { 231 int i; 232 gchar *res; 233 gchar *hostname; 234 GError *error; 235 236 for (i = 0; i < G_N_ELEMENTS (from_uri_tests); i++) 237 { 238 error = NULL; 239 res = g_filename_from_uri (from_uri_tests[i].uri, 240 &hostname, 241 &error); 242 243 if (from_uri_tests[i].expected_filename == NULL) 244 { 245 if (res != NULL) 246 { 247 g_print ("\ng_filename_from_uri() test %d failed, expected to return NULL, actual result: %s\n", i, res); 248 any_failed = TRUE; 249 } 250 else 251 { 252 if (error == NULL) 253 { 254 g_print ("\ng_filename_from_uri() test %d failed, returned NULL, but didn't set error\n", i); 255 any_failed = TRUE; 256 } 257 else if (error->domain != G_CONVERT_ERROR) 258 { 259 g_print ("\ng_filename_from_uri() test %d failed, returned NULL, set non G_CONVERT_ERROR error\n", i); 260 any_failed = TRUE; 261 } 262 else if (error->code != from_uri_tests[i].expected_error) 263 { 264 g_print ("\ng_filename_from_uri() test %d failed as expected, but set wrong errorcode %d instead of expected %d \n", 265 i, error->code, from_uri_tests[i].expected_error); 266 any_failed = TRUE; 267 } 268 } 269 } 270 else 271 { 272 #ifdef G_OS_WIN32 273 gchar *slash, *p; 274 275 p = from_uri_tests[i].expected_filename = g_strdup (from_uri_tests[i].expected_filename); 276 while ((slash = strchr (p, '/')) != NULL) 277 { 278 *slash = '\\'; 279 p = slash + 1; 280 } 281 #endif 282 if (res == NULL || strcmp (res, from_uri_tests[i].expected_filename) != 0) 283 { 284 g_print ("\ng_filename_from_uri() test %d failed, expected result: %s, actual result: %s\n", 285 i, from_uri_tests[i].expected_filename, (res) ? res : "NULL"); 286 any_failed = TRUE; 287 } 288 289 if (from_uri_tests[i].expected_hostname == NULL) 290 { 291 if (hostname != NULL) 292 { 293 g_print ("\ng_filename_from_uri() test %d failed, expected no hostname, got: %s\n", 294 i, hostname); 295 any_failed = TRUE; 296 } 297 } 298 else if (hostname == NULL || 299 strcmp (hostname, from_uri_tests[i].expected_hostname) != 0) 300 { 301 g_print ("\ng_filename_from_uri() test %d failed, expected hostname: %s, actual result: %s\n", 302 i, from_uri_tests[i].expected_hostname, (hostname) ? hostname : "NULL"); 303 any_failed = TRUE; 304 } 305 } 306 } 307 } 308 309 static gint 310 safe_strcmp (const gchar *a, const gchar *b) 311 { 312 return strcmp (a ? a : "", b ? b : ""); 313 } 314 315 static gint 316 safe_strcmp_filename (const gchar *a, const gchar *b) 317 { 318 #ifndef G_OS_WIN32 319 return safe_strcmp (a, b); 320 #else 321 if (!a || !b) 322 return safe_strcmp (a, b); 323 else 324 { 325 while (*a && *b) 326 { 327 if ((G_IS_DIR_SEPARATOR (*a) && G_IS_DIR_SEPARATOR (*b)) || 328 *a == *b) 329 a++, b++; 330 else 331 return (*a - *b); 332 } 333 return (*a - *b); 334 } 335 #endif 336 } 337 338 static gint 339 safe_strcmp_hostname (const gchar *a, const gchar *b) 340 { 341 #ifndef G_OS_WIN32 342 return safe_strcmp (a, b); 343 #else 344 if (safe_strcmp (a, "localhost") == 0 && b == NULL) 345 return 0; 346 else 347 return safe_strcmp (a, b); 348 #endif 349 } 350 351 static void 352 run_roundtrip_tests (void) 353 { 354 int i; 355 gchar *uri, *hostname, *res; 356 GError *error; 357 358 for (i = 0; i < G_N_ELEMENTS (to_uri_tests); i++) 359 { 360 if (to_uri_tests[i].expected_error != 0) 361 continue; 362 363 error = NULL; 364 uri = g_filename_to_uri (to_uri_tests[i].filename, 365 to_uri_tests[i].hostname, 366 &error); 367 368 if (error != NULL) 369 { 370 g_print ("g_filename_to_uri failed unexpectedly: %s\n", 371 error->message); 372 any_failed = TRUE; 373 continue; 374 } 375 376 error = NULL; 377 res = g_filename_from_uri (uri, &hostname, &error); 378 if (error != NULL) 379 { 380 g_print ("g_filename_from_uri failed unexpectedly: %s\n", 381 error->message); 382 any_failed = TRUE; 383 continue; 384 } 385 386 if (safe_strcmp_filename (to_uri_tests[i].filename, res)) 387 { 388 g_print ("roundtrip test %d failed, filename modified: " 389 " expected \"%s\", but got \"%s\"\n", 390 i, to_uri_tests[i].filename, res); 391 any_failed = TRUE; 392 } 393 394 if (safe_strcmp_hostname (to_uri_tests[i].hostname, hostname)) 395 { 396 g_print ("roundtrip test %d failed, hostname modified: " 397 " expected \"%s\", but got \"%s\"\n", 398 i, to_uri_tests[i].hostname, hostname); 399 any_failed = TRUE; 400 } 401 } 402 } 403 404 static void 405 run_uri_list_tests (void) 406 { 407 /* straight from the RFC */ 408 gchar *list = 409 "# urn:isbn:0-201-08372-8\r\n" 410 "http://www.huh.org/books/foo.html\r\n" 411 "http://www.huh.org/books/foo.pdf \r\n" 412 " ftp://ftp.foo.org/books/foo.txt\r\n"; 413 gchar *expected_uris[] = { 414 "http://www.huh.org/books/foo.html", 415 "http://www.huh.org/books/foo.pdf", 416 "ftp://ftp.foo.org/books/foo.txt" 417 }; 418 419 gchar **uris; 420 gint j; 421 422 uris = g_uri_list_extract_uris (list); 423 424 if (g_strv_length (uris) != 3) 425 { 426 g_print ("uri list test failed: " 427 " expected %d uris, but got %d\n", 428 3, g_strv_length (uris)); 429 any_failed = TRUE; 430 } 431 432 for (j = 0; j < 3; j++) 433 { 434 if (safe_strcmp (uris[j], expected_uris[j])) 435 { 436 g_print ("uri list test failed: " 437 " expected \"%s\", but got \"%s\"\n", 438 expected_uris[j], uris[j]); 439 any_failed = TRUE; 440 } 441 } 442 443 g_strfreev (uris); 444 445 uris = g_uri_list_extract_uris ("# just hot air\r\n# more hot air"); 446 if (g_strv_length (uris) != 0) 447 { 448 g_print ("uri list test 2 failed: " 449 " expected %d uris, but got %d (first is \"%s\")\n", 450 0, g_strv_length (uris), uris[0]); 451 any_failed = TRUE; 452 } 453 454 } 455 456 int 457 main (int argc, 458 char *argv[]) 459 { 460 #ifdef G_OS_UNIX 461 # ifdef HAVE_UNSETENV 462 unsetenv ("G_BROKEN_FILENAMES"); 463 # else 464 /* putenv with no = isn't standard, but works to unset the variable 465 * on some systems 466 */ 467 putenv ("G_BROKEN_FILENAMES"); 468 # endif 469 #endif 470 471 run_to_uri_tests (); 472 run_from_uri_tests (); 473 run_roundtrip_tests (); 474 run_uri_list_tests (); 475 476 return any_failed ? 1 : 0; 477 } 478