Imported Upstream version 2.66.6
[platform/upstream/glib.git] / glib / tests / uri.c
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.1 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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GLib Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GLib at ftp://ftp.gtk.org/pub/gtk/.
23  */
24
25 #include <glib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include "gstrfuncsprivate.h"
31
32 typedef struct
33 {
34   char *filename;
35   char *hostname;
36   char *expected_result;
37   GConvertError expected_error; /* If failed */
38 } FileToUriTest;
39
40 FileToUriTest
41 file_to_uri_tests[] = {
42   { "/etc", NULL, "file:///etc"},
43   { "/etc", "", "file:///etc"},
44   { "/etc", "otherhost", "file://otherhost/etc"},
45 #ifdef G_OS_WIN32
46   { "/etc", "localhost", "file:///etc"},
47   { "c:\\windows", NULL, "file:///c:/windows"},
48   { "c:\\windows", "localhost", "file:///c:/windows"},
49   { "c:\\windows", "otherhost", "file://otherhost/c:/windows"},
50   { "\\\\server\\share\\dir", NULL, "file:////server/share/dir"},
51   { "\\\\server\\share\\dir", "localhost", "file:////server/share/dir"},
52 #else
53   { "/etc", "localhost", "file://localhost/etc"},
54   { "c:\\windows", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, /* it's important to get this error on Unix */
55   { "c:\\windows", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
56   { "c:\\windows", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
57 #endif
58   { "etc", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
59 #ifndef G_PLATFORM_WIN32
60   { "/etc/\xE5\xE4\xF6", NULL, "file:///etc/%E5%E4%F6" },
61   { "/etc/\xC3\xB6\xC3\xA4\xC3\xA5", NULL, "file:///etc/%C3%B6%C3%A4%C3%A5"},
62 #endif
63   { "/etc", "\xC3\xB6\xC3\xA4\xC3\xA5", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
64   { "/etc", "\xE5\xE4\xF6", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
65   { "/etc/file with #%", NULL, "file:///etc/file%20with%20%23%25"},
66   { "", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
67   { "", "", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
68   { "", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
69   { "", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
70   { "/0123456789", NULL, "file:///0123456789"},
71   { "/ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL, "file:///ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
72   { "/abcdefghijklmnopqrstuvwxyz", NULL, "file:///abcdefghijklmnopqrstuvwxyz"},
73   { "/-_.!~*'()", NULL, "file:///-_.!~*'()"},
74 #ifdef G_OS_WIN32
75   /* As '\\' is a path separator on Win32, it gets turned into '/' in the URI */
76   { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B/%5D%5E%60%7B%7C%7D%7F"},
77 #else
78   /* On Unix, '\\' is a normal character in the file name */
79   { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B%5C%5D%5E%60%7B%7C%7D%7F"},
80 #endif
81   { "/;@+$,", NULL, "file:///%3B@+$,"},
82   /* This and some of the following are of course as such illegal file names on Windows,
83    * and would not occur in real life.
84    */
85   { "/:", NULL, "file:///:"},
86   { "/?&=", NULL, "file:///%3F&="},
87   { "/", "0123456789-", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
88   { "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/"},
89   { "/", "abcdefghijklmnopqrstuvwxyz", "file://abcdefghijklmnopqrstuvwxyz/"},
90   { "/", "_.!~*'()", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
91   { "/", "\"#%<>[\\]^`{|}\x7F", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
92   { "/", ";?&=+$,", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
93   { "/", "/", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
94   { "/", "@:", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
95   { "/", "\x80\xFF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
96   { "/", "\xC3\x80\xC3\xBF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
97 };
98
99
100 typedef struct
101 {
102   char *uri;
103   char *expected_filename;
104   char *expected_hostname;
105   GConvertError expected_error; /* If failed */
106 } FileFromUriTest;
107
108 FileFromUriTest
109 file_from_uri_tests[] = {
110   { "file:///etc", "/etc"},
111   { "file:/etc", "/etc"},
112 #ifdef G_OS_WIN32
113   /* On Win32 we don't return "localhost" hostames, just in case
114    * it isn't recognized anyway.
115    */
116   { "file://localhost/etc", "/etc", NULL},
117   { "file://localhost/etc/%23%25%20file", "/etc/#% file", NULL},
118   { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", NULL},
119   { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", NULL},
120 #else
121   { "file://localhost/etc", "/etc", "localhost"},
122   { "file://localhost/etc/%23%25%20file", "/etc/#% file", "localhost"},
123   { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", "localhost"},
124   { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", "localhost"},
125 #endif
126   { "file://otherhost/etc", "/etc", "otherhost"},
127   { "file://otherhost/etc/%23%25%20file", "/etc/#% file", "otherhost"},
128   { "file://%C3%B6%C3%A4%C3%A5/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
129   { "file:////etc/%C3%B6%C3%C3%C3%A5", "//etc/\xc3\xb6\xc3\xc3\xc3\xa5", NULL},
130   { "file://\xE5\xE4\xF6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
131   { "file://%E5%E4%F6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
132   { "file:///some/file#bad", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
133   { "file://some", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
134   { "", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
135   { "file:test", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
136   { "http://www.yahoo.com/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
137   { "file:////etc", "//etc"},
138   { "file://///etc", "///etc"},
139 #ifdef G_OS_WIN32
140   /* URIs with backslashes come from some nonstandard application, but accept them anyhow */
141   { "file:///c:\\foo", "c:\\foo"},
142   { "file:///c:/foo\\bar", "c:\\foo\\bar"},
143   /* Accept also the old Netscape drive-letter-and-vertical bar convention */
144   { "file:///c|/foo", "c:\\foo"},
145   { "file:////server/share/dir", "\\\\server\\share\\dir"},
146   { "file://localhost//server/share/foo", "\\\\server\\share\\foo"},
147   { "file://otherhost//server/share/foo", "\\\\server\\share\\foo", "otherhost"},
148 #else
149   { "file:///c:\\foo", "/c:\\foo"},
150   { "file:///c:/foo", "/c:/foo"},
151   { "file:////c:/foo", "//c:/foo"},
152 #endif
153   { "file://0123456789/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
154   { "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/", "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
155   { "file://abcdefghijklmnopqrstuvwxyz/", "/", "abcdefghijklmnopqrstuvwxyz"},
156   { "file://-_.!~*'()/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
157   { "file://\"<>[\\]^`{|}\x7F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
158   { "file://;?&=+$,/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
159   { "file://%C3%80%C3%BF/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
160   { "file://@/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
161   { "file://:/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
162   { "file://#/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
163   { "file://%23/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
164   { "file://%2F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
165 };
166
167 static void
168 run_file_to_uri_tests (void)
169 {
170   int i;
171   gchar *res;
172   GError *error;
173
174   for (i = 0; i < G_N_ELEMENTS (file_to_uri_tests); i++)
175     {
176       error = NULL;
177       res = g_filename_to_uri (file_to_uri_tests[i].filename,
178                                file_to_uri_tests[i].hostname,
179                                &error);
180
181       if (res)
182         g_assert_cmpstr (res, ==, file_to_uri_tests[i].expected_result);
183       else
184         g_assert_error (error, G_CONVERT_ERROR, file_to_uri_tests[i].expected_error);
185
186       g_free (res);
187       g_clear_error (&error);
188     }
189 }
190
191 static void
192 run_file_from_uri_tests (void)
193 {
194   int i;
195   gchar *res;
196   gchar *hostname;
197   GError *error;
198
199   for (i = 0; i < G_N_ELEMENTS (file_from_uri_tests); i++)
200     {
201       error = NULL;
202       res = g_filename_from_uri (file_from_uri_tests[i].uri,
203                                  &hostname,
204                                  &error);
205
206 #ifdef G_OS_WIN32
207       if (file_from_uri_tests[i].expected_filename)
208         {
209           gchar *p, *slash;
210           p = file_from_uri_tests[i].expected_filename =
211             g_strdup (file_from_uri_tests[i].expected_filename);
212           while ((slash = strchr (p, '/')) != NULL)
213             {
214               *slash = '\\';
215               p = slash + 1;
216             }
217         }
218 #endif
219       if (res)
220         g_assert_cmpstr (res, ==, file_from_uri_tests[i].expected_filename);
221       else
222         g_assert_error (error, G_CONVERT_ERROR, file_from_uri_tests[i].expected_error);
223       g_assert_cmpstr (hostname, ==, file_from_uri_tests[i].expected_hostname);
224
225       g_free (res);
226       g_free (hostname);
227       g_clear_error (&error);
228     }
229 }
230
231 static gint
232 safe_strcmp_filename (const gchar *a, const gchar *b)
233 {
234 #ifndef G_OS_WIN32
235   return g_strcmp0 (a, b);
236 #else
237   if (!a || !b)
238     return g_strcmp0 (a, b);
239   else
240     {
241       while (*a && *b)
242         {
243           if ((G_IS_DIR_SEPARATOR (*a) && G_IS_DIR_SEPARATOR (*b)) ||
244               *a == *b)
245             a++, b++;
246           else
247             return (*a - *b);
248         }
249       return (*a - *b);
250     }
251 #endif
252 }
253
254 static gint
255 safe_strcmp_hostname (const gchar *a, const gchar *b)
256 {
257   if (a == NULL)
258     a = "";
259   if (b == NULL)
260     b = "";
261 #ifndef G_OS_WIN32
262   return strcmp (a, b);
263 #else
264   if (strcmp (a, "localhost") == 0 && !*b)
265     return 0;
266   else
267     return strcmp (a, b);
268 #endif
269 }
270
271 static void
272 run_file_roundtrip_tests (void)
273 {
274   int i;
275   gchar *uri, *hostname, *res;
276   GError *error;
277
278   for (i = 0; i < G_N_ELEMENTS (file_to_uri_tests); i++)
279     {
280       if (file_to_uri_tests[i].expected_error != 0)
281         continue;
282
283       error = NULL;
284       uri = g_filename_to_uri (file_to_uri_tests[i].filename,
285                                file_to_uri_tests[i].hostname,
286                                &error);
287       g_assert_no_error (error);
288
289       hostname = NULL;
290       res = g_filename_from_uri (uri, &hostname, &error);
291       g_assert_no_error (error);
292
293       g_assert_cmpint (safe_strcmp_filename (file_to_uri_tests[i].filename, res), ==, 0);
294       g_assert_cmpint (safe_strcmp_hostname (file_to_uri_tests[i].hostname, hostname), ==, 0);
295       g_free (res);
296       g_free (uri);
297       g_free (hostname);
298     }
299 }
300
301 static void
302 run_uri_list_tests (void)
303 {
304   /* straight from the RFC */
305   gchar *list =
306     "# urn:isbn:0-201-08372-8\r\n"
307     "http://www.huh.org/books/foo.html\r\n"
308     "http://www.huh.org/books/foo.pdf   \r\n"
309     "   ftp://ftp.foo.org/books/foo.txt\r\n";
310   gchar *expected_uris[] = {
311     "http://www.huh.org/books/foo.html",
312     "http://www.huh.org/books/foo.pdf",
313     "ftp://ftp.foo.org/books/foo.txt"
314   };
315
316   gchar **uris;
317   gint j;
318
319   uris = g_uri_list_extract_uris (list);
320   g_assert_cmpint (g_strv_length (uris), ==, 3);
321
322   for (j = 0; j < 3; j++)
323     g_assert_cmpstr (uris[j], ==, expected_uris[j]);
324
325   g_strfreev (uris);
326
327   uris = g_uri_list_extract_uris ("# just hot air\r\n# more hot air");
328   g_assert_cmpint (g_strv_length (uris), ==, 0);
329   g_strfreev (uris);
330 }
331
332 static void
333 test_uri_unescape_string (void)
334 {
335   const struct
336     {
337       /* Inputs */
338       const gchar *escaped;  /* (nullable) */
339       const gchar *illegal_characters;  /* (nullable) */
340       /* Outputs */
341       const gchar *expected_unescaped;  /* (nullable) */
342     }
343   tests[] =
344     {
345       { "%2Babc %4F", NULL, "+abc O" },
346       { "%2Babc %4F", "+", NULL },
347       { "%00abc %4F", "+/", NULL },
348       { "/cursors/none.png", "/", "/cursors/none.png" },
349       { "/cursors%2fbad-subdir/none.png", "/", NULL },
350       { "%0", NULL, NULL },
351       { "%ra", NULL, NULL },
352       { "%2r", NULL, NULL },
353       { "Timm B\344der", NULL, "Timm B\344der" },
354       { NULL, NULL, NULL },  /* actually a valid test, not a delimiter */
355     };
356   gsize i;
357
358   for (i = 0; i < G_N_ELEMENTS (tests); i++)
359     {
360       gchar *s = NULL;
361
362       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
363
364       s = g_uri_unescape_string (tests[i].escaped, tests[i].illegal_characters);
365       g_assert_cmpstr (s, ==, tests[i].expected_unescaped);
366       g_free (s);
367     }
368 }
369
370 static void
371 test_uri_unescape_bytes (gconstpointer test_data)
372 {
373   GError *error = NULL;
374   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
375   const struct
376     {
377       /* Inputs */
378       const gchar *escaped;  /* (nullable) */
379       const gchar *illegal;
380       /* Outputs */
381       gssize expected_unescaped_len;  /* -1 => error expected */
382       const guint8 *expected_unescaped;  /* (nullable) */
383     }
384   tests[] =
385     {
386       { "%00%00", NULL, 2, (const guint8 *) "\x00\x00" },
387       { "/cursors/none.png", "/", 17, (const guint8 *) "/cursors/none.png" },
388       { "/cursors%2fbad-subdir/none.png", "/", -1, NULL },
389       { "%%", NULL, -1, NULL },
390       { "%", NULL, -1, NULL },
391     };
392   gsize i;
393
394   for (i = 0; i < G_N_ELEMENTS (tests); i++)
395     {
396       gssize escaped_len = 0;
397       gchar *escaped = NULL;
398       GBytes *bytes = NULL;
399
400       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
401
402       /* The tests get run twice: once with the length unspecified, using a
403        * nul-terminated string; and once with the length specified and a copy of
404        * the string with the trailing nul explicitly removed (to help catch
405        * buffer overflows). */
406       if (use_nul_terminated)
407         {
408           escaped_len = -1;
409           escaped = g_strdup (tests[i].escaped);
410         }
411       else
412         {
413           escaped_len = strlen (tests[i].escaped);  /* no trailing nul */
414           escaped = g_memdup2 (tests[i].escaped, escaped_len);
415         }
416
417       bytes = g_uri_unescape_bytes (escaped, escaped_len, tests[i].illegal, &error);
418
419       if (tests[i].expected_unescaped_len < 0)
420         {
421           g_assert_null (bytes);
422           g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
423           g_clear_error (&error);
424         }
425       else
426         {
427           g_assert_no_error (error);
428           g_assert_cmpmem (g_bytes_get_data (bytes, NULL),
429                            g_bytes_get_size (bytes),
430                            tests[i].expected_unescaped,
431                            tests[i].expected_unescaped_len);
432         }
433
434       g_clear_pointer (&bytes, g_bytes_unref);
435       g_free (escaped);
436     }
437 }
438
439 static void
440 test_uri_unescape_segment (void)
441 {
442   const gchar *escaped_segment = "%2Babc %4F---";
443   gchar *s = NULL;
444
445   s = g_uri_unescape_segment (escaped_segment, escaped_segment + 10, NULL);
446   g_assert_cmpstr (s, ==, "+abc O");
447   g_free (s);
448
449   s = g_uri_unescape_segment ("%2Babc%00cde", NULL, NULL);
450   g_assert_null (s);
451 }
452
453 static void
454 test_uri_escape_string (void)
455 {
456   const struct
457     {
458       /* Inputs */
459       const gchar *unescaped;
460       const gchar *reserved_chars_allowed;
461       gboolean allow_utf8;
462       /* Outputs */
463       const gchar *expected_escaped;
464     }
465   tests[] =
466     {
467       { "abcdefgABCDEFG._~", NULL, FALSE, "abcdefgABCDEFG._~" },
468       { ":+ \\?#", NULL, FALSE, "%3A%2B%20%5C%3F%23" },
469       { "a+b:c", "+", FALSE, "a+b%3Ac" },
470       { "a+b:c\303\234", "+", TRUE, "a+b%3Ac\303\234" },
471       /* Incomplete UTF-8 sequence: */
472       { "\xfc\x3b\xd2", NULL, TRUE, "%FC%3B%D2" },
473       /* Invalid sequence: */
474       { "\xc3\xb1\xc3\x28", NULL, TRUE, "ñ%C3%28" },
475     };
476   gsize i;
477
478   for (i = 0; i < G_N_ELEMENTS (tests); i++)
479     {
480       gchar *s = NULL;
481
482       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].unescaped);
483
484       s = g_uri_escape_string (tests[i].unescaped,
485                                tests[i].reserved_chars_allowed,
486                                tests[i].allow_utf8);
487       g_assert_cmpstr (s, ==, tests[i].expected_escaped);
488       g_free (s);
489     }
490 }
491
492 static void
493 test_uri_escape_bytes (void)
494 {
495   gchar *s = NULL;
496
497   s = g_uri_escape_bytes ((guchar*)"\0\0", 2, NULL);
498   g_assert_cmpstr (s, ==, "%00%00");
499   g_free (s);
500 }
501
502 static void
503 test_uri_scheme (void)
504 {
505   const gchar *s1, *s2;
506   gchar *s;
507
508   s = g_uri_parse_scheme ("ftp://ftp.gtk.org");
509   g_assert_cmpstr (s, ==, "ftp");
510   g_free (s);
511
512   s = g_uri_parse_scheme ("good-scheme.but+weird:gtk.org");
513   g_assert_cmpstr (s, ==, "good-scheme.but+weird");
514   g_free (s);
515
516   s = g_uri_parse_scheme ("1bad:");
517   g_assert_null (s);
518   s = g_uri_parse_scheme ("bad");
519   g_assert_null (s);
520   s = g_uri_parse_scheme ("99http://host/path");
521   g_assert_null (s);
522   s = g_uri_parse_scheme (".http://host/path");
523   g_assert_null (s);
524   s = g_uri_parse_scheme ("+http://host/path");
525   g_assert_null (s);
526
527   s1 = g_uri_peek_scheme ("ftp://ftp.gtk.org");
528   g_assert_cmpstr (s1, ==, "ftp");
529   s2 = g_uri_peek_scheme ("FTP://ftp.gtk.org");
530   g_assert_cmpstr (s2, ==, "ftp");
531   g_assert_true (s1 == s2);
532   s1 = g_uri_peek_scheme ("1bad:");
533   g_assert_null (s1);
534   s1 = g_uri_peek_scheme ("bad");
535   g_assert_null (s1);
536 }
537
538 typedef struct {
539   const gchar *scheme;
540   const gchar *userinfo;
541   const gchar *host;
542   gint         port;
543   const gchar *path;
544   const gchar *query;
545   const gchar *fragment;
546 } UriParts;
547
548 typedef struct {
549   /* Inputs */
550   const gchar *orig;
551   GUriFlags flags;
552   /* Outputs */
553   gboolean expected_success;
554   GUriError expected_error_code;  /* unused if @expected_success is true */
555   const UriParts expected_parts;  /* unused if @expected_success is false */
556 } UriAbsoluteTest;
557
558 static const UriAbsoluteTest absolute_tests[] = {
559   { "foo:", G_URI_FLAGS_NONE, TRUE, 0,
560     { "foo", NULL, NULL, -1, "", NULL, NULL }
561   },
562   { "file:/dev/null", G_URI_FLAGS_NONE, TRUE, 0,
563     { "file", NULL, NULL, -1, "/dev/null", NULL, NULL }
564   },
565   { "file:///dev/null", G_URI_FLAGS_NONE, TRUE, 0,
566     { "file", NULL, "", -1, "/dev/null", NULL, NULL }
567   },
568   { "ftp://user@host/path", G_URI_FLAGS_NONE, TRUE, 0,
569     { "ftp", "user", "host", -1, "/path", NULL, NULL }
570   },
571   { "ftp://user@host:9999/path", G_URI_FLAGS_NONE, TRUE, 0,
572     { "ftp", "user", "host", 9999, "/path", NULL, NULL }
573   },
574   { "ftp://user:password@host/path", G_URI_FLAGS_NONE, TRUE, 0,
575     { "ftp", "user:password", "host", -1, "/path", NULL, NULL }
576   },
577   { "ftp://user:password@host:9999/path", G_URI_FLAGS_NONE, TRUE, 0,
578     { "ftp", "user:password", "host", 9999, "/path", NULL, NULL }
579   },
580   { "ftp://user:password@host", G_URI_FLAGS_NONE, TRUE, 0,
581     { "ftp", "user:password", "host", -1, "", NULL, NULL }
582   },
583   { "http://us%65r@host", G_URI_FLAGS_NONE, TRUE, 0,
584     { "http", "user", "host", -1, "", NULL, NULL }
585   },
586   { "http://us%40r@host", G_URI_FLAGS_NONE, TRUE, 0,
587     { "http", "us@r", "host", -1, "", NULL, NULL }
588   },
589   { "http://us%3ar@host", G_URI_FLAGS_NONE, TRUE, 0,
590     { "http", "us:r", "host", -1, "", NULL, NULL }
591   },
592   { "http://us%2fr@host", G_URI_FLAGS_NONE, TRUE, 0,
593     { "http", "us/r", "host", -1, "", NULL, NULL }
594   },
595   { "http://us%3fr@host", G_URI_FLAGS_NONE, TRUE, 0,
596     { "http", "us?r", "host", -1, "", NULL, NULL }
597   },
598   { "http://host?query", G_URI_FLAGS_NONE, TRUE, 0,
599     { "http", NULL, "host", -1, "", "query", NULL }
600   },
601   { "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value", G_URI_FLAGS_NONE, TRUE, 0,
602     { "http", NULL, "host", -1, "/path", "query=http://host/path?childparam=childvalue&param=value", NULL }
603   },
604   { "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", G_URI_FLAGS_NONE, TRUE, 0,
605     { "http", NULL, "control-chars", -1, "/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F", NULL, NULL }
606   },
607   { "http://space/%20", G_URI_FLAGS_NONE, TRUE, 0,
608     { "http", NULL, "space", -1, "/ ", NULL, NULL }
609   },
610   { "http://delims/%3C%3E%23%25%22", G_URI_FLAGS_NONE, TRUE, 0,
611     { "http", NULL, "delims", -1, "/<>#%\"", NULL, NULL }
612   },
613   { "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", G_URI_FLAGS_NONE, TRUE, 0,
614     { "http", NULL, "unwise-chars", -1, "/{}|\\^[]`", NULL, NULL }
615   },
616
617   /* From RFC 2732 */
618   { "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", G_URI_FLAGS_NONE, TRUE, 0,
619     { "http", NULL, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", 80, "/index.html", NULL, NULL }
620   },
621   { "http://[1080:0:0:0:8:800:200C:417A]/index.html", G_URI_FLAGS_NONE, TRUE, 0,
622     { "http", NULL, "1080:0:0:0:8:800:200C:417A", -1, "/index.html", NULL, NULL }
623   },
624   { "http://[3ffe:2a00:100:7031::1]", G_URI_FLAGS_NONE, TRUE, 0,
625     { "http", NULL, "3ffe:2a00:100:7031::1", -1, "", NULL, NULL }
626   },
627   { "http://[1080::8:800:200C:417A]/foo", G_URI_FLAGS_NONE, TRUE, 0,
628     { "http", NULL, "1080::8:800:200C:417A", -1, "/foo", NULL, NULL }
629   },
630   { "http://[::192.9.5.5]/ipng", G_URI_FLAGS_NONE, TRUE, 0,
631     { "http", NULL, "::192.9.5.5", -1, "/ipng", NULL, NULL }
632   },
633   { "http://[::FFFF:129.144.52.38]:80/index.html", G_URI_FLAGS_NONE, TRUE, 0,
634     { "http", NULL, "::FFFF:129.144.52.38", 80, "/index.html", NULL, NULL }
635   },
636   { "http://[2010:836B:4179::836B:4179]", G_URI_FLAGS_NONE, TRUE, 0,
637     { "http", NULL, "2010:836B:4179::836B:4179", -1, "", NULL, NULL }
638   },
639
640   /* some problematic URIs that are handled differently in libsoup */
641   { "http://host/path with spaces", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
642     { "http", NULL, "host", -1, "/path with spaces", NULL, NULL }
643   },
644   { "  http://host/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
645     { "http", NULL, "host", -1, "/path", NULL, NULL }
646   },
647   { "http://host/path  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
648     { "http", NULL, "host", -1, "/path", NULL, NULL }
649   },
650   { "http://host  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
651     { "http", NULL, "host", -1, "", NULL, NULL }
652   },
653   { "http://host:999  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
654     { "http", NULL, "host", 999, "", NULL, NULL }
655   },
656   { "http://host/pa\nth", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
657     { "http", NULL, "host", -1, "/path", NULL, NULL }
658   },
659   { "http:\r\n//host/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
660     { "http", NULL, "host", -1, "/path", NULL, NULL }
661   },
662   { "http://\thost/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
663     { "http", NULL, "host", -1, "/path", NULL, NULL }
664   },
665
666   /* Bug 594405; 0-length is different from not-present */
667   { "http://host/path?", G_URI_FLAGS_NONE, TRUE, 0,
668     { "http", NULL, "host", -1, "/path", "", NULL }
669   },
670   { "http://host/path#", G_URI_FLAGS_NONE, TRUE, 0,
671     { "http", NULL, "host", -1, "/path", NULL, "" },
672   },
673
674   /* Bug 590524; ignore bad %-encoding */
675   { "http://host/path%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
676     { "http", NULL, "host", -1, "/path%", NULL, NULL }
677   },
678   { "http://h%ost/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
679     { "http", NULL, "h%ost", -1, "/path", NULL, NULL }
680   },
681   { "http://host/path%%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
682     { "http", NULL, "host", -1, "/path%%", NULL, NULL }
683   },
684   { "http://host/path%%%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
685     { "http", NULL, "host", -1, "/path%%%", NULL, NULL }
686   },
687   { "http://host/path%/x/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
688     { "http", NULL, "host", -1, "/path%/x/", NULL, NULL }
689   },
690   { "http://host/path%0x/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
691     { "http", NULL, "host", -1, "/path%0x/", NULL, NULL }
692   },
693   { "http://host/path%ax", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
694     { "http", NULL, "host", -1, "/path%ax", NULL, NULL }
695   },
696
697   /* GUri doesn't %-encode non-ASCII characters */
698   { "http://host/p\xc3\xa4th/", G_URI_FLAGS_NONE, TRUE, 0,
699     { "http", NULL, "host", -1, "/p\xc3\xa4th/", NULL, NULL }
700   },
701
702   { "HTTP:////////////////", G_URI_FLAGS_NONE, TRUE, 0,
703     { "http", NULL, "", -1, "//////////////", NULL, NULL }
704   },
705
706   { "http://@host", G_URI_FLAGS_NONE, TRUE, 0,
707     { "http", "", "host", -1, "", NULL, NULL }
708   },
709   { "http://:@host", G_URI_FLAGS_NONE, TRUE, 0,
710     { "http", ":", "host", -1, "", NULL, NULL }
711   },
712   { "scheme://foo%3Abar._webdav._tcp.local", G_URI_FLAGS_NONE, TRUE, 0,
713     { "scheme", NULL, "foo:bar._webdav._tcp.local", -1, "", NULL, NULL}
714   },
715
716   /* ".." past top */
717   { "http://example.com/..", G_URI_FLAGS_NONE, TRUE, 0,
718     { "http", NULL, "example.com", -1, "/..", NULL, NULL }
719   },
720
721   /* scheme parsing */
722   { "foo0://host/path", G_URI_FLAGS_NONE, TRUE, 0,
723     { "foo0", NULL, "host", -1, "/path", NULL, NULL } },
724   { "f0.o://host/path", G_URI_FLAGS_NONE, TRUE, 0,
725     { "f0.o", NULL, "host", -1, "/path", NULL, NULL } },
726   { "http++://host/path", G_URI_FLAGS_NONE, TRUE, 0,
727     { "http++", NULL, "host", -1, "/path", NULL, NULL } },
728   { "http-ish://host/path", G_URI_FLAGS_NONE, TRUE, 0,
729     { "http-ish", NULL, "host", -1, "/path", NULL, NULL } },
730
731   /* IPv6 scope ID parsing (both correct and incorrect) */
732   { "http://[fe80::dead:beef%]/", G_URI_FLAGS_PARSE_RELAXED, FALSE, G_URI_ERROR_BAD_HOST,
733     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
734   { "http://[fe80::dead:beef%em1]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
735     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
736   { "http://[fe80::dead:beef%em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
737     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
738   { "http://[fe80::dead:beef%25em1]/", G_URI_FLAGS_NONE, TRUE, 0,
739     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
740   { "http://[fe80::dead:beef%25em1%20]/", G_URI_FLAGS_NONE, TRUE, 0,
741     { "http", NULL, "fe80::dead:beef%em1 ", -1, "/", NULL, NULL } },
742   { "http://[fe80::dead:beef%25em%31]/", G_URI_FLAGS_NONE, TRUE, 0,
743     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
744   { "http://[fe80::dead:beef%10]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
745     { "http", NULL, "fe80::dead:beef%10", -1, "/", NULL, NULL } },
746   { "http://[fe80::dead:beef%10]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
747     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
748   { "http://[fe80::dead:beef%25]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
749     { "http", NULL, "fe80::dead:beef%25", -1, "/", NULL, NULL } },
750   { "http://[fe80::dead:beef%25]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
751     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
752   { "http://[192.168.0.1%25em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
753     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
754   { "http://[fe80::dead:beef%2em1]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
755     { "http", NULL, "fe80::dead:beef%2em1", -1, "/", NULL, NULL } },
756   { "http://[fe80::dead:beef%2em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
757     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
758   { "http://[fe80::dead:beef%25em1%00]/", G_URI_FLAGS_PARSE_RELAXED, FALSE, G_URI_ERROR_BAD_HOST,
759     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
760   { "http://[fe80::dead:beef%25em1%00]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
761     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
762 };
763
764 static void
765 test_uri_parsing_absolute (void)
766 {
767   gsize i;
768
769   for (i = 0; i < G_N_ELEMENTS (absolute_tests); i++)
770     {
771       const UriAbsoluteTest *test = &absolute_tests[i];
772       GError *error = NULL;
773       GUri *uri;
774
775       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, test->orig);
776
777       uri = g_uri_parse (test->orig, test->flags, &error);
778       if (test->expected_success)
779         {
780           g_assert_no_error (error);
781
782           g_assert_cmpstr (g_uri_get_scheme (uri),   ==, test->expected_parts.scheme);
783           g_assert_cmpstr (g_uri_get_userinfo (uri), ==, test->expected_parts.userinfo);
784           g_assert_cmpstr (g_uri_get_host (uri),     ==, test->expected_parts.host);
785           g_assert_cmpint (g_uri_get_port (uri),     ==, test->expected_parts.port);
786           g_assert_cmpstr (g_uri_get_path (uri),     ==, test->expected_parts.path);
787           g_assert_cmpstr (g_uri_get_query (uri),    ==, test->expected_parts.query);
788           g_assert_cmpstr (g_uri_get_fragment (uri), ==, test->expected_parts.fragment);
789         }
790       else
791         {
792           g_assert_error (error, G_URI_ERROR, test->expected_error_code);
793           g_assert_null (uri);
794         }
795
796       g_clear_pointer (&uri, g_uri_unref);
797       g_clear_error (&error);
798     }
799 }
800
801 typedef struct {
802   const gchar *orig, *resolved;
803   UriParts parts;
804 } UriRelativeTest;
805
806 /* This all comes from RFC 3986 */
807 static const char *relative_test_base = "http://a/b/c/d;p?q";
808 static const UriRelativeTest relative_tests[] = {
809   { "g:h", "g:h",
810     { "g", NULL, NULL, -1, "h", NULL, NULL } },
811   { "g", "http://a/b/c/g",
812     { "http", NULL, "a", -1, "/b/c/g", NULL, NULL } },
813   { "./g", "http://a/b/c/g",
814     { "http", NULL, "a", -1, "/b/c/g", NULL, NULL } },
815   { "g/", "http://a/b/c/g/",
816     { "http", NULL, "a", -1, "/b/c/g/", NULL, NULL } },
817   { "/g", "http://a/g",
818     { "http", NULL, "a", -1, "/g", NULL, NULL } },
819   { "//g", "http://g",
820     { "http", NULL, "g", -1, "", NULL, NULL } },
821   { "?y", "http://a/b/c/d;p?y",
822     { "http", NULL, "a", -1, "/b/c/d;p", "y", NULL } },
823   { "g?y", "http://a/b/c/g?y",
824     { "http", NULL, "a", -1, "/b/c/g", "y", NULL } },
825   { "#s", "http://a/b/c/d;p?q#s",
826     { "http", NULL, "a", -1, "/b/c/d;p", "q", "s" } },
827   { "g#s", "http://a/b/c/g#s",
828     { "http", NULL, "a", -1, "/b/c/g", NULL, "s" } },
829   { "g?y#s", "http://a/b/c/g?y#s",
830     { "http", NULL, "a", -1, "/b/c/g", "y", "s" } },
831   { ";x", "http://a/b/c/;x",
832     { "http", NULL, "a", -1, "/b/c/;x", NULL, NULL } },
833   { "g;x", "http://a/b/c/g;x",
834     { "http", NULL, "a", -1, "/b/c/g;x", NULL, NULL } },
835   { "g;x?y#s", "http://a/b/c/g;x?y#s",
836     { "http", NULL, "a", -1, "/b/c/g;x", "y", "s" } },
837   { ".", "http://a/b/c/",
838     { "http", NULL, "a", -1, "/b/c/", NULL, NULL } },
839   { "./", "http://a/b/c/",
840     { "http", NULL, "a", -1, "/b/c/", NULL, NULL } },
841   { "..", "http://a/b/",
842     { "http", NULL, "a", -1, "/b/", NULL, NULL } },
843   { "../", "http://a/b/",
844     { "http", NULL, "a", -1, "/b/", NULL, NULL } },
845   { "../g", "http://a/b/g",
846     { "http", NULL, "a", -1, "/b/g", NULL, NULL } },
847   { "../..", "http://a/",
848     { "http", NULL, "a", -1, "/", NULL, NULL } },
849   { "../../", "http://a/",
850     { "http", NULL, "a", -1, "/", NULL, NULL } },
851   { "../../g", "http://a/g",
852     { "http", NULL, "a", -1, "/g", NULL, NULL } },
853   { "", "http://a/b/c/d;p?q",
854     { "http", NULL, "a", -1, "/b/c/d;p", "q", NULL } },
855   { "../../../g", "http://a/g",
856     { "http", NULL, "a", -1, "/g", NULL, NULL } },
857   { "../../../../g", "http://a/g",
858     { "http", NULL, "a", -1, "/g", NULL, NULL } },
859   { "/./g", "http://a/g",
860     { "http", NULL, "a", -1, "/g", NULL, NULL } },
861   { "/../g", "http://a/g",
862     { "http", NULL, "a", -1, "/g", NULL, NULL } },
863   { "g.", "http://a/b/c/g.",
864     { "http", NULL, "a", -1, "/b/c/g.", NULL, NULL } },
865   { ".g", "http://a/b/c/.g",
866     { "http", NULL, "a", -1, "/b/c/.g", NULL, NULL } },
867   { "g..", "http://a/b/c/g..",
868     { "http", NULL, "a", -1, "/b/c/g..", NULL, NULL } },
869   { "..g", "http://a/b/c/..g",
870     { "http", NULL, "a", -1, "/b/c/..g", NULL, NULL } },
871   { "./../g", "http://a/b/g",
872     { "http", NULL, "a", -1, "/b/g", NULL, NULL } },
873   { "./g/.", "http://a/b/c/g/",
874     { "http", NULL, "a", -1, "/b/c/g/", NULL, NULL } },
875   { "g/./h", "http://a/b/c/g/h",
876     { "http", NULL, "a", -1, "/b/c/g/h", NULL, NULL } },
877   { "g/../h", "http://a/b/c/h",
878     { "http", NULL, "a", -1, "/b/c/h", NULL, NULL } },
879   { "g;x=1/./y", "http://a/b/c/g;x=1/y",
880     { "http", NULL, "a", -1, "/b/c/g;x=1/y", NULL, NULL } },
881   { "g;x=1/../y", "http://a/b/c/y",
882     { "http", NULL, "a", -1, "/b/c/y", NULL, NULL } },
883   { "g?y/./x", "http://a/b/c/g?y/./x",
884     { "http", NULL, "a", -1, "/b/c/g", "y/./x", NULL } },
885   { "g?y/../x", "http://a/b/c/g?y/../x",
886     { "http", NULL, "a", -1, "/b/c/g", "y/../x", NULL } },
887   { "g#s/./x", "http://a/b/c/g#s/./x",
888     { "http", NULL, "a", -1, "/b/c/g", NULL, "s/./x" } },
889   { "g#s/../x", "http://a/b/c/g#s/../x",
890     { "http", NULL, "a", -1, "/b/c/g", NULL, "s/../x" } },
891   { "http:g", "http:g",
892     { "http", NULL, NULL, -1, "g", NULL, NULL } },
893   { "http://a/../..", "http://a/",
894     { "http", NULL, "a", -1, "/", NULL, NULL } }
895 };
896 static int num_relative_tests = G_N_ELEMENTS (relative_tests);
897
898 static void
899 test_uri_parsing_relative (void)
900 {
901   int i;
902   GUri *base, *uri;
903   GError *error = NULL;
904   gchar *resolved;
905
906   base = g_uri_parse (relative_test_base, G_URI_FLAGS_NONE, &error);
907   g_assert_no_error (error);
908
909   for (i = 0; i < num_relative_tests; i++)
910     {
911       const UriRelativeTest *test = &relative_tests[i];
912       gchar *tostring;
913
914       uri = g_uri_parse_relative (base, test->orig, G_URI_FLAGS_NONE, &error);
915       g_assert_no_error (error);
916
917       g_assert_cmpstr (g_uri_get_scheme (uri),   ==, test->parts.scheme);
918       g_assert_cmpstr (g_uri_get_userinfo (uri), ==, test->parts.userinfo);
919       g_assert_cmpstr (g_uri_get_host (uri),     ==, test->parts.host);
920       g_assert_cmpint (g_uri_get_port (uri),     ==, test->parts.port);
921       g_assert_cmpstr (g_uri_get_path (uri),     ==, test->parts.path);
922       g_assert_cmpstr (g_uri_get_query (uri),    ==, test->parts.query);
923       g_assert_cmpstr (g_uri_get_fragment (uri), ==, test->parts.fragment);
924
925       tostring = g_uri_to_string (uri);
926       g_assert_cmpstr (tostring, ==, test->resolved);
927       g_free (tostring);
928
929       g_uri_unref (uri);
930
931       resolved = g_uri_resolve_relative (relative_test_base, test->orig, G_URI_FLAGS_NONE, &error);
932       g_assert_no_error (error);
933       g_assert_cmpstr (resolved, ==, test->resolved);
934       g_free (resolved);
935     }
936   uri = g_uri_parse_relative (base, "%%", G_URI_FLAGS_NONE, &error);
937   g_assert_null (uri);
938   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PATH);
939   g_clear_error (&error);
940
941   g_uri_unref (base);
942
943   resolved = g_uri_resolve_relative (NULL, "http://a", G_URI_FLAGS_NONE, &error);
944   g_assert_no_error (error);
945   g_assert_cmpstr (resolved, ==, "http://a");
946   g_free (resolved);
947
948   resolved = g_uri_resolve_relative ("http://a", "b", G_URI_FLAGS_NONE, &error);
949   g_assert_no_error (error);
950   g_assert_cmpstr (resolved, ==, "http://a/b");
951   g_free (resolved);
952
953   resolved = g_uri_resolve_relative (NULL, "a", G_URI_FLAGS_NONE, &error);
954   g_assert_null (resolved);
955   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
956   g_clear_error (&error);
957
958   resolved = g_uri_resolve_relative ("../b", "a", G_URI_FLAGS_NONE, &error);
959   g_assert_null (resolved);
960   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
961   g_clear_error (&error);
962
963   resolved = g_uri_resolve_relative ("%%", "a", G_URI_FLAGS_PARSE_RELAXED, &error);
964   g_assert_null (resolved);
965   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
966   g_clear_error (&error);
967 }
968
969 static void
970 test_uri_to_string (void)
971 {
972   GUri *uri;
973   gchar *tostring;
974
975   uri = g_uri_build (G_URI_FLAGS_NONE, "scheme", "userinfo", "host", 1234,
976                      "/path", "query", "fragment");
977
978   tostring = g_uri_to_string (uri);
979   g_assert_cmpstr (tostring, ==, "scheme://userinfo@host:1234/path?query#fragment");
980   g_free (tostring);
981   g_uri_unref (uri);
982
983   uri = g_uri_build (G_URI_FLAGS_NONE, "scheme", NULL, "fe80::dead:beef%em1", -1, "", NULL, NULL);
984   tostring = g_uri_to_string (uri);
985   g_assert_cmpstr (tostring, ==, "scheme://[fe80::dead:beef%25em1]");
986   g_free (tostring);
987   g_uri_unref (uri);
988
989   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user", "pass", "auth", "host", 1234,
990                                "/path", "query", "fragment");
991   tostring = g_uri_to_string (uri);
992   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path?query#fragment");
993   g_free (tostring);
994   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_USERINFO);
995   g_assert_cmpstr (tostring, ==, "scheme://host:1234/path?query#fragment");
996   g_free (tostring);
997   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_QUERY);
998   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path#fragment");
999   g_free (tostring);
1000   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_FRAGMENT);
1001   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path?query");
1002   g_free (tostring);
1003   g_uri_unref (uri);
1004
1005   uri = g_uri_build_with_user (G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1006                                "scheme", "us:er", "pass", "auth", "host", 1234,
1007                                "/path", "query", "fragment");
1008   tostring = g_uri_to_string (uri);
1009   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass;auth@host:1234/path?query#fragment");
1010   g_free (tostring);
1011   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
1012   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer;auth@host:1234/path?query#fragment");
1013   g_free (tostring);
1014   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_AUTH_PARAMS);
1015   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass@host:1234/path?query#fragment");
1016   g_free (tostring);
1017   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_QUERY);
1018   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass;auth@host:1234/path#fragment");
1019   g_free (tostring);
1020   g_uri_unref (uri);
1021 }
1022
1023 static void
1024 test_uri_build (void)
1025 {
1026   GUri *uri;
1027
1028   uri = g_uri_build (G_URI_FLAGS_NON_DNS, "scheme", "userinfo", "host", 1234,
1029                      "/path", "query", "fragment");
1030
1031   /* check ref/unref */
1032   g_uri_ref (uri);
1033   g_uri_unref (uri);
1034
1035   g_assert_cmpint (g_uri_get_flags (uri), ==, G_URI_FLAGS_NON_DNS);
1036   g_assert_cmpstr (g_uri_get_scheme (uri), ==, "scheme");
1037   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "userinfo");
1038   g_assert_cmpstr (g_uri_get_host (uri), ==, "host");
1039   g_assert_cmpint (g_uri_get_port (uri), ==, 1234);
1040   g_assert_cmpstr (g_uri_get_path (uri), ==, "/path");
1041   g_assert_cmpstr (g_uri_get_query (uri), ==, "query");
1042   g_assert_cmpstr (g_uri_get_fragment (uri), ==, "fragment");
1043   g_assert_cmpstr (g_uri_get_user (uri), ==, NULL);
1044   g_assert_cmpstr (g_uri_get_password (uri), ==, NULL);
1045   g_uri_unref (uri);
1046
1047   uri = g_uri_build_with_user (G_URI_FLAGS_NON_DNS, "scheme", "user", "password",
1048                                "authparams", "host", 1234,
1049                                "/path", "query", "fragment");
1050
1051   g_assert_cmpint (g_uri_get_flags (uri), ==, G_URI_FLAGS_NON_DNS | G_URI_FLAGS_HAS_PASSWORD);
1052   g_assert_cmpstr (g_uri_get_scheme (uri), ==, "scheme");
1053   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user:password;authparams");
1054   g_assert_cmpstr (g_uri_get_host (uri), ==, "host");
1055   g_assert_cmpint (g_uri_get_port (uri), ==, 1234);
1056   g_assert_cmpstr (g_uri_get_path (uri), ==, "/path");
1057   g_assert_cmpstr (g_uri_get_query (uri), ==, "query");
1058   g_assert_cmpstr (g_uri_get_fragment (uri), ==, "fragment");
1059   g_assert_cmpstr (g_uri_get_user (uri), ==, "user");
1060   g_assert_cmpstr (g_uri_get_password (uri), ==, "password");
1061   g_assert_cmpstr (g_uri_get_auth_params (uri), ==, "authparams");
1062   g_uri_unref (uri);
1063
1064   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "password\002",
1065                                "authparams\003", "host", 1234,
1066                                "/path", "query", "fragment");
1067   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user\001:password\002;authparams\003");
1068   g_uri_unref (uri);
1069
1070   uri = g_uri_build_with_user (G_URI_FLAGS_ENCODED, "scheme", "user%01", "password%02",
1071                                "authparams%03", "host", 1234,
1072                                "/path", "query", "fragment");
1073   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user%01:password%02;authparams%03");
1074   g_uri_unref (uri);
1075
1076   uri = g_uri_build_with_user (G_URI_FLAGS_ENCODED, "scheme", NULL, NULL,
1077                                NULL, "host", 1234,
1078                                "/path", "query", "fragment");
1079   g_assert_null (g_uri_get_userinfo (uri));
1080   g_uri_unref (uri);
1081
1082   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user", NULL, NULL,
1083                                "host", 1234,
1084                                "/path", "query", "fragment");
1085   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user");
1086   g_uri_unref (uri);
1087 }
1088
1089 static void
1090 test_uri_split (void)
1091 {
1092   gchar *scheme = NULL;
1093   gchar *userinfo = NULL;
1094   gchar *user = NULL;
1095   gchar *pass = NULL;
1096   gchar *authparams = NULL;
1097   gchar *host = NULL;
1098   gchar *path = NULL;
1099   gchar *query = NULL;
1100   gchar *fragment = NULL;
1101   GError *error = NULL;
1102   gint port;
1103
1104   g_uri_split ("scheme://user%3Apass%3Bauth@host:1234/path?query#fragment",
1105                G_URI_FLAGS_NONE,
1106                &scheme,
1107                &userinfo,
1108                &host,
1109                &port,
1110                &path,
1111                &query,
1112                &fragment,
1113                &error);
1114   g_assert_no_error (error);
1115   g_assert_cmpstr (scheme, ==, "scheme");
1116   g_assert_cmpstr (userinfo, ==, "user:pass;auth");
1117   g_assert_cmpstr (host, ==, "host");
1118   g_assert_cmpint (port, ==, 1234);
1119   g_assert_cmpstr (path, ==, "/path");
1120   g_assert_cmpstr (query, ==, "query");
1121   g_assert_cmpstr (fragment, ==, "fragment");
1122   g_free (scheme);
1123   g_free (userinfo);
1124   g_free (host);
1125   g_free (path);
1126   g_free (query);
1127   g_free (fragment);
1128
1129   g_uri_split ("scheme://user%3Apass%3Bauth@h%01st:1234/path?query#fragment",
1130                G_URI_FLAGS_ENCODED,
1131                NULL,
1132                NULL,
1133                &host,
1134                NULL,
1135                NULL,
1136                NULL,
1137                NULL,
1138                &error);
1139   g_assert_no_error (error);
1140   g_assert_cmpstr (host, ==, "h\001st");
1141   g_free (host);
1142
1143   g_uri_split ("scheme://@@@host:1234/path?query#fragment",
1144                G_URI_FLAGS_ENCODED | G_URI_FLAGS_PARSE_RELAXED,
1145                NULL,
1146                &userinfo,
1147                NULL,
1148                NULL,
1149                NULL,
1150                NULL,
1151                NULL,
1152                &error);
1153   g_assert_no_error (error);
1154   g_assert_cmpstr (userinfo, ==, "@@");
1155   g_free (userinfo);
1156
1157
1158   g_uri_split ("http://f;oo/",
1159                G_URI_FLAGS_NONE | G_URI_FLAGS_PARSE_RELAXED,
1160                NULL,
1161                NULL,
1162                NULL,
1163                NULL,
1164                &path,
1165                NULL,
1166                NULL,
1167                &error);
1168   g_assert_no_error (error);
1169   g_assert_cmpstr (path, ==, ";oo/");
1170   g_free (path);
1171
1172   g_uri_split ("http://h%01st/path?saisons=%C3%89t%C3%A9%2Bhiver",
1173                G_URI_FLAGS_NONE,
1174                NULL,
1175                NULL,
1176                &host,
1177                NULL,
1178                NULL,
1179                &query,
1180                NULL,
1181                &error);
1182   g_assert_no_error (error);
1183   g_assert_cmpstr (host, ==, "h\001st");
1184   g_assert_cmpstr (query, ==, "saisons=Été+hiver");
1185   g_free (host);
1186   g_free (query);
1187
1188   g_uri_split ("http://h%01st/path?saisons=%C3%89t%C3%A9%2Bhiver",
1189                G_URI_FLAGS_ENCODED_QUERY,
1190                NULL,
1191                NULL,
1192                &host,
1193                NULL,
1194                NULL,
1195                &query,
1196                NULL,
1197                &error);
1198   g_assert_no_error (error);
1199   g_assert_cmpstr (host, ==, "h\001st");
1200   g_assert_cmpstr (query, ==, "saisons=%C3%89t%C3%A9%2Bhiver");
1201   g_free (host);
1202   g_free (query);
1203
1204   g_uri_split ("http://h%01st/%C3%89t%C3%A9%2Bhiver",
1205                G_URI_FLAGS_ENCODED_PATH,
1206                NULL,
1207                NULL,
1208                NULL,
1209                NULL,
1210                &path,
1211                NULL,
1212                NULL,
1213                &error);
1214   g_assert_no_error (error);
1215   g_assert_cmpstr (path, ==, "/%C3%89t%C3%A9%2Bhiver");
1216   g_free (path);
1217
1218   g_uri_split ("http://h%01st/path#%C3%89t%C3%A9%2Bhiver",
1219                G_URI_FLAGS_ENCODED_FRAGMENT,
1220                NULL,
1221                NULL,
1222                NULL,
1223                NULL,
1224                NULL,
1225                NULL,
1226                &fragment,
1227                &error);
1228   g_assert_no_error (error);
1229   g_assert_cmpstr (fragment, ==, "%C3%89t%C3%A9%2Bhiver");
1230   g_free (fragment);
1231
1232   g_uri_split_with_user ("scheme://user:pass;auth@host:1234/path?query#fragment",
1233                          G_URI_FLAGS_HAS_AUTH_PARAMS|G_URI_FLAGS_HAS_PASSWORD,
1234                          NULL,
1235                          &user,
1236                          &pass,
1237                          &authparams,
1238                          NULL,
1239                          NULL,
1240                          NULL,
1241                          NULL,
1242                          NULL,
1243                          &error);
1244   g_assert_no_error (error);
1245   g_assert_cmpstr (user, ==, "user");
1246   g_assert_cmpstr (pass, ==, "pass");
1247   g_assert_cmpstr (authparams, ==, "auth");
1248   g_free (user);
1249   g_free (pass);
1250   g_free (authparams);
1251
1252   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?query#fragment",
1253                        G_URI_FLAGS_NONE,
1254                        NULL,
1255                        NULL,
1256                        NULL,
1257                        &error);
1258   g_assert_no_error (error);
1259
1260   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?query#fragment",
1261                        G_URI_FLAGS_NONE,
1262                        &scheme,
1263                        &host,
1264                        &port,
1265                        &error);
1266   g_assert_no_error (error);
1267   g_assert_cmpstr (scheme, ==, "scheme");
1268   g_assert_cmpstr (host, ==, "host");
1269   g_assert_cmpint (port, ==, 1234);
1270   g_free (scheme);
1271   g_free (host);
1272
1273   g_uri_split_network ("%00",
1274                        G_URI_FLAGS_NONE, NULL, NULL, NULL, &error);
1275   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PATH);
1276   g_clear_error (&error);
1277
1278   g_uri_split_network ("/a",
1279                        G_URI_FLAGS_NONE,
1280                        &scheme,
1281                        &host,
1282                        &port,
1283                        &error);
1284   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_SCHEME);
1285   g_clear_error (&error);
1286
1287   g_uri_split_network ("schme:#",
1288                        G_URI_FLAGS_NONE,
1289                        &scheme,
1290                        &host,
1291                        &port,
1292                        &error);
1293   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1294   g_clear_error (&error);
1295
1296   g_uri_split_network ("scheme://[]/a",
1297                        G_URI_FLAGS_NONE, NULL, NULL, NULL, &error);
1298   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1299   g_clear_error (&error);
1300
1301   g_uri_split_network ("scheme://user%00:pass;auth@host",
1302                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1303                        NULL, NULL, NULL, &error);
1304   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_USER);
1305   g_clear_error (&error);
1306
1307   g_uri_split_network ("scheme://user:pass%00;auth@host",
1308                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1309                        NULL, NULL, NULL, &error);
1310   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1311   g_clear_error (&error);
1312
1313   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?quer%00y#fragment",
1314                        G_URI_FLAGS_NONE,
1315                        NULL, NULL, NULL, &error);
1316   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_QUERY);
1317   g_clear_error (&error);
1318
1319   g_uri_split_network ("scheme://use%00r:pass;auth@host:1234/path",
1320                        G_URI_FLAGS_NONE,
1321                        NULL, NULL, NULL, &error);
1322   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_USER);
1323   g_clear_error (&error);
1324
1325   g_uri_split ("scheme://user:pass;auth@host:1234/path?query#fragm%00ent",
1326                G_URI_FLAGS_NONE,
1327                &scheme,
1328                &userinfo,
1329                &host,
1330                &port,
1331                &path,
1332                &query,
1333                &fragment,
1334                &error);
1335   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_FRAGMENT);
1336   g_clear_error (&error);
1337
1338   g_uri_split_with_user ("scheme://user:pa%x0s;auth@host:1234/path?query#fragment",
1339                          G_URI_FLAGS_HAS_PASSWORD,
1340                          &scheme,
1341                          &user,
1342                          &pass,
1343                          &authparams,
1344                          &host,
1345                          &port,
1346                          &path,
1347                          &query,
1348                          &fragment,
1349                          &error);
1350   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1351   g_clear_error (&error);
1352
1353   g_uri_split_with_user ("scheme://user:pass;auth%00@host",
1354                          G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1355                          &scheme,
1356                          &user,
1357                          &pass,
1358                          &authparams,
1359                          &host,
1360                          &port,
1361                          &path,
1362                          &query,
1363                          &fragment,
1364                          &error);
1365   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_AUTH_PARAMS);
1366   g_clear_error (&error);
1367
1368   g_uri_split_network ("scheme://user:pass%00;auth@host",
1369                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1370                        NULL, NULL, NULL, &error);
1371   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1372   g_clear_error (&error);
1373
1374 }
1375
1376 static void
1377 test_uri_is_valid (void)
1378 {
1379   GError *error = NULL;
1380
1381   g_assert_true (g_uri_is_valid ("http://[::192.9.5.5]/ipng", G_URI_FLAGS_NONE, NULL));
1382   g_assert_true (g_uri_is_valid ("http://127.127.127.127/", G_URI_FLAGS_NONE, NULL));
1383   g_assert_true (g_uri_is_valid ("http://127.127.127.b/", G_URI_FLAGS_NONE, NULL));
1384   g_assert_true (g_uri_is_valid ("http://\xc3\x89XAMPLE.COM/", G_URI_FLAGS_NONE, NULL));
1385
1386   g_assert_true (g_uri_is_valid ("  \r http\t://f oo  \t\n ", G_URI_FLAGS_PARSE_RELAXED, NULL));
1387   g_assert_false (g_uri_is_valid ("  \r http\t://f oo  \t\n ", G_URI_FLAGS_NONE, &error));
1388   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_SCHEME);
1389   g_clear_error (&error);
1390
1391   g_assert_false (g_uri_is_valid ("http://[::192.9.5.5/ipng", G_URI_FLAGS_NONE, &error));
1392   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1393   g_clear_error (&error);
1394
1395   g_assert_true (g_uri_is_valid ("http://[fe80::dead:beef%25wef]/", G_URI_FLAGS_NONE, NULL));
1396   g_assert_false (g_uri_is_valid ("http://[fe80::dead:beef%wef%]/", G_URI_FLAGS_NONE, &error));
1397   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1398   g_clear_error (&error);
1399
1400   g_assert_false (g_uri_is_valid ("http://%00/", G_URI_FLAGS_NON_DNS, &error));
1401   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1402   g_clear_error (&error);
1403
1404   g_assert_true (g_uri_is_valid ("http://foo/", G_URI_FLAGS_NON_DNS, &error));
1405
1406   g_assert_false (g_uri_is_valid ("http://%00/", G_URI_FLAGS_NONE, &error));
1407   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1408   g_clear_error (&error);
1409
1410   g_assert_false (g_uri_is_valid ("http://%30.%30.%30.%30/", G_URI_FLAGS_NONE, &error));
1411   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1412   g_clear_error (&error);
1413
1414   g_assert_false (g_uri_is_valid ("http://host:port", G_URI_FLAGS_NONE, &error));
1415   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1416   g_clear_error (&error);
1417
1418   g_assert_false (g_uri_is_valid ("http://host:65536", G_URI_FLAGS_NONE, &error));
1419   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1420   g_clear_error (&error);
1421
1422   g_assert_false (g_uri_is_valid ("http://host:6553l", G_URI_FLAGS_NONE, &error));
1423   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1424   g_clear_error (&error);
1425
1426   g_assert_true (g_uri_is_valid ("data:,Hello", G_URI_FLAGS_NONE, &error));
1427 }
1428
1429 static const struct
1430 {
1431   /* Inputs */
1432   const gchar *uri;
1433   gchar *separators;
1434   GUriParamsFlags flags;
1435   /* Outputs */
1436   /* key, value, key, value, â€¦, limited to length 2*expected_n_params */
1437   gssize expected_n_iter;  /* -1 => error expected */
1438   const gchar *expected_iter_key_values[6];
1439   gssize expected_n_params;  /* -1 => error expected */
1440   const gchar *expected_param_key_values[6];
1441 } params_tests[] =
1442   {
1443     { "p1=foo&p2=bar;p3=baz", "&;", G_URI_PARAMS_NONE,
1444       3, { "p1", "foo", "p2", "bar", "p3", "baz" },
1445       3, { "p1", "foo", "p2", "bar", "p3", "baz" }},
1446     { "p1=foo&p2=bar", "", G_URI_PARAMS_NONE,
1447       1, { "p1", "foo&p2=bar" },
1448       1, { "p1", "foo&p2=bar" }},
1449     { "p1=foo&&P1=bar", "&", G_URI_PARAMS_NONE,
1450       1, { "p1", "foo" },
1451       -1, { NULL, }},
1452     { "%00=foo", "&", G_URI_PARAMS_NONE,
1453       0, { NULL, },
1454       -1, { NULL, }},
1455     { "p1=%00", "&", G_URI_PARAMS_NONE,
1456       0, { NULL, },
1457       -1, { NULL, }},
1458     { "p1=foo&p1=bar", "&", G_URI_PARAMS_NONE,
1459       2, { "p1", "foo", "p1", "bar" },
1460       1, { "p1", "bar", NULL, }},
1461     { "p1=foo&P1=bar", "&", G_URI_PARAMS_CASE_INSENSITIVE,
1462       2, { "p1", "foo", "P1", "bar" },
1463       1, { "p1", "bar", NULL, }},
1464     { "=%", "&", G_URI_PARAMS_PARSE_RELAXED,
1465       1, { "", "%", NULL, },
1466       1, { "", "%", NULL, }},
1467     { "=", "&", G_URI_PARAMS_NONE,
1468       1, { "", "", NULL, },
1469       1, { "", "", NULL, }},
1470     { "foo", "&", G_URI_PARAMS_NONE,
1471       0, { NULL, },
1472       -1, { NULL, }},
1473     { "foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver", "&", G_URI_PARAMS_WWW_FORM,
1474       2, { "foo", "bar & baz", "saisons", "Été+hiver", NULL, },
1475       2, { "foo", "bar & baz", "saisons", "Été+hiver", NULL, }},
1476     { "foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver", "&", G_URI_PARAMS_NONE,
1477       2, { "foo", "bar+&+baz", "saisons", "Été+hiver", NULL, },
1478       2, { "foo", "bar+&+baz", "saisons", "Été+hiver", NULL, }},
1479   };
1480
1481 static void
1482 test_uri_iter_params (gconstpointer test_data)
1483 {
1484   GError *err = NULL;
1485   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
1486   gsize i, n;
1487
1488   for (i = 0; i < G_N_ELEMENTS (params_tests); i++)
1489     {
1490       GUriParamsIter iter;
1491       gchar *uri, *attr, *value;
1492       gssize uri_len;
1493
1494       g_test_message ("URI %" G_GSIZE_FORMAT ": %s", i, params_tests[i].uri);
1495
1496       g_assert (params_tests[i].expected_n_params < 0 ||
1497                 params_tests[i].expected_n_params <= G_N_ELEMENTS (params_tests[i].expected_param_key_values) / 2);
1498
1499       /* The tests get run twice: once with the length unspecified, using a
1500        * nul-terminated string; and once with the length specified and a copy of
1501        * the string with the trailing nul explicitly removed (to help catch
1502        * buffer overflows). */
1503       if (use_nul_terminated)
1504         {
1505           uri_len = -1;
1506           uri = g_strdup (params_tests[i].uri);
1507         }
1508       else
1509         {
1510           uri_len = strlen (params_tests[i].uri);  /* no trailing nul */
1511           uri = g_memdup2 (params_tests[i].uri, uri_len);
1512         }
1513
1514       /* Run once without extracting the attr or value, just to check the numbers. */
1515       n = 0;
1516       g_uri_params_iter_init (&iter, params_tests[i].uri, -1, params_tests[i].separators, params_tests[i].flags);
1517       while (g_uri_params_iter_next (&iter, NULL, NULL, &err))
1518         n++;
1519       g_assert_cmpint (n, ==, params_tests[i].expected_n_iter);
1520       if (err)
1521         {
1522           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1523           g_clear_error (&err);
1524         }
1525
1526       /* Run again and check the strings too. */
1527       n = 0;
1528       g_uri_params_iter_init (&iter, params_tests[i].uri, -1, params_tests[i].separators, params_tests[i].flags);
1529       while (g_uri_params_iter_next (&iter, &attr, &value, &err))
1530         {
1531           g_assert_cmpstr (attr, ==, params_tests[i].expected_iter_key_values[n * 2]);
1532           g_assert_cmpstr (value, ==, params_tests[i].expected_iter_key_values[n * 2 + 1]);
1533           n++;
1534           g_free (attr);
1535           g_free (value);
1536         }
1537       g_assert_cmpint (n, ==, params_tests[i].expected_n_iter);
1538       if (err)
1539         {
1540           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1541           g_clear_error (&err);
1542         }
1543
1544       g_free (uri);
1545     }
1546 }
1547
1548 static void
1549 test_uri_parse_params (gconstpointer test_data)
1550 {
1551   GError *err = NULL;
1552   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
1553   gsize i;
1554
1555   for (i = 0; i < G_N_ELEMENTS (params_tests); i++)
1556     {
1557       GHashTable *params;
1558       gchar *uri = NULL;
1559       gssize uri_len;
1560
1561       g_test_message ("URI %" G_GSIZE_FORMAT ": %s", i, params_tests[i].uri);
1562
1563       g_assert (params_tests[i].expected_n_params < 0 ||
1564                 params_tests[i].expected_n_params <= G_N_ELEMENTS (params_tests[i].expected_param_key_values) / 2);
1565
1566       /* The tests get run twice: once with the length unspecified, using a
1567        * nul-terminated string; and once with the length specified and a copy of
1568        * the string with the trailing nul explicitly removed (to help catch
1569        * buffer overflows). */
1570       if (use_nul_terminated)
1571         {
1572           uri_len = -1;
1573           uri = g_strdup (params_tests[i].uri);
1574         }
1575       else
1576         {
1577           uri_len = strlen (params_tests[i].uri);  /* no trailing nul */
1578           uri = g_memdup2 (params_tests[i].uri, uri_len);
1579         }
1580
1581       params = g_uri_parse_params (uri, uri_len, params_tests[i].separators, params_tests[i].flags, &err);
1582
1583       if (params_tests[i].expected_n_params < 0)
1584         {
1585           g_assert_null (params);
1586           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1587           g_clear_error (&err);
1588         }
1589       else
1590         {
1591           gsize j;
1592
1593           g_assert_no_error (err);
1594           g_assert_cmpint (g_hash_table_size (params), ==, params_tests[i].expected_n_params);
1595
1596           for (j = 0; j < params_tests[i].expected_n_params; j += 2)
1597             g_assert_cmpstr (g_hash_table_lookup (params, params_tests[i].expected_param_key_values[j]), ==,
1598                              params_tests[i].expected_param_key_values[j + 1]);
1599         }
1600
1601       g_clear_pointer (&params, g_hash_table_unref);
1602       g_free (uri);
1603     }
1604 }
1605
1606 static void
1607 test_uri_join (void)
1608 {
1609   gchar *uri = NULL;
1610
1611   uri = g_uri_join (G_URI_FLAGS_NONE, "foo", "some:user@info", "bar", -1, "", NULL, NULL);
1612   g_assert_cmpstr (uri, ==, "foo://some:user%40info@bar");
1613   g_free (uri);
1614
1615   uri = g_uri_join (G_URI_FLAGS_NONE, NULL, NULL, NULL, -1, "/foo", "abc", NULL);
1616   g_assert_cmpstr (uri, ==, "/foo?abc");
1617   g_free (uri);
1618
1619   uri = g_uri_join (G_URI_FLAGS_NONE, NULL, NULL, "hostname", -1, "/foo", "abc", NULL);
1620   g_assert_cmpstr (uri, ==, "//hostname/foo?abc");
1621   g_free (uri);
1622
1623   uri = g_uri_join_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "pass\002", "authparams\003",
1624                               "host", 9876, "/path", "query", "fragment");
1625   g_assert_cmpstr (uri, ==, "scheme://user%01:pass%02;authparams%03@host:9876/path?query#fragment");
1626   g_free (uri);
1627
1628   uri = g_uri_join_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "pass\002", "authparams\003",
1629                               "::192.9.5.5", 9876, "/path", "query", "fragment");
1630   g_assert_cmpstr (uri, ==, "scheme://user%01:pass%02;authparams%03@[::192.9.5.5]:9876/path?query#fragment");
1631   g_free (uri);
1632
1633   uri = g_uri_join_with_user (G_URI_FLAGS_ENCODED,
1634                               "scheme", "user%01", "pass%02", "authparams%03",
1635                               "::192.9.5.5", 9876, "/path", "query", "fragment");
1636   g_assert_cmpstr (uri, ==,
1637                    "scheme://user%01:pass%02;authparams%03@[::192.9.5.5]:9876/path?query#fragment");
1638   g_free (uri);
1639
1640   uri = g_uri_join (G_URI_FLAGS_NONE, "scheme", NULL, "foo:bar._webdav._tcp.local", -1, "", NULL, NULL);
1641   g_assert_cmpstr (uri, ==, "scheme://foo%3Abar._webdav._tcp.local");
1642   g_free (uri);
1643 }
1644
1645 static void
1646 test_uri_join_split_round_trip (void)
1647 {
1648   GUriFlags flags = G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_HAS_AUTH_PARAMS;
1649   guint i;
1650
1651   g_test_summary ("Test that joining different URI components survives a round trip");
1652
1653   /* Each bit in @i indicates whether the corresponding URI field should be set
1654    * or %NULL. */
1655   for (i = 0; i < (1 << 8); i++)
1656     {
1657       gchar *uri = NULL;
1658       const gchar *scheme, *user, *password, *auth_params, *host, *path, *query, *fragment;
1659       gint port;
1660       gchar *scheme_out = NULL, *user_out = NULL, *password_out = NULL;
1661       gchar *auth_params_out = NULL, *host_out = NULL, *path_out = NULL;
1662       gchar *query_out = NULL, *fragment_out = NULL;
1663       gint port_out = -1;
1664       gboolean split_success;
1665       GError *local_error = NULL;
1666
1667       g_test_message ("Combination %u", i);
1668
1669       scheme = (i & (1 << 8)) ? "scheme" : NULL;
1670       host = (i & (1 << 4)) ? "host" : NULL;
1671       user = (host != NULL && i & (1 << 7)) ? "user" : NULL;  /* only supported if host is also set */
1672       password = (host != NULL && user != NULL && i & (1 << 6)) ? "password" : NULL;  /* only supported if host and user are also set */
1673       auth_params = (host != NULL && user != NULL && i & (1 << 5)) ? "auth_params" : NULL;  /* only supported if host and user are also set */
1674       port = (host != NULL && i & (1 << 3)) ? 123 : -1;  /* only supported if host is also set */
1675       path = (i & (1 << 2)) ? "/path" : "";  /* the only mandatory component */
1676       query = (i & (1 << 1)) ? "query" : NULL;
1677       fragment = (i & (1 << 0)) ? "fragment" : NULL;
1678
1679       uri = g_uri_join_with_user (flags, scheme, user, password, auth_params,
1680                                   host, port, path, query, fragment);
1681       g_assert_nonnull (uri);
1682
1683       split_success = g_uri_split_with_user (uri, flags, &scheme_out, &user_out,
1684                                              &password_out, &auth_params_out,
1685                                              &host_out, &port_out, &path_out,
1686                                              &query_out, &fragment_out,
1687                                              &local_error);
1688       g_assert_no_error (local_error);
1689       g_assert_true (split_success);
1690
1691       g_assert_cmpstr (scheme, ==, scheme_out);
1692       g_assert_cmpstr (user, ==, user_out);
1693       g_assert_cmpstr (password, ==, password_out);
1694       g_assert_cmpstr (auth_params, ==, auth_params_out);
1695       g_assert_cmpstr (host, ==, host_out);
1696       g_assert_cmpint (port, ==, port_out);
1697       g_assert_cmpstr (path, ==, path_out);
1698       g_assert_cmpstr (query, ==, query_out);
1699       g_assert_cmpstr (fragment, ==, fragment_out);
1700
1701       g_free (uri);
1702       g_free (scheme_out);
1703       g_free (user_out);
1704       g_free (password_out);
1705       g_free (auth_params_out);
1706       g_free (host_out);
1707       g_free (path_out);
1708       g_free (query_out);
1709       g_free (fragment_out);
1710     }
1711 }
1712
1713 int
1714 main (int   argc,
1715       char *argv[])
1716 {
1717   g_test_init (&argc, &argv, NULL);
1718
1719   g_test_add_func ("/uri/file-to-uri", run_file_to_uri_tests);
1720   g_test_add_func ("/uri/file-from-uri", run_file_from_uri_tests);
1721   g_test_add_func ("/uri/file-roundtrip", run_file_roundtrip_tests);
1722   g_test_add_func ("/uri/list", run_uri_list_tests);
1723   g_test_add_func ("/uri/unescape-string", test_uri_unescape_string);
1724   g_test_add_data_func ("/uri/unescape-bytes/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_unescape_bytes);
1725   g_test_add_data_func ("/uri/unescape-bytes/length", GINT_TO_POINTER (FALSE), test_uri_unescape_bytes);
1726   g_test_add_func ("/uri/unescape-segment", test_uri_unescape_segment);
1727   g_test_add_func ("/uri/escape-string", test_uri_escape_string);
1728   g_test_add_func ("/uri/escape-bytes", test_uri_escape_bytes);
1729   g_test_add_func ("/uri/scheme", test_uri_scheme);
1730   g_test_add_func ("/uri/parsing/absolute", test_uri_parsing_absolute);
1731   g_test_add_func ("/uri/parsing/relative", test_uri_parsing_relative);
1732   g_test_add_func ("/uri/build", test_uri_build);
1733   g_test_add_func ("/uri/split", test_uri_split);
1734   g_test_add_func ("/uri/is_valid", test_uri_is_valid);
1735   g_test_add_func ("/uri/to-string", test_uri_to_string);
1736   g_test_add_func ("/uri/join", test_uri_join);
1737   g_test_add_func ("/uri/join-split-round-trip", test_uri_join_split_round_trip);
1738   g_test_add_data_func ("/uri/iter-params/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_iter_params);
1739   g_test_add_data_func ("/uri/iter-params/length", GINT_TO_POINTER (FALSE), test_uri_iter_params);
1740   g_test_add_data_func ("/uri/parse-params/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_parse_params);
1741   g_test_add_data_func ("/uri/parse-params/length", GINT_TO_POINTER (FALSE), test_uri_parse_params);
1742
1743   return g_test_run ();
1744 }