urlapi: have *set(PATH) prepend a slash if one is missing

Previously the code would just do that for the path when extracting the
full URL, which made a subsequent curl_url_get() of the path to
(unexpectedly) still return it without the leading path.

Amend lib1560 to verify this. Clarify the curl_url_set() docs about it.

Bug: https://curl.se/mail/lib-2023-06/0015.html
Closes #11272
Reported-by: Pedro Henrique
This commit is contained in:
Daniel Stenberg 2023-06-08 13:15:09 +02:00
parent 7d62f0d9b8
commit 3c9256c8a0
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
3 changed files with 112 additions and 20 deletions

View file

@ -1547,7 +1547,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
}
}
url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
scheme,
u->user ? u->user : "",
u->password ? ":": "",
@ -1558,7 +1558,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
allochost ? allochost : u->host,
port ? ":": "",
port ? port : "",
(u->path && (u->path[0] != '/')) ? "/": "",
u->path ? u->path : "/",
(u->query && u->query[0]) ? "?": "",
(u->query && u->query[0]) ? u->query : "",
@ -1640,6 +1639,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
bool plusencode = FALSE;
bool urlskipslash = FALSE;
bool leadingslash = FALSE;
bool appendquery = FALSE;
bool equalsencode = FALSE;
@ -1751,6 +1751,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
break;
case CURLUPART_PATH:
urlskipslash = TRUE;
leadingslash = TRUE; /* enforce */
storep = &u->path;
break;
case CURLUPART_QUERY:
@ -1801,16 +1802,21 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
{
const char *newp = part;
size_t nalloc = strlen(part);
struct dynbuf enc;
if(nalloc > CURL_MAX_INPUT_LENGTH)
/* excessive input length */
return CURLUE_MALFORMED_INPUT;
Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
if(leadingslash && (part[0] != '/')) {
CURLcode result = Curl_dyn_addn(&enc, "/", 1);
if(result)
return CURLUE_OUT_OF_MEMORY;
}
if(urlencode) {
const unsigned char *i;
struct dynbuf enc;
Curl_dyn_init(&enc, nalloc * 3 + 1);
for(i = (const unsigned char *)part; *i; i++) {
CURLcode result;
@ -1838,14 +1844,13 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
return CURLUE_OUT_OF_MEMORY;
}
}
newp = Curl_dyn_ptr(&enc);
}
else {
char *p;
newp = strdup(part);
if(!newp)
CURLcode result = Curl_dyn_add(&enc, part);
if(result)
return CURLUE_OUT_OF_MEMORY;
p = (char *)newp;
p = Curl_dyn_ptr(&enc);
while(*p) {
/* make sure percent encoded are lower case */
if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
@ -1858,6 +1863,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
p++;
}
}
newp = Curl_dyn_ptr(&enc);
if(appendquery) {
/* Append the 'newp' string onto the old query. Add a '&' separator if
@ -1866,24 +1872,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
size_t querylen = u->query ? strlen(u->query) : 0;
bool addamperand = querylen && (u->query[querylen -1] != '&');
if(querylen) {
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
struct dynbuf qbuf;
Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */
if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
goto nomem;
if(addamperand) {
if(Curl_dyn_addn(&enc, "&", 1))
if(Curl_dyn_addn(&qbuf, "&", 1))
goto nomem;
}
if(Curl_dyn_add(&enc, newp))
if(Curl_dyn_add(&qbuf, newp))
goto nomem;
free((char *)newp);
Curl_dyn_free(&enc);
free(*storep);
*storep = Curl_dyn_ptr(&enc);
*storep = Curl_dyn_ptr(&qbuf);
return CURLUE_OK;
nomem:
free((char *)newp);
Curl_dyn_free(&enc);
return CURLUE_OUT_OF_MEMORY;
}
}
@ -1895,7 +1901,7 @@ nomem:
}
else {
if(!n || hostname_check(u, (char *)newp, n)) {
free((char *)newp);
Curl_dyn_free(&enc);
return CURLUE_BAD_HOSTNAME;
}
}