in libs/curl/lib/urlapi.c [1708:2005]
CURLUcode curl_url_set(CURLU *u, CURLUPart what,
const char *part, unsigned int flags)
{
char **storep = NULL;
bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
bool plusencode = FALSE;
bool urlskipslash = FALSE;
bool leadingslash = FALSE;
bool appendquery = FALSE;
bool equalsencode = FALSE;
size_t nalloc;
if(!u)
return CURLUE_BAD_HANDLE;
if(!part) {
/* setting a part to NULL clears it */
switch(what) {
case CURLUPART_URL:
break;
case CURLUPART_SCHEME:
storep = &u->scheme;
u->guessed_scheme = FALSE;
break;
case CURLUPART_USER:
storep = &u->user;
break;
case CURLUPART_PASSWORD:
storep = &u->password;
break;
case CURLUPART_OPTIONS:
storep = &u->options;
break;
case CURLUPART_HOST:
storep = &u->host;
break;
case CURLUPART_ZONEID:
storep = &u->zoneid;
break;
case CURLUPART_PORT:
u->portnum = 0;
storep = &u->port;
break;
case CURLUPART_PATH:
storep = &u->path;
break;
case CURLUPART_QUERY:
storep = &u->query;
u->query_present = FALSE;
break;
case CURLUPART_FRAGMENT:
storep = &u->fragment;
u->fragment_present = FALSE;
break;
default:
return CURLUE_UNKNOWN_PART;
}
if(storep && *storep) {
Curl_safefree(*storep);
}
else if(!storep) {
free_urlhandle(u);
memset(u, 0, sizeof(struct Curl_URL));
}
return CURLUE_OK;
}
nalloc = strlen(part);
if(nalloc > CURL_MAX_INPUT_LENGTH)
/* excessive input length */
return CURLUE_MALFORMED_INPUT;
switch(what) {
case CURLUPART_SCHEME: {
size_t plen = strlen(part);
const char *s = part;
if((plen > MAX_SCHEME_LEN) || (plen < 1))
/* too long or too short */
return CURLUE_BAD_SCHEME;
/* verify that it is a fine scheme */
if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
return CURLUE_UNSUPPORTED_SCHEME;
storep = &u->scheme;
urlencode = FALSE; /* never */
if(ISALPHA(*s)) {
/* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
while(--plen) {
if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
s++; /* fine */
else
return CURLUE_BAD_SCHEME;
}
}
else
return CURLUE_BAD_SCHEME;
u->guessed_scheme = FALSE;
break;
}
case CURLUPART_USER:
storep = &u->user;
break;
case CURLUPART_PASSWORD:
storep = &u->password;
break;
case CURLUPART_OPTIONS:
storep = &u->options;
break;
case CURLUPART_HOST:
storep = &u->host;
Curl_safefree(u->zoneid);
break;
case CURLUPART_ZONEID:
storep = &u->zoneid;
break;
case CURLUPART_PORT:
if(!ISDIGIT(part[0]))
/* not a number */
return CURLUE_BAD_PORT_NUMBER;
else {
char *tmp;
char *endp;
unsigned long port;
errno = 0;
port = strtoul(part, &endp, 10); /* must be decimal */
if(errno || (port > 0xffff) || *endp)
/* weirdly provided number, not good! */
return CURLUE_BAD_PORT_NUMBER;
tmp = strdup(part);
if(!tmp)
return CURLUE_OUT_OF_MEMORY;
free(u->port);
u->port = tmp;
u->portnum = (unsigned short)port;
return CURLUE_OK;
}
case CURLUPART_PATH:
urlskipslash = TRUE;
leadingslash = TRUE; /* enforce */
storep = &u->path;
break;
case CURLUPART_QUERY:
plusencode = urlencode;
appendquery = (flags & CURLU_APPENDQUERY)?1:0;
equalsencode = appendquery;
storep = &u->query;
u->query_present = TRUE;
break;
case CURLUPART_FRAGMENT:
storep = &u->fragment;
u->fragment_present = TRUE;
break;
case CURLUPART_URL: {
/*
* Allow a new URL to replace the existing (if any) contents.
*
* If the existing contents is enough for a URL, allow a relative URL to
* replace it.
*/
CURLcode result;
CURLUcode uc;
char *oldurl;
char *redired_url;
if(!nalloc)
/* a blank URL is not a valid URL */
return CURLUE_MALFORMED_INPUT;
/* if the new thing is absolute or the old one is not
* (we could not get an absolute URL in 'oldurl'),
* then replace the existing with the new. */
if(Curl_is_absolute_url(part, NULL, 0,
flags & (CURLU_GUESS_SCHEME|
CURLU_DEFAULT_SCHEME))
|| curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
return parseurl_and_replace(part, u, flags);
}
/* apply the relative part to create a new URL
* and replace the existing one with it. */
result = concat_url(oldurl, part, &redired_url);
free(oldurl);
if(result)
return cc2cu(result);
uc = parseurl_and_replace(redired_url, u, flags);
free(redired_url);
return uc;
}
default:
return CURLUE_UNKNOWN_PART;
}
DEBUGASSERT(storep);
{
const char *newp;
struct dynbuf enc;
Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
if(leadingslash && (part[0] != '/')) {
CURLcode result = Curl_dyn_addn(&enc, "/", 1);
if(result)
return cc2cu(result);
}
if(urlencode) {
const unsigned char *i;
for(i = (const unsigned char *)part; *i; i++) {
CURLcode result;
if((*i == ' ') && plusencode) {
result = Curl_dyn_addn(&enc, "+", 1);
if(result)
return CURLUE_OUT_OF_MEMORY;
}
else if(ISUNRESERVED(*i) ||
((*i == '/') && urlskipslash) ||
((*i == '=') && equalsencode)) {
if((*i == '=') && equalsencode)
/* only skip the first equals sign */
equalsencode = FALSE;
result = Curl_dyn_addn(&enc, i, 1);
if(result)
return cc2cu(result);
}
else {
char out[3]={'%'};
out[1] = hexdigits[*i>>4];
out[2] = hexdigits[*i & 0xf];
result = Curl_dyn_addn(&enc, out, 3);
if(result)
return cc2cu(result);
}
}
}
else {
char *p;
CURLcode result = Curl_dyn_add(&enc, part);
if(result)
return cc2cu(result);
p = Curl_dyn_ptr(&enc);
while(*p) {
/* make sure percent encoded are lower case */
if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
(ISUPPER(p[1]) || ISUPPER(p[2]))) {
p[1] = Curl_raw_tolower(p[1]);
p[2] = Curl_raw_tolower(p[2]);
p += 3;
}
else
p++;
}
}
newp = Curl_dyn_ptr(&enc);
if(appendquery && newp) {
/* Append the 'newp' string onto the old query. Add a '&' separator if
none is present at the end of the existing query already */
size_t querylen = u->query ? strlen(u->query) : 0;
bool addamperand = querylen && (u->query[querylen -1] != '&');
if(querylen) {
struct dynbuf qbuf;
Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
goto nomem;
if(addamperand) {
if(Curl_dyn_addn(&qbuf, "&", 1))
goto nomem;
}
if(Curl_dyn_add(&qbuf, newp))
goto nomem;
Curl_dyn_free(&enc);
free(*storep);
*storep = Curl_dyn_ptr(&qbuf);
return CURLUE_OK;
nomem:
Curl_dyn_free(&enc);
return CURLUE_OUT_OF_MEMORY;
}
}
else if(what == CURLUPART_HOST) {
size_t n = Curl_dyn_len(&enc);
if(!n && (flags & CURLU_NO_AUTHORITY)) {
/* Skip hostname check, it is allowed to be empty. */
}
else {
if(!n || hostname_check(u, (char *)newp, n)) {
Curl_dyn_free(&enc);
return CURLUE_BAD_HOSTNAME;
}
}
}
free(*storep);
*storep = (char *)newp;
}
return CURLUE_OK;
}