1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, 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  ***************************************************************************/
22 
23 /*
24  * Note:
25  *
26  * Since the URL parser by default only accepts schemes that *this instance*
27  * of libcurl supports, make sure that the test1560 file lists all the schemes
28  * that this test will assume to be present!
29  */
30 
31 #include "test.h"
32 
33 #include "testutil.h"
34 #include "warnless.h"
35 #include "memdebug.h" /* LAST include file */
36 
37 struct part {
38   CURLUPart part;
39   const char *name;
40 };
41 
42 
checkparts(CURLU * u,const char * in,const char * wanted,unsigned int getflags)43 static int checkparts(CURLU *u, const char *in, const char *wanted,
44                       unsigned int getflags)
45 {
46   int i;
47   CURLUcode rc;
48   char buf[256];
49   char *bufp = &buf[0];
50   size_t len = sizeof(buf);
51   struct part parts[] = {
52     {CURLUPART_SCHEME, "scheme"},
53     {CURLUPART_USER, "user"},
54     {CURLUPART_PASSWORD, "password"},
55     {CURLUPART_OPTIONS, "options"},
56     {CURLUPART_HOST, "host"},
57     {CURLUPART_PORT, "port"},
58     {CURLUPART_PATH, "path"},
59     {CURLUPART_QUERY, "query"},
60     {CURLUPART_FRAGMENT, "fragment"},
61     {0, NULL}
62   };
63   memset(buf, 0, sizeof(buf));
64 
65   for(i = 0; parts[i].name; i++) {
66     char *p = NULL;
67     size_t n;
68     rc = curl_url_get(u, parts[i].part, &p, getflags);
69     if(!rc && p) {
70       msnprintf(bufp, len, "%s%s", buf[0]?" | ":"", p);
71     }
72     else
73       msnprintf(bufp, len, "%s[%d]", buf[0]?" | ":"", (int)rc);
74 
75     n = strlen(bufp);
76     bufp += n;
77     len -= n;
78     curl_free(p);
79   }
80   if(strcmp(buf, wanted)) {
81     fprintf(stderr, "in: %s\nwanted: %s\ngot:    %s\n", in, wanted, buf);
82     return 1;
83   }
84   return 0;
85 }
86 
87 struct redircase {
88   const char *in;
89   const char *set;
90   const char *out;
91   unsigned int urlflags;
92   unsigned int setflags;
93   CURLUcode ucode;
94 };
95 
96 struct setcase {
97   const char *in;
98   const char *set;
99   const char *out;
100   unsigned int urlflags;
101   unsigned int setflags;
102   CURLUcode ucode; /* for the main URL set */
103   CURLUcode pcode; /* for updating parts */
104 };
105 
106 struct testcase {
107   const char *in;
108   const char *out;
109   unsigned int urlflags;
110   unsigned int getflags;
111   CURLUcode ucode;
112 };
113 
114 struct urltestcase {
115   const char *in;
116   const char *out;
117   unsigned int urlflags; /* pass to curl_url() */
118   unsigned int getflags; /* pass to curl_url_get() */
119   CURLUcode ucode;
120 };
121 
122 struct querycase {
123   const char *in;
124   const char *q;
125   const char *out;
126   unsigned int urlflags; /* pass to curl_url() */
127   unsigned int qflags; /* pass to curl_url_get() */
128   CURLUcode ucode;
129 };
130 
131 static struct testcase get_parts_list[] ={
132   {"[::1]",
133    "http | [11] | [12] | [13] | [::1] | [15] | / | [16] | [17]",
134    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
135   {"[::]",
136    "http | [11] | [12] | [13] | [::] | [15] | / | [16] | [17]",
137    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
138   {"https://[::1]",
139    "https | [11] | [12] | [13] | [::1] | [15] | / | [16] | [17]",
140    0, 0, CURLUE_OK },
141   {"user:moo@ftp.example.com/color/#green?no-red",
142    "ftp | user | moo | [13] | ftp.example.com | [15] | /color/ | [16] | "
143    "green?no-red",
144    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
145   {"ftp.user:moo@example.com/color/#green?no-red",
146    "http | ftp.user | moo | [13] | example.com | [15] | /color/ | [16] | "
147    "green?no-red",
148    CURLU_GUESS_SCHEME, 0, CURLUE_OK },
149 #ifdef WIN32
150   {"file:/C:\\programs\\foo",
151    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
152    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
153   {"file://C:\\programs\\foo",
154    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
155    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
156   {"file:///C:\\programs\\foo",
157    "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
158    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
159 #endif
160   {"https://example.com/color/#green?no-red",
161    "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "
162    "green?no-red",
163    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
164   {"https://example.com/color/#green#no-red",
165    "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "
166    "green#no-red",
167    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
168   {"https://example.com/color/?green#no-red",
169    "https | [11] | [12] | [13] | example.com | [15] | /color/ | green | "
170    "no-red",
171    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
172   {"https://example.com/#color/?green#no-red",
173    "https | [11] | [12] | [13] | example.com | [15] | / | [16] | "
174    "color/?green#no-red",
175    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
176   {"https://example.#com/color/?green#no-red",
177    "https | [11] | [12] | [13] | example. | [15] | / | [16] | "
178    "com/color/?green#no-red",
179    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK },
180   {"http://[ab.be:1]/x", "",
181    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
182   {"http://[ab.be]/x", "",
183    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
184   /* URL without host name */
185   {"http://a:b@/x", "",
186    CURLU_DEFAULT_SCHEME, 0, CURLUE_NO_HOST},
187   {"boing:80",
188    "https | [11] | [12] | [13] | boing | 80 | / | [16] | [17]",
189    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
190   {"http://[fd00:a41::50]:8080",
191    "http | [11] | [12] | [13] | [fd00:a41::50] | 8080 | / | [16] | [17]",
192    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
193   {"http://[fd00:a41::50]/",
194    "http | [11] | [12] | [13] | [fd00:a41::50] | [15] | / | [16] | [17]",
195    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
196   {"http://[fd00:a41::50]",
197    "http | [11] | [12] | [13] | [fd00:a41::50] | [15] | / | [16] | [17]",
198    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
199   {"https://[::1%252]:1234",
200    "https | [11] | [12] | [13] | [::1] | 1234 | / | [16] | [17]",
201    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
202 
203   /* here's "bad" zone id */
204   {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
205    "https | [11] | [12] | [13] | [fe80::20c:29ff:fe9c:409b] | 1234 "
206    "| / | [16] | [17]",
207    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
208   {"https://127.0.0.1:443",
209    "https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]",
210    0, CURLU_NO_DEFAULT_PORT, CURLUE_OK},
211   {"http://%3a:%3a@ex%0ample/%3f+?+%3f+%23#+%23%3f%g7",
212    "http | : | : | [13] | [6] | [15] | /?+ |  ? # | +#?%g7",
213    0, CURLU_URLDECODE, CURLUE_OK},
214   {"http://%3a:%3a@ex%0ample/%3f?%3f%35#%35%3f%g7",
215    "http | %3a | %3a | [13] | ex%0ample | [15] | /%3f | %3f%35 | %35%3f%g7",
216    0, 0, CURLUE_OK},
217   {"http://HO0_-st%41/",
218    "http | [11] | [12] | [13] | HO0_-st%41 | [15] | / | [16] | [17]",
219    0, 0, CURLUE_OK},
220   {"file://hello.html",
221    "",
222    0, 0, CURLUE_MALFORMED_INPUT},
223   {"http://HO0_-st/",
224    "http | [11] | [12] | [13] | HO0_-st | [15] | / | [16] | [17]",
225    0, 0, CURLUE_OK},
226   {"imap://user:pass;option@server/path",
227    "imap | user | pass | option | server | [15] | /path | [16] | [17]",
228    0, 0, CURLUE_OK},
229   {"http://user:pass;option@server/path",
230    "http | user | pass;option | [13] | server | [15] | /path | [16] | [17]",
231    0, 0, CURLUE_OK},
232   {"file:/hello.html",
233    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
234    0, 0, CURLUE_OK},
235   {"file://127.0.0.1/hello.html",
236    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
237    0, 0, CURLUE_OK},
238   {"file:////hello.html",
239    "file | [11] | [12] | [13] | [14] | [15] | //hello.html | [16] | [17]",
240    0, 0, CURLUE_OK},
241   {"file:///hello.html",
242    "file | [11] | [12] | [13] | [14] | [15] | /hello.html | [16] | [17]",
243    0, 0, CURLUE_OK},
244   {"https://127.0.0.1",
245    "https | [11] | [12] | [13] | 127.0.0.1 | 443 | / | [16] | [17]",
246    0, CURLU_DEFAULT_PORT, CURLUE_OK},
247   {"https://127.0.0.1",
248    "https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]",
249    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
250   {"https://[::1]:1234",
251    "https | [11] | [12] | [13] | [::1] | 1234 | / | [16] | [17]",
252    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
253   {"https://127abc.com",
254    "https | [11] | [12] | [13] | 127abc.com | [15] | / | [16] | [17]",
255    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
256   {"https:// example.com?check",
257    "",
258    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
259   {"https://e x a m p l e.com?check",
260    "",
261    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
262   {"https://example.com?check",
263    "https | [11] | [12] | [13] | example.com | [15] | / | check | [17]",
264    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
265   {"https://example.com:65536",
266    "",
267    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_PORT_NUMBER},
268   {"https://example.com:0#moo",
269    "",
270    CURLU_DEFAULT_SCHEME, 0, CURLUE_BAD_PORT_NUMBER},
271   {"https://example.com:01#moo",
272    "https | [11] | [12] | [13] | example.com | 1 | / | "
273    "[16] | moo",
274    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
275   {"https://example.com:1#moo",
276    "https | [11] | [12] | [13] | example.com | 1 | / | "
277    "[16] | moo",
278    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
279   {"http://example.com#moo",
280    "http | [11] | [12] | [13] | example.com | [15] | / | "
281    "[16] | moo",
282    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
283   {"http://example.com",
284    "http | [11] | [12] | [13] | example.com | [15] | / | "
285    "[16] | [17]",
286    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
287   {"http://example.com/path/html",
288    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
289    "[16] | [17]",
290    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
291   {"http://example.com/path/html?query=name",
292    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
293    "query=name | [17]",
294    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
295   {"http://example.com/path/html?query=name#anchor",
296    "http | [11] | [12] | [13] | example.com | [15] | /path/html | "
297    "query=name | anchor",
298    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
299   {"http://example.com:1234/path/html?query=name#anchor",
300    "http | [11] | [12] | [13] | example.com | 1234 | /path/html | "
301    "query=name | anchor",
302    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
303   {"http:///user:password@example.com:1234/path/html?query=name#anchor",
304    "http | user | password | [13] | example.com | 1234 | /path/html | "
305    "query=name | anchor",
306    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
307   {"https://user:password@example.com:1234/path/html?query=name#anchor",
308    "https | user | password | [13] | example.com | 1234 | /path/html | "
309    "query=name | anchor",
310    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
311   {"http://user:password@example.com:1234/path/html?query=name#anchor",
312    "http | user | password | [13] | example.com | 1234 | /path/html | "
313    "query=name | anchor",
314    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
315   {"http:/user:password@example.com:1234/path/html?query=name#anchor",
316    "http | user | password | [13] | example.com | 1234 | /path/html | "
317    "query=name | anchor",
318    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
319   {"http:////user:password@example.com:1234/path/html?query=name#anchor",
320    "",
321    CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
322   {NULL, NULL, 0, 0, CURLUE_OK},
323 };
324 
325 static struct urltestcase get_url_list[] = {
326   /* 40 bytes scheme is the max allowed */
327   {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA://hostname/path",
328    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa://hostname/path",
329    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
330   /* 41 bytes scheme is not allowed */
331   {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA://hostname/path",
332    "",
333    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
334   {"https://[fe80::20c:29ff:fe9c:409b%]:1234",
335    "",
336    0, 0, CURLUE_MALFORMED_INPUT},
337   {"https://[fe80::20c:29ff:fe9c:409b%25]:1234",
338    "https://[fe80::20c:29ff:fe9c:409b%2525]:1234/",
339    0, 0, CURLUE_OK},
340   {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
341    "https://[fe80::20c:29ff:fe9c:409b%25eth0]:1234/",
342    0, 0, CURLUE_OK},
343   {"https://[::%25fakeit]/moo",
344    "https://[::%25fakeit]/moo",
345    0, 0, CURLUE_OK},
346   {"smtp.example.com/path/html",
347    "smtp://smtp.example.com/path/html",
348    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
349   {"https.example.com/path/html",
350    "http://https.example.com/path/html",
351    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
352   {"dict.example.com/path/html",
353    "dict://dict.example.com/path/html",
354    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
355   {"pop3.example.com/path/html",
356    "pop3://pop3.example.com/path/html",
357    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
358   {"ldap.example.com/path/html",
359    "ldap://ldap.example.com/path/html",
360    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
361   {"imap.example.com/path/html",
362    "imap://imap.example.com/path/html",
363    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
364   {"ftp.example.com/path/html",
365    "ftp://ftp.example.com/path/html",
366    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
367   {"example.com/path/html",
368    "http://example.com/path/html",
369    CURLU_GUESS_SCHEME, 0, CURLUE_OK},
370   {"HTTP://test/", "http://test/", 0, 0, CURLUE_OK},
371   {"http://HO0_-st..~./", "http://HO0_-st..~./", 0, 0, CURLUE_OK},
372   {"http:/@example.com: 123/", "", 0, 0, CURLUE_BAD_PORT_NUMBER},
373   {"http:/@example.com:123 /", "", 0, 0, CURLUE_BAD_PORT_NUMBER},
374   {"http:/@example.com:123a/", "", 0, 0, CURLUE_BAD_PORT_NUMBER},
375   {"http://host/file\r", "", 0, 0, CURLUE_MALFORMED_INPUT},
376   {"http://host/file\n\x03", "", 0, 0, CURLUE_MALFORMED_INPUT},
377   {"htt\x02://host/file", "",
378    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
379   {" http://host/file", "", 0, 0, CURLUE_MALFORMED_INPUT},
380   /* here the password ends at the semicolon and options is 'word' */
381   {"imap://user:pass;word@host/file",
382    "imap://user:pass;word@host/file",
383    0, 0, CURLUE_OK},
384   /* here the password has the semicolon */
385   {"http://user:pass;word@host/file",
386    "http://user:pass;word@host/file",
387    0, 0, CURLUE_OK},
388   {"file:///file.txt#moo",
389    "file:///file.txt#moo",
390    0, 0, CURLUE_OK},
391   {"file:////file.txt",
392    "file:////file.txt",
393    0, 0, CURLUE_OK},
394   {"file:///file.txt",
395    "file:///file.txt",
396    0, 0, CURLUE_OK},
397   {"file:./",
398    "file://",
399    0, 0, CURLUE_MALFORMED_INPUT},
400   {"http://example.com/hello/../here",
401    "http://example.com/hello/../here",
402    CURLU_PATH_AS_IS, 0, CURLUE_OK},
403   {"http://example.com/hello/../here",
404    "http://example.com/here",
405    0, 0, CURLUE_OK},
406   {"http://example.com:80",
407    "http://example.com/",
408    0, CURLU_NO_DEFAULT_PORT, CURLUE_OK},
409   {"tp://example.com/path/html",
410    "",
411    0, 0, CURLUE_UNSUPPORTED_SCHEME},
412   {"http://hello:fool@example.com",
413    "",
414    CURLU_DISALLOW_USER, 0, CURLUE_USER_NOT_ALLOWED},
415   {"http:/@example.com:123",
416    "http://example.com:123/",
417    0, 0, CURLUE_OK},
418   {"http:/:password@example.com",
419    "http://:password@example.com/",
420    0, 0, CURLUE_OK},
421   {"http://user@example.com?#",
422    "http://user@example.com/",
423    0, 0, CURLUE_OK},
424   {"http://user@example.com?",
425    "http://user@example.com/",
426    0, 0, CURLUE_OK},
427   {"http://user@example.com#anchor",
428    "http://user@example.com/#anchor",
429    0, 0, CURLUE_OK},
430   {"example.com/path/html",
431    "https://example.com/path/html",
432    CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
433   {"example.com/path/html",
434    "",
435    0, 0, CURLUE_MALFORMED_INPUT},
436   {"http://user:password@example.com:1234/path/html?query=name#anchor",
437    "http://user:password@example.com:1234/path/html?query=name#anchor",
438    0, 0, CURLUE_OK},
439   {"http://example.com:1234/path/html?query=name#anchor",
440    "http://example.com:1234/path/html?query=name#anchor",
441    0, 0, CURLUE_OK},
442   {"http://example.com/path/html?query=name#anchor",
443    "http://example.com/path/html?query=name#anchor",
444    0, 0, CURLUE_OK},
445   {"http://example.com/path/html?query=name",
446    "http://example.com/path/html?query=name",
447    0, 0, CURLUE_OK},
448   {"http://example.com/path/html",
449    "http://example.com/path/html",
450    0, 0, CURLUE_OK},
451   {"tp://example.com/path/html",
452    "tp://example.com/path/html",
453    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
454   {"custom-scheme://host?expected=test-good",
455    "custom-scheme://host/?expected=test-good",
456    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_OK},
457   {"custom-scheme://?expected=test-bad",
458    "",
459    CURLU_NON_SUPPORT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
460   {"custom-scheme://?expected=test-new-good",
461    "custom-scheme:///?expected=test-new-good",
462    CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK},
463   {"custom-scheme://host?expected=test-still-good",
464    "custom-scheme://host/?expected=test-still-good",
465    CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY, 0, CURLUE_OK},
466   {NULL, NULL, 0, 0, 0}
467 };
468 
checkurl(const char * url,const char * out)469 static int checkurl(const char *url, const char *out)
470 {
471   if(strcmp(out, url)) {
472     fprintf(stderr, "Wanted: %s\nGot   : %s\n",
473             out, url);
474     return 1;
475   }
476   return 0;
477 }
478 
479 /* !checksrc! disable SPACEBEFORECOMMA 1 */
480 static struct setcase set_parts_list[] = {
481   {"https://example.com/",
482    "query=Al2cO3tDkcDZ3EWE5Lh+LX8TPHs,", /* contains '+' */
483    "https://example.com/?Al2cO3tDkcDZ3EWE5Lh%2bLX8TPHs",
484    CURLU_URLDECODE, /* decode on get */
485    CURLU_URLENCODE, /* encode on set */
486    CURLUE_OK, CURLUE_OK},
487 
488   {"https://example.com/",
489    /* Set a 41 bytes scheme. That's too long so the old scheme remains set. */
490    "scheme=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc,",
491    "https://example.com/",
492    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_MALFORMED_INPUT},
493   {"https://example.com/",
494    /* set a 40 bytes scheme */
495    "scheme=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,",
496    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb://example.com/",
497    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
498   {"https://[::1%25fake]:1234/",
499    "zoneid=NULL,",
500    "https://[::1]:1234/",
501    0, 0, CURLUE_OK, CURLUE_OK},
502   {"https://host:1234/",
503    "port=NULL,",
504    "https://host/",
505    0, 0, CURLUE_OK, CURLUE_OK},
506   {"https://host:1234/",
507    "port=\"\",",
508    "https://host:1234/",
509    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
510   {"https://host:1234/",
511    "port=56 78,",
512    "https://host:1234/",
513    0, 0, CURLUE_OK, CURLUE_MALFORMED_INPUT},
514   {"https://host:1234/",
515    "port=0,",
516    "https://host:1234/",
517    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
518   {"https://host:1234/",
519    "port=65535,",
520    "https://host:65535/",
521    0, 0, CURLUE_OK, CURLUE_OK},
522   {"https://host:1234/",
523    "port=65536,",
524    "https://host:1234/",
525    0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
526   {"https://host/",
527    "path=%4A%4B%4C,",
528    "https://host/%4a%4b%4c",
529    0, 0, CURLUE_OK, CURLUE_OK},
530   {"https://host/mooo?q#f",
531    "path=NULL,query=NULL,fragment=NULL,",
532    "https://host/",
533    0, 0, CURLUE_OK, CURLUE_OK},
534   {"https://user:secret@host/",
535    "user=NULL,password=NULL,",
536    "https://host/",
537    0, 0, CURLUE_OK, CURLUE_OK},
538   {NULL,
539    "scheme=https,user=   @:,host=foobar,",
540    "https://%20%20%20%40%3a@foobar/",
541    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
542   {NULL,
543    "scheme=https,host=  ,path= ,user= ,password= ,query= ,fragment= ,",
544    "https://%20:%20@%20%20/%20?+#%20",
545    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
546   {NULL,
547    "scheme=https,host=foobar,path=/this /path /is /here,",
548    "https://foobar/this%20/path%20/is%20/here",
549    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
550   {NULL,
551    "scheme=https,host=foobar,path=\xc3\xa4\xc3\xb6\xc3\xbc,",
552    "https://foobar/%c3%a4%c3%b6%c3%bc",
553    0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK},
554   {"imap://user:secret;opt@host/",
555    "options=updated,scheme=imaps,password=p4ssw0rd,",
556    "imaps://user:p4ssw0rd;updated@host/",
557    0, 0, CURLUE_NO_HOST, CURLUE_OK},
558   {"imap://user:secret;optit@host/",
559    "scheme=https,",
560    "https://user:secret@host/",
561    0, 0, CURLUE_NO_HOST, CURLUE_OK},
562   {"file:///file#anchor",
563    "scheme=https,host=example,",
564    "https://example/file#anchor",
565    0, 0, CURLUE_NO_HOST, CURLUE_OK},
566   {NULL, /* start fresh! */
567    "scheme=file,host=127.0.0.1,path=/no,user=anonymous,",
568    "file:///no",
569    0, 0, CURLUE_OK, CURLUE_OK},
570   {NULL, /* start fresh! */
571    "scheme=ftp,host=127.0.0.1,path=/no,user=anonymous,",
572    "ftp://anonymous@127.0.0.1/no",
573    0, 0, CURLUE_OK, CURLUE_OK},
574   {NULL, /* start fresh! */
575    "scheme=https,host=example.com,",
576    "https://example.com/",
577    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
578   {"http://user:foo@example.com/path?query#frag",
579    "fragment=changed,",
580    "http://user:foo@example.com/path?query#changed",
581    0, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK, CURLUE_OK},
582   {"http://example.com/",
583    "scheme=foo,", /* not accepted */
584    "http://example.com/",
585    0, 0, CURLUE_OK, CURLUE_UNSUPPORTED_SCHEME},
586   {"http://example.com/",
587    "scheme=https,path=/hello,fragment=snippet,",
588    "https://example.com/hello#snippet",
589    0, 0, CURLUE_OK, CURLUE_OK},
590   {"http://example.com:80",
591    "user=foo,port=1922,",
592    "http://foo@example.com:1922/",
593    0, 0, CURLUE_OK, CURLUE_OK},
594   {"http://example.com:80",
595    "user=foo,password=bar,",
596    "http://foo:bar@example.com:80/",
597    0, 0, CURLUE_OK, CURLUE_OK},
598   {"http://example.com:80",
599    "user=foo,",
600    "http://foo@example.com:80/",
601    0, 0, CURLUE_OK, CURLUE_OK},
602   {"http://example.com",
603    "host=www.example.com,",
604    "http://www.example.com/",
605    0, 0, CURLUE_OK, CURLUE_OK},
606   {"http://example.com:80",
607    "scheme=ftp,",
608    "ftp://example.com:80/",
609    0, 0, CURLUE_OK, CURLUE_OK},
610   {"custom-scheme://host",
611    "host=\"\",",
612    "custom-scheme://host/",
613    CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME, CURLUE_OK,
614    CURLUE_MALFORMED_INPUT},
615   {"custom-scheme://host",
616    "host=\"\",",
617    "custom-scheme:///",
618    CURLU_NON_SUPPORT_SCHEME, CURLU_NON_SUPPORT_SCHEME | CURLU_NO_AUTHORITY,
619    CURLUE_OK, CURLUE_OK},
620 
621   {NULL, NULL, NULL, 0, 0, 0, 0}
622 };
623 
part2id(char * part)624 static CURLUPart part2id(char *part)
625 {
626   if(!strcmp("url", part))
627     return CURLUPART_URL;
628   if(!strcmp("scheme", part))
629     return CURLUPART_SCHEME;
630   if(!strcmp("user", part))
631     return CURLUPART_USER;
632   if(!strcmp("password", part))
633     return CURLUPART_PASSWORD;
634   if(!strcmp("options", part))
635     return CURLUPART_OPTIONS;
636   if(!strcmp("host", part))
637     return CURLUPART_HOST;
638   if(!strcmp("port", part))
639     return CURLUPART_PORT;
640   if(!strcmp("path", part))
641     return CURLUPART_PATH;
642   if(!strcmp("query", part))
643     return CURLUPART_QUERY;
644   if(!strcmp("fragment", part))
645     return CURLUPART_FRAGMENT;
646   if(!strcmp("zoneid", part))
647     return CURLUPART_ZONEID;
648   return (CURLUPart)9999; /* bad input => bad output */
649 }
650 
updateurl(CURLU * u,const char * cmd,unsigned int setflags)651 static CURLUcode updateurl(CURLU *u, const char *cmd, unsigned int setflags)
652 {
653   const char *p = cmd;
654   CURLUcode uc;
655 
656   /* make sure the last command ends with a comma too! */
657   while(p) {
658     char *e = strchr(p, ',');
659     if(e) {
660       size_t n = e-p;
661       char buf[80];
662       char part[80];
663       char value[80];
664 
665       memset(part, 0, sizeof(part)); /* Avoid valgrind false positive. */
666       memset(value, 0, sizeof(value)); /* Avoid valgrind false positive. */
667       memcpy(buf, p, n);
668       buf[n] = 0;
669       if(2 == sscanf(buf, "%79[^=]=%79[^,]", part, value)) {
670         CURLUPart what = part2id(part);
671 #if 0
672         /* for debugging this */
673         fprintf(stderr, "%s = %s [%d]\n", part, value, (int)what);
674 #endif
675         if(what > CURLUPART_ZONEID)
676           fprintf(stderr, "UNKNOWN part '%s'\n", part);
677 
678         if(!strcmp("NULL", value))
679           uc = curl_url_set(u, what, NULL, setflags);
680         else if(!strcmp("\"\"", value))
681           uc = curl_url_set(u, what, "", setflags);
682         else
683           uc = curl_url_set(u, what, value, setflags);
684         if(uc)
685           return uc;
686       }
687       p = e + 1;
688       continue;
689     }
690     break;
691   }
692   return CURLUE_OK;
693 }
694 
695 static struct redircase set_url_list[] = {
696   {"http://example.org/static/favicon/wikipedia.ico",
697    "//fake.example.com/licenses/by-sa/3.0/",
698    "http://fake.example.com/licenses/by-sa/3.0/",
699    0, 0, 0},
700   {"https://example.org/static/favicon/wikipedia.ico",
701    "//fake.example.com/licenses/by-sa/3.0/",
702    "https://fake.example.com/licenses/by-sa/3.0/",
703    0, 0, 0},
704   {"file://localhost/path?query#frag",
705    "foo#another",
706    "file:///foo#another",
707    0, 0, 0},
708   {"http://example.com/path?query#frag",
709    "https://two.example.com/bradnew",
710    "https://two.example.com/bradnew",
711    0, 0, 0},
712   {"http://example.com/path?query#frag",
713    "../../newpage#foo",
714    "http://example.com/newpage#foo",
715    0, 0, 0},
716   {"http://user:foo@example.com/path?query#frag",
717    "../../newpage",
718    "http://user:foo@example.com/newpage",
719    0, 0, 0},
720   {"http://user:foo@example.com/path?query#frag",
721    "../newpage",
722    "http://user:foo@example.com/newpage",
723    0, 0, 0},
724   {NULL, NULL, NULL, 0, 0, 0}
725 };
726 
set_url(void)727 static int set_url(void)
728 {
729   int i;
730   int error = 0;
731 
732   for(i = 0; set_url_list[i].in && !error; i++) {
733     CURLUcode rc;
734     CURLU *urlp = curl_url();
735     if(!urlp)
736       break;
737     rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].in,
738                       set_url_list[i].urlflags);
739     if(!rc) {
740       rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].set,
741                         set_url_list[i].setflags);
742       if(rc) {
743         fprintf(stderr, "%s:%d Set URL %s returned %d\n",
744                 __FILE__, __LINE__, set_url_list[i].set,
745                 (int)rc);
746         error++;
747       }
748       else {
749         char *url = NULL;
750         rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
751         if(rc) {
752           fprintf(stderr, "%s:%d Get URL returned %d\n",
753                   __FILE__, __LINE__, (int)rc);
754           error++;
755         }
756         else {
757           if(checkurl(url, set_url_list[i].out)) {
758             error++;
759           }
760         }
761         curl_free(url);
762       }
763     }
764     else if(rc != set_url_list[i].ucode) {
765       fprintf(stderr, "Set URL\nin: %s\nreturned %d (expected %d)\n",
766               set_url_list[i].in, (int)rc, set_url_list[i].ucode);
767       error++;
768     }
769     curl_url_cleanup(urlp);
770   }
771   return error;
772 }
773 
set_parts(void)774 static int set_parts(void)
775 {
776   int i;
777   int error = 0;
778 
779   for(i = 0; set_parts_list[i].set && !error; i++) {
780     CURLUcode rc;
781     CURLU *urlp = curl_url();
782     if(!urlp) {
783       error++;
784       break;
785     }
786     if(set_parts_list[i].in)
787       rc = curl_url_set(urlp, CURLUPART_URL, set_parts_list[i].in,
788                         set_parts_list[i].urlflags);
789     else
790       rc = CURLUE_OK;
791     if(!rc) {
792       char *url = NULL;
793       CURLUcode uc = updateurl(urlp, set_parts_list[i].set,
794                                set_parts_list[i].setflags);
795 
796       if(uc != set_parts_list[i].pcode) {
797         fprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n",
798                 set_parts_list[i].set, (int)uc, set_parts_list[i].pcode);
799         error++;
800       }
801 
802       rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
803 
804       if(rc) {
805         fprintf(stderr, "%s:%d Get URL returned %d\n",
806                 __FILE__, __LINE__, (int)rc);
807         error++;
808       }
809       else if(checkurl(url, set_parts_list[i].out)) {
810         error++;
811       }
812       curl_free(url);
813     }
814     else if(rc != set_parts_list[i].ucode) {
815       fprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n",
816               set_parts_list[i].in, (int)rc, set_parts_list[i].ucode);
817       error++;
818     }
819     curl_url_cleanup(urlp);
820   }
821   return error;
822 }
823 
get_url(void)824 static int get_url(void)
825 {
826   int i;
827   int error = 0;
828   for(i = 0; get_url_list[i].in && !error; i++) {
829     CURLUcode rc;
830     CURLU *urlp = curl_url();
831     if(!urlp) {
832       error++;
833       break;
834     }
835     rc = curl_url_set(urlp, CURLUPART_URL, get_url_list[i].in,
836                       get_url_list[i].urlflags);
837     if(!rc) {
838       char *url = NULL;
839       rc = curl_url_get(urlp, CURLUPART_URL, &url, get_url_list[i].getflags);
840 
841       if(rc) {
842         fprintf(stderr, "%s:%d returned %d\n",
843                 __FILE__, __LINE__, (int)rc);
844         error++;
845       }
846       else {
847         if(checkurl(url, get_url_list[i].out)) {
848           error++;
849         }
850       }
851       curl_free(url);
852     }
853     else if(rc != get_url_list[i].ucode) {
854       fprintf(stderr, "Get URL\nin: %s\nreturned %d (expected %d)\n",
855               get_url_list[i].in, (int)rc, get_url_list[i].ucode);
856       error++;
857     }
858     curl_url_cleanup(urlp);
859   }
860   return error;
861 }
862 
get_parts(void)863 static int get_parts(void)
864 {
865   int i;
866   int error = 0;
867   for(i = 0; get_parts_list[i].in && !error; i++) {
868     CURLUcode rc;
869     CURLU *urlp = curl_url();
870     if(!urlp) {
871       error++;
872       break;
873     }
874     rc = curl_url_set(urlp, CURLUPART_URL,
875                       get_parts_list[i].in,
876                       get_parts_list[i].urlflags);
877     if(rc != get_parts_list[i].ucode) {
878       fprintf(stderr, "Get parts\nin: %s\nreturned %d (expected %d)\n",
879               get_parts_list[i].in, (int)rc, get_parts_list[i].ucode);
880       error++;
881     }
882     else if(get_parts_list[i].ucode) {
883       /* the expected error happened */
884     }
885     else if(checkparts(urlp, get_parts_list[i].in, get_parts_list[i].out,
886                        get_parts_list[i].getflags))
887       error++;
888     curl_url_cleanup(urlp);
889   }
890   return error;
891 }
892 
893 static struct querycase append_list[] = {
894   {"HTTP://test/?s", "name=joe\x02", "http://test/?s&name=joe%02",
895    0, CURLU_URLENCODE, CURLUE_OK},
896   {"HTTP://test/?size=2#f", "name=joe=", "http://test/?size=2&name=joe%3d#f",
897    0, CURLU_URLENCODE, CURLUE_OK},
898   {"HTTP://test/?size=2#f", "name=joe doe",
899    "http://test/?size=2&name=joe+doe#f",
900    0, CURLU_URLENCODE, CURLUE_OK},
901   {"HTTP://test/", "name=joe", "http://test/?name=joe", 0, 0, CURLUE_OK},
902   {"HTTP://test/?size=2", "name=joe", "http://test/?size=2&name=joe",
903    0, 0, CURLUE_OK},
904   {"HTTP://test/?size=2&", "name=joe", "http://test/?size=2&name=joe",
905    0, 0, CURLUE_OK},
906   {"HTTP://test/?size=2#f", "name=joe", "http://test/?size=2&name=joe#f",
907    0, 0, CURLUE_OK},
908   {NULL, NULL, NULL, 0, 0, 0}
909 };
910 
append(void)911 static int append(void)
912 {
913   int i;
914   int error = 0;
915   for(i = 0; append_list[i].in && !error; i++) {
916     CURLUcode rc;
917     CURLU *urlp = curl_url();
918     if(!urlp) {
919       error++;
920       break;
921     }
922     rc = curl_url_set(urlp, CURLUPART_URL,
923                       append_list[i].in,
924                       append_list[i].urlflags);
925     if(rc)
926       error++;
927     else
928       rc = curl_url_set(urlp, CURLUPART_QUERY,
929                         append_list[i].q,
930                         append_list[i].qflags | CURLU_APPENDQUERY);
931     if(error)
932       ;
933     else if(rc != append_list[i].ucode) {
934       fprintf(stderr, "Append\nin: %s\nreturned %d (expected %d)\n",
935               append_list[i].in, (int)rc, append_list[i].ucode);
936       error++;
937     }
938     else if(append_list[i].ucode) {
939       /* the expected error happened */
940     }
941     else {
942       char *url;
943       rc = curl_url_get(urlp, CURLUPART_URL, &url, 0);
944       if(rc) {
945         fprintf(stderr, "%s:%d Get URL returned %d\n",
946                 __FILE__, __LINE__, (int)rc);
947         error++;
948       }
949       else {
950         if(checkurl(url, append_list[i].out)) {
951           error++;
952         }
953         curl_free(url);
954       }
955     }
956     curl_url_cleanup(urlp);
957   }
958   return error;
959 }
960 
scopeid(void)961 static int scopeid(void)
962 {
963   CURLU *u = curl_url();
964   int error = 0;
965   CURLUcode rc;
966   char *url;
967 
968   rc = curl_url_set(u, CURLUPART_URL,
969                     "https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0);
970   if(rc != CURLUE_OK) {
971     fprintf(stderr, "%s:%d curl_url_set returned %d\n",
972             __FILE__, __LINE__, (int)rc);
973     error++;
974   }
975 
976   rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
977   if(rc != CURLUE_OK) {
978     fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d\n",
979             __FILE__, __LINE__, (int)rc);
980     error++;
981   }
982   else {
983     printf("we got %s\n", url);
984     curl_free(url);
985   }
986 
987   rc = curl_url_set(u, CURLUPART_HOST, "[::1]", 0);
988   if(rc != CURLUE_OK) {
989     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
990             __FILE__, __LINE__, (int)rc);
991     error++;
992   }
993 
994   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
995   if(rc != CURLUE_OK) {
996     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
997             __FILE__, __LINE__, (int)rc);
998     error++;
999   }
1000   else {
1001     printf("we got %s\n", url);
1002     curl_free(url);
1003   }
1004 
1005   rc = curl_url_set(u, CURLUPART_HOST, "example.com", 0);
1006   if(rc != CURLUE_OK) {
1007     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
1008             __FILE__, __LINE__, (int)rc);
1009     error++;
1010   }
1011 
1012   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1013   if(rc != CURLUE_OK) {
1014     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
1015             __FILE__, __LINE__, (int)rc);
1016     error++;
1017   }
1018   else {
1019     printf("we got %s\n", url);
1020     curl_free(url);
1021   }
1022 
1023   rc = curl_url_set(u, CURLUPART_HOST,
1024                     "[fe80::20c:29ff:fe9c:409b%25eth0]", 0);
1025   if(rc != CURLUE_OK) {
1026     fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
1027             __FILE__, __LINE__, (int)rc);
1028     error++;
1029   }
1030 
1031   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1032   if(rc != CURLUE_OK) {
1033     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
1034             __FILE__, __LINE__, (int)rc);
1035     error++;
1036   }
1037   else {
1038     printf("we got %s\n", url);
1039     curl_free(url);
1040   }
1041 
1042   rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
1043   if(rc != CURLUE_OK) {
1044     fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d\n",
1045             __FILE__, __LINE__, (int)rc);
1046     error++;
1047   }
1048   else {
1049     printf("we got %s\n", url);
1050     curl_free(url);
1051   }
1052 
1053   rc = curl_url_get(u, CURLUPART_ZONEID, &url, 0);
1054   if(rc != CURLUE_OK) {
1055     fprintf(stderr, "%s:%d curl_url_get CURLUPART_ZONEID returned %d\n",
1056             __FILE__, __LINE__, (int)rc);
1057     error++;
1058   }
1059   else {
1060     printf("we got %s\n", url);
1061     curl_free(url);
1062   }
1063 
1064   rc = curl_url_set(u, CURLUPART_ZONEID, "clown", 0);
1065   if(rc != CURLUE_OK) {
1066     fprintf(stderr, "%s:%d curl_url_set CURLUPART_ZONEID returned %d\n",
1067             __FILE__, __LINE__, (int)rc);
1068     error++;
1069   }
1070 
1071   rc = curl_url_get(u, CURLUPART_URL, &url, 0);
1072   if(rc != CURLUE_OK) {
1073     fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
1074             __FILE__, __LINE__, (int)rc);
1075     error++;
1076   }
1077   else {
1078     printf("we got %s\n", url);
1079     curl_free(url);
1080   }
1081 
1082   curl_url_cleanup(u);
1083 
1084   return error;
1085 }
1086 
test(char * URL)1087 int test(char *URL)
1088 {
1089   (void)URL; /* not used */
1090 
1091   if(scopeid())
1092     return 6;
1093 
1094   if(append())
1095     return 5;
1096 
1097   if(set_url())
1098     return 1;
1099 
1100   if(set_parts())
1101     return 2;
1102 
1103   if(get_url())
1104     return 3;
1105 
1106   if(get_parts())
1107     return 4;
1108 
1109   printf("success\n");
1110   return 0;
1111 }
1112