1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 /*
26  * Note:
27  *
28  * Since the URL parser by default only accepts schemes that *this instance*
29  * of libcurl supports, make sure that the test1560 file lists all the schemes
30  * that this test will assume to be present!
31  */
32 
33 #include "test.h"
34 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
35 #define USE_IDN
36 #endif
37 
38 #include "testutil.h"
39 #include "warnless.h"
40 #include "memdebug.h" /* LAST include file */
41 
42 struct part {
43   CURLUPart part;
44   const char *name;
45 };
46 
47 
checkparts(CURLU *u, const char *in, const char *wanted, unsigned int getflags)48 static int checkparts(CURLU *u, const char *in, const char *wanted,
49                       unsigned int getflags)
50 {
51   int i;
52   CURLUcode rc;
53   char buf[256];
54   char *bufp = &buf[0];
55   size_t len = sizeof(buf);
56   struct part parts[] = {
57     {CURLUPART_SCHEME, "scheme"},
58     {CURLUPART_USER, "user"},
59     {CURLUPART_PASSWORD, "password"},
60     {CURLUPART_OPTIONS, "options"},
61     {CURLUPART_HOST, "host"},
62     {CURLUPART_PORT, "port"},
63     {CURLUPART_PATH, "path"},
64     {CURLUPART_QUERY, "query"},
65     {CURLUPART_FRAGMENT, "fragment"},
66     {CURLUPART_URL, NULL}
67   };
68   memset(buf, 0, sizeof(buf));
69 
70   for(i = 0; parts[i].name; i++) {
71     char *p = NULL;
72     size_t n;
73     rc = curl_url_get(u, parts[i].part, &p, getflags);
74     if(!rc && p) {
75       msnprintf(bufp, len, "%s%s", buf[0]?" | ":"", p);
76     }
77     else
78       msnprintf(bufp, len, "%s[%d]", buf[0]?" | ":"", (int)rc);
79 
80     n = strlen(bufp);
81     bufp += n;
82     len -= n;
83     curl_free(p);
84   }
85   if(strcmp(buf, wanted)) {
86     fprintf(stderr, "in: %s\nwanted: %s\ngot:    %s\n", in, wanted, buf);
87     return 1;
88   }
89   return 0;
90 }
91 
92 struct redircase {
93   const char *in;
94   const char *set;
95   const char *out;
96   unsigned int urlflags;
97   unsigned int setflags;
98   CURLUcode ucode;
99 };
100 
101 struct setcase {
102   const char *in;
103   const char *set;
104   const char *out;
105   unsigned int urlflags;
106   unsigned int setflags;
107   CURLUcode ucode; /* for the main URL set */
108   CURLUcode pcode; /* for updating parts */
109 };
110 
111 struct setgetcase {
112   const char *in;
113   const char *set;
114   const char *out;
115   unsigned int urlflags; /* for setting the URL */
116   unsigned int setflags; /* for updating parts */
117   unsigned int getflags; /* for getting parts */
118   CURLUcode pcode; /* for updating parts */
119 };
120 
121 struct testcase {
122   const char *in;
123   const char *out;
124   unsigned int urlflags;
125   unsigned int getflags;
126   CURLUcode ucode;
127 };
128 
129 struct urltestcase {
130   const char *in;
131   const char *out;
132   unsigned int urlflags; /* pass to curl_url() */
133   unsigned int getflags; /* pass to curl_url_get() */
134   CURLUcode ucode;
135 };
136 
137 struct querycase {
138   const char *in;
139   const char *q;
140   const char *out;
141   unsigned int urlflags; /* pass to curl_url() */
142   unsigned int qflags; /* pass to curl_url_get() */
143   CURLUcode ucode;
144 };
145 
146 struct clearurlcase {
147   CURLUPart part;
148   const char *in;
149   const char *out;
150   CURLUcode ucode;
151 };
152 
153 static const struct testcase get_parts_list[] ={
154   {"https://curl.se/#  ",
155    "https | [11] | [12] | [13] | curl.se | [15] | / | [16] | %20%20",
156    CURLU_URLENCODE|CURLU_ALLOW_SPACE, 0, CURLUE_OK},
157   {"", "", 0, 0, CURLUE_MALFORMED_INPUT},
158   {" ", "", 0, 0, CURLUE_MALFORMED_INPUT},
159   {"1h://example.net", "", 0, 0, CURLUE_BAD_SCHEME},
160   {"..://example.net", "", 0, 0, CURLUE_BAD_SCHEME},
161   {"-ht://example.net", "", 0, 0, CURLUE_BAD_SCHEME},
162   {"+ftp://example.net", "", 0, 0, CURLUE_BAD_SCHEME},
163   {"hej.hej://example.net",
164    "hej.hej | [11] | [12] | [13] | example.net | [15] | / | [16] | [17]",
165    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
166   {"ht-tp://example.net",
167    "ht-tp | [11] | [12] | [13] | example.net | [15] | / | [16] | [17]",
168    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
169   {"ftp+more://example.net",
170    "ftp+more | [11] | [12] | [13] | example.net | [15] | / | [16] | [17]",
171    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
172   {"f1337://example.net",
173    "f1337 | [11] | [12] | [13] | example.net | [15] | / | [16] | [17]",
174    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
175   {"https://user@example.net?hello# space ",
176    "https | user | [12] | [13] | example.net | [15] | / | hello | %20space%20",
177    CURLU_ALLOW_SPACE|CURLU_URLENCODE, 0, CURLUE_OK},
178   {"https://test%test", "", 0, 0, CURLUE_BAD_HOSTNAME},
179   {"https://example.com%252f%40@example.net",
180    "https | example.com%2f@ | [12] | [13] | example.net | [15] | / "
181    "| [16] | [17]",
182    0, CURLU_URLDECODE, CURLUE_OK },
183 #ifdef USE_IDN
184   {"https://räksmörgås.se",
185    "https | [11] | [12] | [13] | xn--rksmrgs-5wao1o.se | "
186    "[15] | / | [16] | [17]", 0, CURLU_PUNYCODE, CURLUE_OK},
187   {"https://xn--rksmrgs-5wao1o.se",
188    "https | [11] | [12] | [13] | räksmörgås.se | "
189    "[15] | / | [16] | [17]", 0, CURLU_PUNY2IDN, CURLUE_OK},
190 #else
191   {"https://räksmörgås.se",
192    "https | [11] | [12] | [13] | [30] | [15] | / | [16] | [17]",
193    0, CURLU_PUNYCODE, CURLUE_OK},
194 #endif
195   /* https://ℂᵤⓇℒ。�� */
196   {"https://"
197    "%e2%84%82%e1%b5%a4%e2%93%87%e2%84%92%e3%80%82%f0%9d%90%92%f0%9f%84%b4",
198    "https | [11] | [12] | [13] | ℂᵤⓇℒ。�� | [15] |"
199    " / | [16] | [17]",
200    0, 0, CURLUE_OK},
201   {"https://"
202    "%e2%84%82%e1%b5%a4%e2%93%87%e2%84%92%e3%80%82%f0%9d%90%92%f0%9f%84%b4",
203    "https | [11] | [12] | [13] | "
204    "%e2%84%82%e1%b5%a4%e2%93%87%e2%84%92%e3%80%82%f0%9d%90%92%f0%9f%84%b4 "
205    "| [15] | / | [16] | [17]",
206    0, CURLU_URLENCODE, CURLUE_OK},
207   {"https://"
208    "\xe2\x84\x82\xe1\xb5\xa4\xe2\x93\x87\xe2\x84\x92"
209    "\xe3\x80\x82\xf0\x9d\x90\x92\xf0\x9f\x84\xb4",
210    "https | [11] | [12] | [13] | "
211    "%e2%84%82%e1%b5%a4%e2%93%87%e2%84%92%e3%80%82%f0%9d%90%92%f0%9f%84%b4 "
212    "| [15] | / | [16] | [17]",
213    0, CURLU_URLENCODE, CURLUE_OK},
214   {"https://user@example.net?he l lo",
215    "https | user | [12] | [13] | example.net | [15] | / | he+l+lo | [17]",
216    CURLU_ALLOW_SPACE, CURLU_URLENCODE, CURLUE_OK},
217   {"https://user@example.net?he l lo",
218    "https | user | [12] | [13] | example.net | [15] | / | he l lo | [17]",
219    CURLU_ALLOW_SPACE, 0, CURLUE_OK},
220   {"https://exam{}[]ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
221   {"https://exam{ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
222   {"https://exam}ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
223   {"https://exam]ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
224   {"https://exam\\ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
225   {"https://exam$ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
226   {"https://exam'ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
227   {"https://exam\"ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
228   {"https://exam^ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
229   {"https://exam`ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
230   {"https://exam*ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
231   {"https://exam<ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
232   {"https://exam>ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
233   {"https://exam=ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
234   {"https://exam;ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME},
235   {"https://example,net", "", 0, 0, CURLUE_BAD_HOSTNAME},
236   {"https://example&net", "", 0, 0, CURLUE_BAD_HOSTNAME},
237   {"https://example+net", "", 0, 0, CURLUE_BAD_HOSTNAME},
238   {"https://example(net", "", 0, 0, CURLUE_BAD_HOSTNAME},
239   {"https://example)net", "", 0, 0, CURLUE_BAD_HOSTNAME},
240   {"https://example.net/}",
241    "https | [11] | [12] | [13] | example.net | [15] | /} | [16] | [17]",
242    0, 0, CURLUE_OK},
243 
244   /* blank user is blank */
245   {"https://:password@example.net",
246    "https |  | password | [13] | example.net | [15] | / | [16] | [17]",
247    0, 0, CURLUE_OK},
248   /* blank user + blank password */
249   {"https://:@example.net",
250    "https |  |  | [13] | example.net | [15] | / | [16] | [17]",
251    0, 0, CURLUE_OK},
252   /* user-only (no password) */
253   {"https://user@example.net",
254    "https | user | [12] | [13] | example.net | [15] | / | [16] | [17]",
255    0, 0, CURLUE_OK},
256 #ifdef USE_WEBSOCKETS
257   {"ws://example.com/color/?green",
258    "ws | [11] | [12] | [13] | example.com | [15] | /color/ | green |"
259    " [17]",
260    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
261   {"wss://example.com/color/?green",
262    "wss | [11] | [12] | [13] | example.com | [15] | /color/ | green |"
263    " [17]",
264    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
265 #endif
266 
267   {"https://user:password@example.net/get?this=and#but frag then", "",
268    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
269   {"https://user:password@example.net/get?this=and what", "",
270    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
271   {"https://user:password@example.net/ge t?this=and-what", "",
272    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
273   {"https://user:pass word@example.net/get?this=and-what", "",
274    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
275   {"https://u ser:password@example.net/get?this=and-what", "",
276    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
277   {"imap://user:pass;opt ion@server/path", "",
278    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
279   /* no space allowed in scheme */
280   {"htt ps://user:password@example.net/get?this=and-what", "",
281    CURLU_NON_SUPPORT_SCHEME|CURLU_ALLOW_SPACE, 0, CURLUE_BAD_SCHEME},
282   {"https://user:password@example.net/get?this=and what",
283    "https | user | password | [13] | example.net | [15] | /get | "
284    "this=and what | [17]",
285    CURLU_ALLOW_SPACE, 0, CURLUE_OK},
286   {"https://user:password@example.net/ge t?this=and-what",
287    "https | user | password | [13] | example.net | [15] | /ge t | "
288    "this=and-what | [17]",
289    CURLU_ALLOW_SPACE, 0, CURLUE_OK},
290   {"https://user:pass word@example.net/get?this=and-what",
291    "https | user | pass word | [13] | example.net | [15] | /get | "
292    "this=and-what | [17]",
293    CURLU_ALLOW_SPACE, 0, CURLUE_OK},
294   {"https://u ser:password@example.net/get?this=and-what",
295    "https | u ser | password | [13] | example.net | [15] | /get | "
296    "this=and-what | [17]",
297    CURLU_ALLOW_SPACE, 0, CURLUE_OK},
298   {"https://user:password@example.net/ge t?this=and-what",
299    "https | user | password | [13] | example.net | [15] | /ge%20t | "
300    "this=and-what | [17]",
301    CURLU_ALLOW_SPACE | CURLU_URLENCODE, 0, CURLUE_OK},
302   {"[0:0:0:0:0:0:0:1]",
303    "http | [11] | [12] | [13] | [::1] | [15] | / | [16] | [17]",
304    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
305   {"[::1]",
306    "http | [11] | [12] | [13] | [::1] | [15] | / | [16] | [17]",
307    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
308   {"[::]",
309    "http | [11] | [12] | [13] | [::] | [15] | / | [16] | [17]",
310    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
311   {"https://[::1]",
312    "https | [11] | [12] | [13] | [::1] | [15] | / | [16] | [17]",
313    0, 0, CURLUE_OK },
314   {"user:moo@ftp.example.com/color/#green?no-red",
315    "ftp | user | moo | [13] | ftp.example.com | [15] | /color/ | [16] | "
316    "green?no-red",
317    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
318   {"ftp.user:moo@example.com/color/#green?no-red",
319    "http | ftp.user | moo | [13] | example.com | [15] | /color/ | [16] | "
320    "green?no-red",
321    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
322 #ifdef _WIN32
323   {"file:/C:\\programs\\foo",
324    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
325    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
326   {"file://C:\\programs\\foo",
327    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
328    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
329   {"file:///C:\\programs\\foo",
330    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
331    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
332   {"file://host.example.com/Share/path/to/file.txt",
333    "file | [11] | [12] | [13] | host.example.com | [15] | "
334    "//host.example.com/Share/path/to/file.txt | [16] | [17]",
335    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
336 #endif
337   {"https://example.com/color/#green?no-red",
338    "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "
339    "green?no-red",
340    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
341   {"https://example.com/color/#green#no-red",
342    "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "
343    "green#no-red",
344    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
345   {"https://example.com/color/?green#no-red",
346    "https | [11] | [12] | [13] | example.com | [15] | /color/ | green | "
347    "no-red",
348    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
349   {"https://example.com/#color/?green#no-red",
350    "https | [11] | [12] | [13] | example.com | [15] | / | [16] | "
351    "color/?green#no-red",
352    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
353   {"https://example.#com/color/?green#no-red",
354    "https | [11] | [12] | [13] | example. | [15] | / | [16] | "
355    "com/color/?green#no-red",
356    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
357   {"http://[ab.be:1]/x", "",
358    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_IPV6},
359   {"http://[ab.be]/x", "",
360    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_IPV6},
361   /* URL without host name */
362   {"http://a:b@/x", "",
363    CURLU_DEFAULT_SCHEME, 0, CURLUE_NO_HOST},
364   {"boing:80",
365    "https | [11] | [12] | [13] | boing | 80 | / | [16] | [17]",
366    CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME, 0, CURLUE_OK},
367   {"http://[fd00:a41::50]:8080",
368    "http | [11] | [12] | [13] | [fd00:a41::50] | 8080 | / | [16] | [17]",
369    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
370   {"http://[fd00:a41::50]/",
371    "http | [11] | [12] | [13] | [fd00:a41::50] | [15] | / | [16] | [17]",
372    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
373   {"http://[fd00:a41::50]",
374    "http | [11] | [12] | [13] | [fd00:a41::50] | [15] | / | [16] | [17]",
375    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
376   {"https://[::1%252]:1234",
377    "https | [11] | [12] | [13] | [::1] | 1234 | / | [16] | [17]",
378    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
379 
380   /* here's "bad" zone id */
381   {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
382    "https | [11] | [12] | [13] | [fe80::20c:29ff:fe9c:409b] | 1234 "
383    "| / | [16] | [17]",
384    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
385   {"https://127.0.0.1:443",
386    "https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]",
387    0, CURLU_NO_DEFAULT_PORT, CURLUE_OK},
388   {"http://%3a:%3a@ex4mple/%3f+?+%3f+%23#+%23%3f%g7",
389    "http | : | : | [13] | ex4mple | [15] | /?+ |  ? # | +#?%g7",
390    0, CURLU_URLDECODE, CURLUE_OK},
391   {"http://%3a:%3a@ex4mple/%3f?%3f%35#%35%3f%g7",
392    "http | %3a | %3a | [13] | ex4mple | [15] | /%3f | %3f%35 | %35%3f%g7",
393    0, 0, CURLUE_OK},
394   {"http://HO0_-st%41/",
395    "http | [11] | [12] | [13] | HO0_-stA | [15] | / | [16] | [17]",
396    0, 0, CURLUE_OK},
397   {"file://hello.html",
398    "",
399    0, 0, CURLUE_BAD_FILE_URL},
400   {"http://HO0_-st/",
401    "http | [11] | [12] | [13] | HO0_-st | [15] | / | [16] | [17]",
402    0, 0, CURLUE_OK},
403   {"imap://user:pass;option@server/path",
404    "imap | user | pass | option | server | [15] | /path | [16] | [17]",
405    0, 0, CURLUE_OK},
406   {"http://user:pass;option@server/path",
407    "http | user | pass;option | [13] | server | [15] | /path | [16] | [17]",
408    0, 0, CURLUE_OK},
409   {"file:/hello.html",
410    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
411    0, 0, CURLUE_OK},
412   {"file:/h",
413    "file | [11] | [12] | [13] | [14] | [15] | /h | [16] | [17]",
414    0, 0, CURLUE_OK},
415   {"file:/",
416    "file | [11] | [12] | [13] | [14] | [15] | | [16] | [17]",
417    0, 0, CURLUE_BAD_FILE_URL},
418   {"file://127.0.0.1/hello.html",
419    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
420    0, 0, CURLUE_OK},
421   {"file:////hello.html",
422    "file | [11] | [12] | [13] | [14] | [15] | //hello.html | [16] | [17]",
423    0, 0, CURLUE_OK},
424   {"file:///hello.html",
425    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
426    0, 0, CURLUE_OK},
427   {"https://127.0.0.1",
428    "https | [11] | [12] | [13] | 127.0.0.1 | 443 | / | [16] | [17]",
429    0, CURLU_DEFAULT_PORT, CURLUE_OK},
430   {"https://127.0.0.1",
431    "https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]",
432    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
433   {"https://[::1]:1234",
434    "https | [11] | [12] | [13] | [::1] | 1234 | / | [16] | [17]",
435    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
436   {"https://127abc.com",
437    "https | [11] | [12] | [13] | 127abc.com | [15] | / | [16] | [17]",
438    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
439   {"https:// example.com?check", "",
440    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
441   {"https://e x a m p l e.com?check", "",
442    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
443   {"https://example.com?check",
444    "https | [11] | [12] | [13] | example.com | [15] | / | check | [17]",
445    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
446   {"https://example.com:65536",
447    "",
448    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_PORT_NUMBER},
449   {"https://example.com:-1#moo",
450    "",
451    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_PORT_NUMBER},
452   {"https://example.com:0#moo",
453    "https | [11] | [12] | [13] | example.com | 0 | / | "
454    "[16] | moo",
455    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
456   {"https://example.com:01#moo",
457    "https | [11] | [12] | [13] | example.com | 1 | / | "
458    "[16] | moo",
459    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
460   {"https://example.com:1#moo",
461    "https | [11] | [12] | [13] | example.com | 1 | / | "
462    "[16] | moo",
463    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
464   {"http://example.com#moo",
465    "http | [11] | [12] | [13] | example.com | [15] | / | "
466    "[16] | moo",
467    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
468   {"http://example.com",
469    "http | [11] | [12] | [13] | example.com | [15] | / | "
470    "[16] | [17]",
471    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
472   {"http://example.com/path/html",
473    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
474    "[16] | [17]",
475    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
476   {"http://example.com/path/html?query=name",
477    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
478    "query=name | [17]",
479    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
480   {"http://example.com/path/html?query=name#anchor",
481    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
482    "query=name | anchor",
483    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
484   {"http://example.com:1234/path/html?query=name#anchor",
485    "http | [11] | [12] | [13] | example.com | 1234 | /path/html | "
486    "query=name | anchor",
487    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
488   {"http:///user:password@example.com:1234/path/html?query=name#anchor",
489    "http | user | password | [13] | example.com | 1234 | /path/html | "
490    "query=name | anchor",
491    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
492   {"https://user:password@example.com:1234/path/html?query=name#anchor",
493    "https | user | password | [13] | example.com | 1234 | /path/html | "
494    "query=name | anchor",
495    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
496   {"http://user:password@example.com:1234/path/html?query=name#anchor",
497    "http | user | password | [13] | example.com | 1234 | /path/html | "
498    "query=name | anchor",
499    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
500   {"http:/user:password@example.com:1234/path/html?query=name#anchor",
501    "http | user | password | [13] | example.com | 1234 | /path/html | "
502    "query=name | anchor",
503    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
504   {"http:////user:password@example.com:1234/path/html?query=name#anchor",
505    "",
506    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_SLASHES},
507   {NULL, NULL, 0, 0, CURLUE_OK},
508 };
509 
510 static const struct urltestcase get_url_list[] = {
511   {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_OK},
512   {"https://0x7f.1", "https://127.0.0.1/", 0, 0, CURLUE_OK},
513   {"https://1.2.3.256.com", "https://1.2.3.256.com/", 0, 0, CURLUE_OK},
514   {"https://10.com", "https://10.com/", 0, 0, CURLUE_OK},
515   {"https://1.2.com", "https://1.2.com/", 0, 0, CURLUE_OK},
516   {"https://1.2.3.com", "https://1.2.3.com/", 0, 0, CURLUE_OK},
517   {"https://1.2.com.99", "https://1.2.com.99/", 0, 0, CURLUE_OK},
518   {"https://[fe80::0000:20c:29ff:fe9c:409b]:80/moo",
519    "https://[fe80::20c:29ff:fe9c:409b]:80/moo",
520    0, 0, CURLUE_OK},
521   {"https://[fe80::020c:29ff:fe9c:409b]:80/moo",
522    "https://[fe80::20c:29ff:fe9c:409b]:80/moo",
523    0, 0, CURLUE_OK},
524   {"https://[fe80:0000:0000:0000:020c:29ff:fe9c:409b]:80/moo",
525    "https://[fe80::20c:29ff:fe9c:409b]:80/moo",
526    0, 0, CURLUE_OK},
527   {"https://[fe80:0:0:0:409b::]:80/moo",
528    "https://[fe80::409b:0:0:0]:80/moo",
529    0, 0, CURLUE_OK},
530   {"https://[::%25fakeit];80/moo",
531    "",
532    0, 0, CURLUE_BAD_PORT_NUMBER},
533   {"https://[fe80::20c:29ff:fe9c:409b]-80/moo",
534    "",
535    0, 0, CURLUE_BAD_PORT_NUMBER},
536 #ifdef USE_IDN
537   {"https://räksmörgås.se/path?q#frag",
538    "https://xn--rksmrgs-5wao1o.se/path?q#frag", 0, CURLU_PUNYCODE, CURLUE_OK},
539 #endif
540   /* unsupported schemes with no guessing enabled */
541   {"data:text/html;charset=utf-8;base64,PCFET0NUWVBFIEhUTUw+PG1ldGEgY",
542    "", 0, 0, CURLUE_UNSUPPORTED_SCHEME},
543   {"d:anything-really", "", 0, 0, CURLUE_UNSUPPORTED_SCHEME},
544   {"about:config", "", 0, 0, CURLUE_UNSUPPORTED_SCHEME},
545   {"example://foo", "", 0, 0, CURLUE_UNSUPPORTED_SCHEME},
546   {"mailto:infobot@example.com?body=send%20current-issue", "", 0, 0,
547    CURLUE_UNSUPPORTED_SCHEME},
548   {"about:80", "https://about:80/", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
549   /* percent encoded host names */
550   {"http://example.com%40127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
551   {"http://example.com%21127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
552   {"http://example.com%3f127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
553   {"http://example.com%23127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
554   {"http://example.com%3a127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
555   {"http://example.com%09127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
556   {"http://example.com%2F127.0.0.1/", "", 0, 0, CURLUE_BAD_HOSTNAME},
557   {"https://%41", "https://A/", 0, 0, CURLUE_OK},
558   {"https://%20", "", 0, 0, CURLUE_BAD_HOSTNAME},
559   {"https://%41%0d", "", 0, 0, CURLUE_BAD_HOSTNAME},
560   {"https://%25", "", 0, 0, CURLUE_BAD_HOSTNAME},
561   {"https://_%c0_", "https://_\xC0_/", 0, 0, CURLUE_OK},
562   {"https://_%c0_", "https://_%C0_/", 0, CURLU_URLENCODE, CURLUE_OK},
563 
564   /* IPv4 trickeries */
565   {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK},
566   {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK},
567   {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK},
568   {"https://0111.02.0x3.", "https://0111.02.0x3./", 0, 0, CURLUE_OK},
569   {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK},
570   {"https://0111.02.030.", "https://0111.02.030./", 0, 0, CURLUE_OK},
571   {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK},
572   {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK},
573   /* IPv4 numerical overflows or syntax errors will not normalize */
574   {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_OK},
575   {"https://\xff.127.0.0.1", "https://%FF.127.0.0.1/", 0, CURLU_URLENCODE,
576    CURLUE_OK},
577   {"https://127.-0.0.1", "https://127.-0.0.1/", 0, 0, CURLUE_OK},
578   {"https://127.0. 1", "https://127.0.0.1/", 0, 0, CURLUE_MALFORMED_INPUT},
579   {"https://1.2.3.256", "https://1.2.3.256/", 0, 0, CURLUE_OK},
580   {"https://1.2.3.256.", "https://1.2.3.256./", 0, 0, CURLUE_OK},
581   {"https://1.2.3.4.5", "https://1.2.3.4.5/", 0, 0, CURLUE_OK},
582   {"https://1.2.0x100.3", "https://1.2.0x100.3/", 0, 0, CURLUE_OK},
583   {"https://4294967296", "https://4294967296/", 0, 0, CURLUE_OK},
584   {"https://123host", "https://123host/", 0, 0, CURLUE_OK},
585   /* 40 bytes scheme is the max allowed */
586   {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA://hostname/path",
587    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa://hostname/path",
588    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
589   /* 41 bytes scheme is not allowed */
590   {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA://hostname/path",
591    "",
592    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_BAD_SCHEME},
593   {"https://[fe80::20c:29ff:fe9c:409b%]:1234",
594    "",
595    0, 0, CURLUE_BAD_IPV6},
596   {"https://[fe80::20c:29ff:fe9c:409b%25]:1234",
597    "https://[fe80::20c:29ff:fe9c:409b%2525]:1234/",
598    0, 0, CURLUE_OK},
599   {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
600    "https://[fe80::20c:29ff:fe9c:409b%25eth0]:1234/",
601    0, 0, CURLUE_OK},
602   {"https://[::%25fakeit]/moo",
603    "https://[::%25fakeit]/moo",
604    0, 0, CURLUE_OK},
605   {"smtp.example.com/path/html",
606    "smtp://smtp.example.com/path/html",
607    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
608   {"https.example.com/path/html",
609    "http://https.example.com/path/html",
610    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
611   {"dict.example.com/path/html",
612    "dict://dict.example.com/path/html",
613    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
614   {"pop3.example.com/path/html",
615    "pop3://pop3.example.com/path/html",
616    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
617   {"ldap.example.com/path/html",
618    "ldap://ldap.example.com/path/html",
619    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
620   {"imap.example.com/path/html",
621    "imap://imap.example.com/path/html",
622    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
623   {"ftp.example.com/path/html",
624    "ftp://ftp.example.com/path/html",
625    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
626   {"example.com/path/html",
627    "http://example.com/path/html",
628    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
629   {"smtp.com/path/html",
630    "smtp://smtp.com/path/html",
631    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
632   {"dict.com/path/html",
633    "dict://dict.com/path/html",
634    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
635   {"pop3.com/path/html",
636    "pop3://pop3.com/path/html",
637    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
638   {"ldap.com/path/html",
639    "ldap://ldap.com/path/html",
640    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
641   {"imap.com/path/html",
642    "imap://imap.com/path/html",
643    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
644   {"ftp.com/path/html",
645    "ftp://ftp.com/path/html",
646    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
647   {"smtp/path/html",
648    "http://smtp/path/html",
649    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
650   {"dict/path/html",
651    "http://dict/path/html",
652    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
653   {"pop3/path/html",
654    "http://pop3/path/html",
655    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
656   {"ldap/path/html",
657    "http://ldap/path/html",
658    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
659   {"imap/path/html",
660    "http://imap/path/html",
661    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
662   {"ftp/path/html",
663    "http://ftp/path/html",
664    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
665   {"HTTP://test/", "http://test/", 0, 0, CURLUE_OK},
666   {"http://HO0_-st..~./", "http://HO0_-st..~./", 0, 0, CURLUE_OK},
667   {"http:/@example.com: 123/", "", 0, 0, CURLUE_MALFORMED_INPUT},
668   {"http:/@example.com:123 /", "", 0, 0, CURLUE_MALFORMED_INPUT},
669   {"http:/@example.com:123a/", "", 0, 0, CURLUE_BAD_PORT_NUMBER},
670   {"http://host/file\r", "", 0, 0, CURLUE_MALFORMED_INPUT},
671   {"http://host/file\n\x03", "", 0, 0, CURLUE_MALFORMED_INPUT},
672   {"htt\x02://host/file", "",
673    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
674   {" http://host/file", "", 0, 0, CURLUE_MALFORMED_INPUT},
675   /* here the password ends at the semicolon and options is 'word' */
676   {"imap://user:pass;word@host/file",
677    "imap://user:pass;word@host/file",
678    0, 0, CURLUE_OK},
679   /* here the password has the semicolon */
680   {"http://user:pass;word@host/file",
681    "http://user:pass;word@host/file", 0, 0, CURLUE_OK},
682   {"file:///file.txt#moo", "file:///file.txt#moo", 0, 0, CURLUE_OK},
683   {"file:////file.txt", "file:////file.txt", 0, 0, CURLUE_OK},
684   {"file:///file.txt", "file:///file.txt", 0, 0, CURLUE_OK},
685   {"file:./", "file://", 0, 0, CURLUE_OK},
686   {"http://example.com/hello/../here",
687    "http://example.com/hello/../here",
688    CURLU_PATH_AS_IS, 0, CURLUE_OK},
689   {"http://example.com/hello/../here",
690    "http://example.com/here",
691    0, 0, CURLUE_OK},
692   {"http://example.com:80",
693    "http://example.com/",
694    0, CURLU_NO_DEFAULT_PORT, CURLUE_OK},
695   {"tp://example.com/path/html",
696    "",
697    0, 0, CURLUE_UNSUPPORTED_SCHEME},
698   {"http://hello:fool@example.com",
699    "",
700    CURLU_DISALLOW_USER, 0, CURLUE_USER_NOT_ALLOWED},
701   {"http:/@example.com:123",
702    "http://@example.com:123/",
703    0, 0, CURLUE_OK},
704   {"http:/:password@example.com",
705    "http://:password@example.com/",
706    0, 0, CURLUE_OK},
707   {"http://user@example.com?#",
708    "http://user@example.com/",
709    0, 0, CURLUE_OK},
710   {"http://user@example.com?",
711    "http://user@example.com/",
712    0, 0, CURLUE_OK},
713   {"http://user@example.com#anchor",
714    "http://user@example.com/#anchor",
715    0, 0, CURLUE_OK},
716   {"example.com/path/html",
717    "https://example.com/path/html",
718    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
719   {"example.com/path/html",
720    "",
721    0, 0, CURLUE_BAD_SCHEME},
722   {"http://user:password@example.com:1234/path/html?query=name#anchor",
723    "http://user:password@example.com:1234/path/html?query=name#anchor",
724    0, 0, CURLUE_OK},
725   {"http://example.com:1234/path/html?query=name#anchor",
726    "http://example.com:1234/path/html?query=name#anchor",
727    0, 0, CURLUE_OK},
728   {"http://example.com/path/html?query=name#anchor",
729    "http://example.com/path/html?query=name#anchor",
730    0, 0, CURLUE_OK},
731   {"http://example.com/path/html?query=name",
732    "http://example.com/path/html?query=name",
733    0, 0, CURLUE_OK},
734   {"http://example.com/path/html",
735    "http://example.com/path/html",
736    0, 0, CURLUE_OK},
737   {"tp://example.com/path/html",
738    "tp://example.com/path/html",
739    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
740   {"custom-scheme://host?expected=test-good",
741    "custom-scheme://host/?expected=test-good",
742    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
743   {"custom-scheme://?expected=test-bad",
744    "",
745    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_NO_HOST},
746   {"custom-scheme://?expected=test-new-good",
747    "custom-scheme:///?expected=test-new-good",
748    CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK},
749   {"custom-scheme://host?expected=test-still-good",
750    "custom-scheme://host/?expected=test-still-good",
751    CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK},
752   {NULL, NULL, 0, 0, CURLUE_OK}
753 };
754 
checkurl(const char *org, const char *url, const char *out)755 static int checkurl(const char *org, const char *url, const char *out)
756 {
757   if(strcmp(out, url)) {
758     fprintf(stderr,
759             "Org:    %s\n"
760             "Wanted: %s\n"
761             "Got   : %s\n",
762             org, out, url);
763     return 1;
764   }
765   return 0;
766 }
767 
768 /* 1. Set the URL
769    2. Set components
770    3. Extract all components (not URL)
771 */
772 static const struct setgetcase setget_parts_list[] = {
773   {"https://example.com",
774    "path=get,",
775    "https | [11] | [12] | [13] | example.com | [15] | /get | [16] | [17]",
776    0, 0, 0, CURLUE_OK},
777   {"https://example.com",
778    "path=/get,",
779    "https | [11] | [12] | [13] | example.com | [15] | /get | [16] | [17]",
780    0, 0, 0, CURLUE_OK},
781   {"https://example.com",
782    "path=g e t,",
783    "https | [11] | [12] | [13] | example.com | [15] | /g%20e%20t | "
784    "[16] | [17]",
785    0, CURLU_URLENCODE, 0, CURLUE_OK},
786   {NULL, NULL, NULL, 0, 0, 0, CURLUE_OK}
787 };
788 
789 /* !checksrc! disable SPACEBEFORECOMMA 1 */
790 static const struct setcase set_parts_list[] = {
791   {"https://example.com/?param=value",
792    "query=\"\",",
793    "https://example.com/",
794    0, CURLU_APPENDQUERY | CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
795   {"https://example.com/",
796    "host=\"\",",
797    "https://example.com/",
798    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_BAD_HOSTNAME},
799   {"https://example.com/",
800    "host=\"\",",
801    "https://example.com/",
802    0, 0, CURLUE_OK, CURLUE_BAD_HOSTNAME},
803   {"https://example.com",
804    "path=get,",
805    "https://example.com/get",
806    0, 0, CURLUE_OK, CURLUE_OK},
807   {"https://example.com/",
808    "scheme=ftp+-.123,",
809    "ftp+-.123://example.com/",
810    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
811   {"https://example.com/",
812    "scheme=1234,",
813    "https://example.com/",
814    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
815   {"https://example.com/",
816    "scheme=1http,",
817    "https://example.com/",
818    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
819   {"https://example.com/",
820    "scheme=-ftp,",
821    "https://example.com/",
822    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
823   {"https://example.com/",
824    "scheme=+ftp,",
825    "https://example.com/",
826    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
827   {"https://example.com/",
828    "scheme=.ftp,",
829    "https://example.com/",
830    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
831   {"https://example.com/",
832    "host=example.com%2fmoo,",
833    "",
834    0, /* get */
835    0, /* set */
836    CURLUE_OK, CURLUE_BAD_HOSTNAME},
837   {"https://example.com/",
838    "host=http://fake,",
839    "",
840    0, /* get */
841    0, /* set */
842    CURLUE_OK, CURLUE_BAD_HOSTNAME},
843   {"https://example.com/",
844    "host=test%,",
845    "",
846    0, /* get */
847    0, /* set */
848    CURLUE_OK, CURLUE_BAD_HOSTNAME},
849   {"https://example.com/",
850    "host=te st,",
851    "",
852    0, /* get */
853    0, /* set */
854    CURLUE_OK, CURLUE_BAD_HOSTNAME},
855   {"https://example.com/",
856    "host=0xff,", /* '++' there's no automatic URL decode when setting this
857                   part */
858    "https://0xff/",
859    0, /* get */
860    0, /* set */
861    CURLUE_OK, CURLUE_OK},
862 
863   {"https://example.com/",
864    "query=Al2cO3tDkcDZ3EWE5Lh+LX8TPHs,", /* contains '+' */
865    "https://example.com/?Al2cO3tDkcDZ3EWE5Lh%2bLX8TPHs",
866    CURLU_URLDECODE, /* decode on get */
867    CURLU_URLENCODE, /* encode on set */
868    CURLUE_OK, CURLUE_OK},
869 
870   {"https://example.com/",
871    /* Set a bad scheme *including* :// */
872    "scheme=https://,",
873    "https://example.com/",
874    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
875   {"https://example.com/",
876    /* Set a 41 bytes scheme. That's too long so the old scheme remains set. */
877    "scheme=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc,",
878    "https://example.com/",
879    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_BAD_SCHEME},
880   {"https://example.com/",
881    /* set a 40 bytes scheme */
882    "scheme=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,",
883    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb://example.com/",
884    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
885   {"https://[::1%25fake]:1234/",
886    "zoneid=NULL,",
887    "https://[::1]:1234/",
888    0, 0, CURLUE_OK, CURLUE_OK},
889   {"https://host:1234/",
890    "port=NULL,",
891    "https://host/",
892    0, 0, CURLUE_OK, CURLUE_OK},
893   {"https://host:1234/",
894    "port=\"\",",
895    "https://host:1234/",
896    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
897   {"https://host:1234/",
898    "port=56 78,",
899    "https://host:1234/",
900    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
901   {"https://host:1234/",
902    "port=0,",
903    "https://host:1234/",
904    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
905   {"https://host:1234/",
906    "port=65535,",
907    "https://host:65535/",
908    0, 0, CURLUE_OK, CURLUE_OK},
909   {"https://host:1234/",
910    "port=65536,",
911    "https://host:1234/",
912    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
913   {"https://host/",
914    "path=%4A%4B%4C,",
915    "https://host/%4a%4b%4c",
916    0, 0, CURLUE_OK, CURLUE_OK},
917   {"https://host/mooo?q#f",
918    "path=NULL,query=NULL,fragment=NULL,",
919    "https://host/",
920    0, 0, CURLUE_OK, CURLUE_OK},
921   {"https://user:secret@host/",
922    "user=NULL,password=NULL,",
923    "https://host/",
924    0, 0, CURLUE_OK, CURLUE_OK},
925   {NULL,
926    "scheme=https,user=   @:,host=foobar,",
927    "https://%20%20%20%40%3a@foobar/",
928    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
929   /* Setting a host name with spaces is not OK: */
930   {NULL,
931    "scheme=https,host=  ,path= ,user= ,password= ,query= ,fragment= ,",
932    "[nothing]",
933    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_BAD_HOSTNAME},
934   {NULL,
935    "scheme=https,host=foobar,path=/this /path /is /here,",
936    "https://foobar/this%20/path%20/is%20/here",
937    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
938   {NULL,
939    "scheme=https,host=foobar,path=\xc3\xa4\xc3\xb6\xc3\xbc,",
940    "https://foobar/%c3%a4%c3%b6%c3%bc",
941    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
942   {"imap://user:secret;opt@host/",
943    "options=updated,scheme=imaps,password=p4ssw0rd,",
944    "imaps://user:p4ssw0rd;updated@host/",
945    0, 0, CURLUE_NO_HOST, CURLUE_OK},
946   {"imap://user:secret;optit@host/",
947    "scheme=https,",
948    "https://user:secret@host/",
949    0, 0, CURLUE_NO_HOST, CURLUE_OK},
950   {"file:///file#anchor",
951    "scheme=https,host=example,",
952    "https://example/file#anchor",
953    0, 0, CURLUE_NO_HOST, CURLUE_OK},
954   {NULL, /* start fresh! */
955    "scheme=file,host=127.0.0.1,path=/no,user=anonymous,",
956    "file:///no",
957    0, 0, CURLUE_OK, CURLUE_OK},
958   {NULL, /* start fresh! */
959    "scheme=ftp,host=127.0.0.1,path=/no,user=anonymous,",
960    "ftp://anonymous@127.0.0.1/no",
961    0, 0, CURLUE_OK, CURLUE_OK},
962   {NULL, /* start fresh! */
963    "scheme=https,host=example.com,",
964    "https://example.com/",
965    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
966   {"http://user:foo@example.com/path?query#frag",
967    "fragment=changed,",
968    "http://user:foo@example.com/path?query#changed",
969    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
970   {"http://example.com/",
971    "scheme=foo,", /* not accepted */
972    "http://example.com/",
973    0, 0, CURLUE_OK, CURLUE_UNSUPPORTED_SCHEME},
974   {"http://example.com/",
975    "scheme=https,path=/hello,fragment=snippet,",
976    "https://example.com/hello#snippet",
977    0, 0, CURLUE_OK, CURLUE_OK},
978   {"http://example.com:80",
979    "user=foo,port=1922,",
980    "http://foo@example.com:1922/",
981    0, 0, CURLUE_OK, CURLUE_OK},
982   {"http://example.com:80",
983    "user=foo,password=bar,",
984    "http://foo:bar@example.com:80/",
985    0, 0, CURLUE_OK, CURLUE_OK},
986   {"http://example.com:80",
987    "user=foo,",
988    "http://foo@example.com:80/",
989    0, 0, CURLUE_OK, CURLUE_OK},
990   {"http://example.com",
991    "host=www.example.com,",
992    "http://www.example.com/",
993    0, 0, CURLUE_OK, CURLUE_OK},
994   {"http://example.com:80",
995    "scheme=ftp,",
996    "ftp://example.com:80/",
997    0, 0, CURLUE_OK, CURLUE_OK},
998   {"custom-scheme://host",
999    "host=\"\",",
1000    "custom-scheme://host/",
1001    CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK,
1002    CURLUE_BAD_HOSTNAME},
1003   {"custom-scheme://host",
1004    "host=\"\",",
1005    "custom-scheme:///",
1006    CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY,
1007    CURLUE_OK, CURLUE_OK},
1008 
1009   {NULL, NULL, NULL, 0, 0, CURLUE_OK, CURLUE_OK}
1010 };
1011 
part2id(char *part)1012 static CURLUPart part2id(char *part)
1013 {
1014   if(!strcmp("url", part))
1015     return CURLUPART_URL;
1016   if(!strcmp("scheme", part))
1017     return CURLUPART_SCHEME;
1018   if(!strcmp("user", part))
1019     return CURLUPART_USER;
1020   if(!strcmp("password", part))
1021     return CURLUPART_PASSWORD;
1022   if(!strcmp("options", part))
1023     return CURLUPART_OPTIONS;
1024   if(!strcmp("host", part))
1025     return CURLUPART_HOST;
1026   if(!strcmp("port", part))
1027     return CURLUPART_PORT;
1028   if(!strcmp("path", part))
1029     return CURLUPART_PATH;
1030   if(!strcmp("query", part))
1031     return CURLUPART_QUERY;
1032   if(!strcmp("fragment", part))
1033     return CURLUPART_FRAGMENT;
1034   if(!strcmp("zoneid", part))
1035     return CURLUPART_ZONEID;
1036   return (CURLUPart)9999; /* bad input => bad output */
1037 }
1038 
updateurl(CURLU *u, const char *cmd, unsigned int setflags)1039 static CURLUcode updateurl(CURLU *u, const char *cmd, unsigned int setflags)
1040 {
1041   const char *p = cmd;
1042   CURLUcode uc;
1043 
1044   /* make sure the last command ends with a comma too! */
1045   while(p) {
1046     char *e = strchr(p, ',');
1047     if(e) {
1048       size_t n = (size_t)(e - p);
1049       char buf[80];
1050       char part[80];
1051       char value[80];
1052 
1053       memset(part, 0, sizeof(part)); /* Avoid valgrind false positive. */
1054       memset(value, 0, sizeof(value)); /* Avoid valgrind false positive. */
1055       memcpy(buf, p, n);
1056       buf[n] = 0;
1057       if(2 == sscanf(buf, "%79[^=]=%79[^,]", part, value)) {
1058         CURLUPart what = part2id(part);
1059 #if 0
1060         /* for debugging this */
1061         fprintf(stderr, "%s = \"%s\" [%d]\n", part, value, (int)what);
1062 #endif
1063         if(what > CURLUPART_ZONEID)
1064           fprintf(stderr, "UNKNOWN part '%s'\n", part);
1065 
1066         if(!strcmp("NULL", value))
1067           uc = curl_url_set(u, what, NULL, setflags);
1068         else if(!strcmp("\"\"", value))
1069           uc = curl_url_set(u, what, "", setflags);
1070         else
1071           uc = curl_url_set(u, what, value, setflags);
1072         if(uc)
1073           return uc;
1074       }
1075       p = e + 1;
1076       continue;
1077     }
1078     break;
1079   }
1080   return CURLUE_OK;
1081 }
1082 
1083 static const struct redircase set_url_list[] = {
1084   {"http://example.com/please/../gimme/%TESTNUMBER?foobar#hello",
1085    "http://example.net/there/it/is/../../tes t case=/%TESTNUMBER0002? yes no",
1086    "http://example.net/there/tes%20t%20case=/%TESTNUMBER0002?+yes+no",
1087    0, CURLU_URLENCODE|CURLU_ALLOW_SPACE, CURLUE_OK},
1088   {"http://local.test?redirect=http://local.test:80?-321",
1089    "http://local.test:80?-123",
1090    "http://local.test:80/?-123",
1091    0, CURLU_URLENCODE|CURLU_ALLOW_SPACE, CURLUE_OK},
1092   {"http://local.test?redirect=http://local.test:80?-321",
1093    "http://local.test:80?-123",
1094    "http://local.test:80/?-123",
1095    0, 0, CURLUE_OK},
1096   {"http://example.org/static/favicon/wikipedia.ico",
1097    "//fake.example.com/licenses/by-sa/3.0/",
1098    "http://fake.example.com/licenses/by-sa/3.0/",
1099    0, 0, CURLUE_OK},
1100   {"https://example.org/static/favicon/wikipedia.ico",
1101    "//fake.example.com/licenses/by-sa/3.0/",
1102    "https://fake.example.com/licenses/by-sa/3.0/",
1103    0, 0, CURLUE_OK},
1104   {"file://localhost/path?query#frag",
1105    "foo#another",
1106    "file:///foo#another",
1107    0, 0, CURLUE_OK},
1108   {"http://example.com/path?query#frag",
1109    "https://two.example.com/bradnew",
1110    "https://two.example.com/bradnew",
1111    0, 0, CURLUE_OK},
1112   {"http://example.com/path?query#frag",
1113    "../../newpage#foo",
1114    "http://example.com/newpage#foo",
1115    0, 0, CURLUE_OK},
1116   {"http://user:foo@example.com/path?query#frag",
1117    "../../newpage",
1118    "http://user:foo@example.com/newpage",
1119    0, 0, CURLUE_OK},
1120   {"http://user:foo@example.com/path?query#frag",
1121    "../newpage",
1122    "http://user:foo@example.com/newpage",
1123    0, 0, CURLUE_OK},
1124   {"http://user:foo@example.com/path?query#frag",
1125    "http://?hi",
1126    "http:///?hi",
1127    0, CURLU_NO_AUTHORITY, CURLUE_OK},
1128   {NULL, NULL, NULL, 0, 0, CURLUE_OK}
1129 };
1130 
set_url(void)1131 static int set_url(void)
1132 {
1133   int i;
1134   int error = 0;
1135 
1136   for(i = 0; set_url_list[i].in && !error; i++) {
1137     CURLUcode rc;
1138     CURLU *urlp = curl_url();
1139     if(!urlp)
1140       break;
1141     rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].in,
1142                       set_url_list[i].urlflags);
1143     if(!rc) {
1144       rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].set,
1145                         set_url_list[i].setflags);
1146       if(rc) {
1147         fprintf(stderr, "%s:%d Set URL %s returned %d (%s)\n",
1148                 __FILE__, __LINE__, set_url_list[i].set,
1149                 (int)rc, curl_url_strerror(rc));
1150         error++;
1151       }
1152       else {
1153         char *url = NULL;
1154         rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
1155         if(rc) {
1156           fprintf(stderr, "%s:%d Get URL returned %d (%s)\n",
1157                   __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1158           error++;
1159         }
1160         else {
1161           if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) {
1162             error++;
1163           }
1164         }
1165         curl_free(url);
1166       }
1167     }
1168     else if(rc != set_url_list[i].ucode) {
1169       fprintf(stderr, "Set URL\nin: %s\nreturned %d (expected %d)\n",
1170               set_url_list[i].in, (int)rc, set_url_list[i].ucode);
1171       error++;
1172     }
1173     curl_url_cleanup(urlp);
1174   }
1175   return error;
1176 }
1177 
1178 /* 1. Set a URL
1179    2. Set one or more parts
1180    3. Extract and compare all parts - not the URL
1181 */
setget_parts(void)1182 static int setget_parts(void)
1183 {
1184   int i;
1185   int error = 0;
1186 
1187   for(i = 0; setget_parts_list[i].set && !error; i++) {
1188     CURLUcode rc;
1189     CURLU *urlp = curl_url();
1190     if(!urlp) {
1191       error++;
1192       break;
1193     }
1194     if(setget_parts_list[i].in)
1195       rc = curl_url_set(urlp, CURLUPART_URL, setget_parts_list[i].in,
1196                         setget_parts_list[i].urlflags);
1197     else
1198       rc = CURLUE_OK;
1199     if(!rc) {
1200       char *url = NULL;
1201       CURLUcode uc = updateurl(urlp, setget_parts_list[i].set,
1202                                setget_parts_list[i].setflags);
1203 
1204       if(uc != setget_parts_list[i].pcode) {
1205         fprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n",
1206                 setget_parts_list[i].set, (int)uc, setget_parts_list[i].pcode);
1207         error++;
1208       }
1209       if(!uc) {
1210         if(checkparts(urlp, setget_parts_list[i].set, setget_parts_list[i].out,
1211                       setget_parts_list[i].getflags))
1212           error++;        /* add */
1213       }
1214       curl_free(url);
1215     }
1216     else if(rc != CURLUE_OK) {
1217       fprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n",
1218               setget_parts_list[i].in, (int)rc, 0);
1219       error++;
1220     }
1221     curl_url_cleanup(urlp);
1222   }
1223   return error;
1224 }
1225 
set_parts(void)1226 static int set_parts(void)
1227 {
1228   int i;
1229   int error = 0;
1230 
1231   for(i = 0; set_parts_list[i].set && !error; i++) {
1232     CURLUcode rc;
1233     CURLU *urlp = curl_url();
1234     if(!urlp) {
1235       error++;
1236       break;
1237     }
1238     if(set_parts_list[i].in)
1239       rc = curl_url_set(urlp, CURLUPART_URL, set_parts_list[i].in,
1240                         set_parts_list[i].urlflags);
1241     else
1242       rc = CURLUE_OK;
1243     if(!rc) {
1244       char *url = NULL;
1245       CURLUcode uc = updateurl(urlp, set_parts_list[i].set,
1246                                set_parts_list[i].setflags);
1247 
1248       if(uc != set_parts_list[i].pcode) {
1249         fprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n",
1250                 set_parts_list[i].set, (int)uc, set_parts_list[i].pcode);
1251         error++;
1252       }
1253       if(!uc) {
1254         /* only do this if it worked */
1255         rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
1256 
1257         if(rc) {
1258           fprintf(stderr, "%s:%d Get URL returned %d (%s)\n",
1259                   __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1260           error++;
1261         }
1262         else if(checkurl(set_parts_list[i].in, url, set_parts_list[i].out)) {
1263           error++;
1264         }
1265       }
1266       curl_free(url);
1267     }
1268     else if(rc != set_parts_list[i].ucode) {
1269       fprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n",
1270               set_parts_list[i].in, (int)rc, set_parts_list[i].ucode);
1271       error++;
1272     }
1273     curl_url_cleanup(urlp);
1274   }
1275   return error;
1276 }
1277 
get_url(void)1278 static int get_url(void)
1279 {
1280   int i;
1281   int error = 0;
1282   for(i = 0; get_url_list[i].in && !error; i++) {
1283     CURLUcode rc;
1284     CURLU *urlp = curl_url();
1285     if(!urlp) {
1286       error++;
1287       break;
1288     }
1289     rc = curl_url_set(urlp, CURLUPART_URL, get_url_list[i].in,
1290                       get_url_list[i].urlflags);
1291     if(!rc) {
1292       char *url = NULL;
1293       rc = curl_url_get(urlp, CURLUPART_URL, &url, get_url_list[i].getflags);
1294 
1295       if(rc) {
1296         fprintf(stderr, "%s:%d returned %d (%s). URL: '%s'\n",
1297                 __FILE__, __LINE__, (int)rc, curl_url_strerror(rc),
1298                 get_url_list[i].in);
1299         error++;
1300       }
1301       else {
1302         if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) {
1303           error++;
1304         }
1305       }
1306       curl_free(url);
1307     }
1308     if(rc != get_url_list[i].ucode) {
1309       fprintf(stderr, "Get URL\nin: %s\nreturned %d (expected %d)\n",
1310               get_url_list[i].in, (int)rc, get_url_list[i].ucode);
1311       error++;
1312     }
1313     curl_url_cleanup(urlp);
1314   }
1315   return error;
1316 }
1317 
get_parts(void)1318 static int get_parts(void)
1319 {
1320   int i;
1321   int error = 0;
1322   for(i = 0; get_parts_list[i].in && !error; i++) {
1323     CURLUcode rc;
1324     CURLU *urlp = curl_url();
1325     if(!urlp) {
1326       error++;
1327       break;
1328     }
1329     rc = curl_url_set(urlp, CURLUPART_URL,
1330                       get_parts_list[i].in,
1331                       get_parts_list[i].urlflags);
1332     if(rc != get_parts_list[i].ucode) {
1333       fprintf(stderr, "Get parts\nin: %s\nreturned %d (expected %d)\n",
1334               get_parts_list[i].in, (int)rc, get_parts_list[i].ucode);
1335       error++;
1336     }
1337     else if(get_parts_list[i].ucode) {
1338       /* the expected error happened */
1339     }
1340     else if(checkparts(urlp, get_parts_list[i].in, get_parts_list[i].out,
1341                        get_parts_list[i].getflags))
1342       error++;
1343     curl_url_cleanup(urlp);
1344   }
1345   return error;
1346 }
1347 
1348 static const struct querycase append_list[] = {
1349   {"HTTP://test/?s", "name=joe\x02", "http://test/?s&name=joe%02",
1350    0, CURLU_URLENCODE, CURLUE_OK},
1351   {"HTTP://test/?size=2#f", "name=joe=", "http://test/?size=2&name=joe%3d#f",
1352    0, CURLU_URLENCODE, CURLUE_OK},
1353   {"HTTP://test/?size=2#f", "name=joe doe",
1354    "http://test/?size=2&name=joe+doe#f",
1355    0, CURLU_URLENCODE, CURLUE_OK},
1356   {"HTTP://test/", "name=joe", "http://test/?name=joe", 0, 0, CURLUE_OK},
1357   {"HTTP://test/?size=2", "name=joe", "http://test/?size=2&name=joe",
1358    0, 0, CURLUE_OK},
1359   {"HTTP://test/?size=2&", "name=joe", "http://test/?size=2&name=joe",
1360    0, 0, CURLUE_OK},
1361   {"HTTP://test/?size=2#f", "name=joe", "http://test/?size=2&name=joe#f",
1362    0, 0, CURLUE_OK},
1363   {NULL, NULL, NULL, 0, 0, CURLUE_OK}
1364 };
1365 
append(void)1366 static int append(void)
1367 {
1368   int i;
1369   int error = 0;
1370   for(i = 0; append_list[i].in && !error; i++) {
1371     CURLUcode rc;
1372     CURLU *urlp = curl_url();
1373     if(!urlp) {
1374       error++;
1375       break;
1376     }
1377     rc = curl_url_set(urlp, CURLUPART_URL,
1378                       append_list[i].in,
1379                       append_list[i].urlflags);
1380     if(rc)
1381       error++;
1382     else
1383       rc = curl_url_set(urlp, CURLUPART_QUERY,
1384                         append_list[i].q,
1385                         append_list[i].qflags | CURLU_APPENDQUERY);
1386     if(error)
1387       ;
1388     else if(rc != append_list[i].ucode) {
1389       fprintf(stderr, "Append\nin: %s\nreturned %d (expected %d)\n",
1390               append_list[i].in, (int)rc, append_list[i].ucode);
1391       error++;
1392     }
1393     else if(append_list[i].ucode) {
1394       /* the expected error happened */
1395     }
1396     else {
1397       char *url;
1398       rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
1399       if(rc) {
1400         fprintf(stderr, "%s:%d Get URL returned %d (%s)\n",
1401                 __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1402         error++;
1403       }
1404       else {
1405         if(checkurl(append_list[i].in, url, append_list[i].out)) {
1406           error++;
1407         }
1408         curl_free(url);
1409       }
1410     }
1411     curl_url_cleanup(urlp);
1412   }
1413   return error;
1414 }
1415 
scopeid(void)1416 static int scopeid(void)
1417 {
1418   CURLU *u = curl_url();
1419   int error = 0;
1420   CURLUcode rc;
1421   char *url;
1422 
1423   rc = curl_url_set(u, CURLUPART_URL,
1424                     "https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0);
1425   if(rc != CURLUE_OK) {
1426     fprintf(stderr, "%s:%d curl_url_set returned %d (%s)\n",
1427             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1428     error++;
1429   }
1430 
1431   rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
1432   if(rc != CURLUE_OK) {
1433     fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n",
1434             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1435     error++;
1436   }
1437   else {
1438     curl_free(url);
1439   }
1440 
1441   rc = curl_url_set(u, CURLUPART_HOST, "[::1]", 0);
1442   if(rc != CURLUE_OK) {
1443     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n",
1444             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1445     error++;
1446   }
1447 
1448   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1449   if(rc != CURLUE_OK) {
1450     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n",
1451             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1452     error++;
1453   }
1454   else {
1455     curl_free(url);
1456   }
1457 
1458   rc = curl_url_set(u, CURLUPART_HOST, "example.com", 0);
1459   if(rc != CURLUE_OK) {
1460     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n",
1461             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1462     error++;
1463   }
1464 
1465   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1466   if(rc != CURLUE_OK) {
1467     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n",
1468             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1469     error++;
1470   }
1471   else {
1472     curl_free(url);
1473   }
1474 
1475   rc = curl_url_set(u, CURLUPART_HOST,
1476                     "[fe80::20c:29ff:fe9c:409b%25eth0]", 0);
1477   if(rc != CURLUE_OK) {
1478     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n",
1479             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1480     error++;
1481   }
1482 
1483   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1484   if(rc != CURLUE_OK) {
1485     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n",
1486             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1487     error++;
1488   }
1489   else {
1490     curl_free(url);
1491   }
1492 
1493   rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
1494   if(rc != CURLUE_OK) {
1495     fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n",
1496             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1497     error++;
1498   }
1499   else {
1500     curl_free(url);
1501   }
1502 
1503   rc = curl_url_get(u, CURLUPART_ZONEID, &url, 0);
1504   if(rc != CURLUE_OK) {
1505     fprintf(stderr, "%s:%d curl_url_get CURLUPART_ZONEID returned %d (%s)\n",
1506             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1507     error++;
1508   }
1509   else {
1510     curl_free(url);
1511   }
1512 
1513   rc = curl_url_set(u, CURLUPART_ZONEID, "clown", 0);
1514   if(rc != CURLUE_OK) {
1515     fprintf(stderr, "%s:%d curl_url_set CURLUPART_ZONEID returned %d (%s)\n",
1516             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1517     error++;
1518   }
1519 
1520   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1521   if(rc != CURLUE_OK) {
1522     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n",
1523             __FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
1524     error++;
1525   }
1526   else {
1527     curl_free(url);
1528   }
1529 
1530   curl_url_cleanup(u);
1531 
1532   return error;
1533 }
1534 
get_nothing(void)1535 static int get_nothing(void)
1536 {
1537   CURLU *u = curl_url();
1538   if(u) {
1539     char *p;
1540     CURLUcode rc;
1541 
1542     rc = curl_url_get(u, CURLUPART_SCHEME, &p, 0);
1543     if(rc != CURLUE_NO_SCHEME)
1544       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1545 
1546     rc = curl_url_get(u, CURLUPART_HOST, &p, 0);
1547     if(rc != CURLUE_NO_HOST)
1548       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1549 
1550     rc = curl_url_get(u, CURLUPART_USER, &p, 0);
1551     if(rc != CURLUE_NO_USER)
1552       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1553 
1554     rc = curl_url_get(u, CURLUPART_PASSWORD, &p, 0);
1555     if(rc != CURLUE_NO_PASSWORD)
1556       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1557 
1558     rc = curl_url_get(u, CURLUPART_OPTIONS, &p, 0);
1559     if(rc != CURLUE_NO_OPTIONS)
1560       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1561 
1562     rc = curl_url_get(u, CURLUPART_PATH, &p, 0);
1563     if(rc != CURLUE_OK)
1564       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1565     else
1566       curl_free(p);
1567 
1568     rc = curl_url_get(u, CURLUPART_QUERY, &p, 0);
1569     if(rc != CURLUE_NO_QUERY)
1570       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1571 
1572     rc = curl_url_get(u, CURLUPART_FRAGMENT, &p, 0);
1573     if(rc != CURLUE_NO_FRAGMENT)
1574       fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1575 
1576     rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0);
1577     if(rc != CURLUE_NO_ZONEID)
1578       fprintf(stderr, "unexpected return code %u on line %u\n", (int)rc,
1579               __LINE__);
1580 
1581     curl_url_cleanup(u);
1582   }
1583   return 0;
1584 }
1585 
1586 static const struct clearurlcase clear_url_list[] ={
1587   {CURLUPART_SCHEME, "http", NULL, CURLUE_NO_SCHEME},
1588   {CURLUPART_USER, "user", NULL, CURLUE_NO_USER},
1589   {CURLUPART_PASSWORD, "password", NULL, CURLUE_NO_PASSWORD},
1590   {CURLUPART_OPTIONS, "options", NULL, CURLUE_NO_OPTIONS},
1591   {CURLUPART_HOST, "host", NULL, CURLUE_NO_HOST},
1592   {CURLUPART_ZONEID, "eth0", NULL, CURLUE_NO_ZONEID},
1593   {CURLUPART_PORT, "1234", NULL, CURLUE_NO_PORT},
1594   {CURLUPART_PATH, "/hello", "/", CURLUE_OK},
1595   {CURLUPART_QUERY, "a=b", NULL, CURLUE_NO_QUERY},
1596   {CURLUPART_FRAGMENT, "anchor", NULL, CURLUE_NO_FRAGMENT},
1597   {CURLUPART_URL, NULL, NULL, CURLUE_OK},
1598 };
1599 
clear_url(void)1600 static int clear_url(void)
1601 {
1602   CURLU *u = curl_url();
1603   int i, error = 0;
1604   if(u) {
1605     char *p = NULL;
1606     CURLUcode rc;
1607 
1608     for(i = 0; clear_url_list[i].in && !error; i++) {
1609       rc = curl_url_set(u, clear_url_list[i].part, clear_url_list[i].in, 0);
1610       if(rc != CURLUE_OK)
1611         fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1612 
1613       rc = curl_url_set(u, CURLUPART_URL, NULL, 0);
1614       if(rc != CURLUE_OK)
1615         fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1616 
1617       rc = curl_url_get(u, clear_url_list[i].part, &p, 0);
1618       if(rc != clear_url_list[i].ucode || (clear_url_list[i].out &&
1619          0 != strcmp(p, clear_url_list[i].out))) {
1620 
1621         fprintf(stderr, "unexpected return code line %u\n", __LINE__);
1622         error++;
1623       }
1624       if(rc == CURLUE_OK)
1625         curl_free(p);
1626     }
1627   }
1628 
1629   curl_url_cleanup(u);
1630 
1631   return error;
1632 }
1633 
1634 static char total[128000];
1635 static char bigpart[120000];
1636 
1637 /*
1638  * verify ridiculous URL part sizes
1639  */
huge(void)1640 static int huge(void)
1641 {
1642   const char *smallpart = "c";
1643   int i;
1644   CURLU *urlp = curl_url();
1645   CURLUcode rc;
1646   CURLUPart part[]= {
1647     CURLUPART_SCHEME,
1648     CURLUPART_USER,
1649     CURLUPART_PASSWORD,
1650     CURLUPART_HOST,
1651     CURLUPART_PATH,
1652     CURLUPART_QUERY,
1653     CURLUPART_FRAGMENT
1654   };
1655   int error = 0;
1656   if(!urlp)
1657     return 1;
1658   bigpart[0] = '/'; /* for the path */
1659   memset(&bigpart[1], 'a', sizeof(bigpart) - 2);
1660   bigpart[sizeof(bigpart) - 1] = 0;
1661 
1662   for(i = 0; i <  7; i++) {
1663     char *partp;
1664     msnprintf(total, sizeof(total),
1665               "%s://%s:%s@%s/%s?%s#%s",
1666               (i == 0)? &bigpart[1] : smallpart,
1667               (i == 1)? &bigpart[1] : smallpart,
1668               (i == 2)? &bigpart[1] : smallpart,
1669               (i == 3)? &bigpart[1] : smallpart,
1670               (i == 4)? &bigpart[1] : smallpart,
1671               (i == 5)? &bigpart[1] : smallpart,
1672               (i == 6)? &bigpart[1] : smallpart);
1673     rc = curl_url_set(urlp, CURLUPART_URL, total, CURLU_NON_SUPPORT_SCHEME);
1674     if((!i && (rc != CURLUE_BAD_SCHEME)) ||
1675        (i && rc)) {
1676       printf("URL %u: failed to parse [%s]\n", i, total);
1677       error++;
1678     }
1679 
1680     /* only extract if the parse worked */
1681     if(!rc) {
1682       curl_url_get(urlp, part[i], &partp, 0);
1683       if(!partp || strcmp(partp, &bigpart[1 - (i == 4)])) {
1684         printf("URL %u part %u: failure\n", i, part[i]);
1685         error++;
1686       }
1687       curl_free(partp);
1688     }
1689   }
1690   curl_url_cleanup(urlp);
1691   return error;
1692 }
1693 
urldup(void)1694 static int urldup(void)
1695 {
1696   const char *url[] = {
1697     "http://"
1698     "user:pwd@"
1699     "[2a04:4e42:e00::347%25eth0]"
1700     ":80"
1701     "/path"
1702     "?query"
1703     "#fraggie",
1704     "https://example.com",
1705     "https://user@example.com",
1706     "https://user.pwd@example.com",
1707     "https://user.pwd@example.com:1234",
1708     "https://example.com:1234",
1709     "example.com:1234",
1710     "https://user.pwd@example.com:1234/path?query#frag",
1711     NULL
1712   };
1713   CURLU *copy = NULL;
1714   char *h_str = NULL, *copy_str = NULL;
1715   CURLU *h = curl_url();
1716   int i;
1717 
1718   if(!h)
1719     goto err;
1720 
1721   for(i = 0; url[i]; i++) {
1722     CURLUcode rc = curl_url_set(h, CURLUPART_URL, url[i],
1723                                 CURLU_GUESS_SCHEME);
1724     if(rc)
1725       goto err;
1726     copy = curl_url_dup(h);
1727 
1728     rc = curl_url_get(h, CURLUPART_URL, &h_str, 0);
1729     if(rc)
1730       goto err;
1731 
1732     rc = curl_url_get(copy, CURLUPART_URL, &copy_str, 0);
1733     if(rc)
1734       goto err;
1735 
1736     if(strcmp(h_str, copy_str)) {
1737       printf("Original:  %s\nParsed:    %s\nCopy:      %s\n",
1738              url[i], h_str, copy_str);
1739       goto err;
1740     }
1741     curl_free(copy_str);
1742     curl_free(h_str);
1743     curl_url_cleanup(copy);
1744     copy_str = NULL;
1745     h_str = NULL;
1746     copy = NULL;
1747   }
1748   curl_url_cleanup(h);
1749   return 0;
1750 err:
1751   curl_free(copy_str);
1752   curl_free(h_str);
1753   curl_url_cleanup(copy);
1754   curl_url_cleanup(h);
1755   return 1;
1756 }
1757 
test(char *URL)1758 int test(char *URL)
1759 {
1760   (void)URL; /* not used */
1761 
1762   if(urldup())
1763     return 11;
1764 
1765   if(setget_parts())
1766     return 10;
1767 
1768   if(get_url())
1769     return 3;
1770 
1771   if(huge())
1772     return 9;
1773 
1774   if(get_nothing())
1775     return 7;
1776 
1777   if(scopeid())
1778     return 6;
1779 
1780   if(append())
1781     return 5;
1782 
1783   if(set_url())
1784     return 1;
1785 
1786   if(set_parts())
1787     return 2;
1788 
1789   if(get_parts())
1790     return 4;
1791 
1792   if(clear_url())
1793     return 8;
1794 
1795   printf("success\n");
1796   return 0;
1797 }
1798