in libs/curl/lib/urlapi.c [1417:1706]
CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
char **part, unsigned int flags)
{
const char *ptr;
CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
char portbuf[7];
bool urldecode = (flags & CURLU_URLDECODE)?1:0;
bool urlencode = (flags & CURLU_URLENCODE)?1:0;
bool punycode = FALSE;
bool depunyfy = FALSE;
bool plusdecode = FALSE;
(void)flags;
if(!u)
return CURLUE_BAD_HANDLE;
if(!part)
return CURLUE_BAD_PARTPOINTER;
*part = NULL;
switch(what) {
case CURLUPART_SCHEME:
ptr = u->scheme;
ifmissing = CURLUE_NO_SCHEME;
urldecode = FALSE; /* never for schemes */
if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme)
return CURLUE_NO_SCHEME;
break;
case CURLUPART_USER:
ptr = u->user;
ifmissing = CURLUE_NO_USER;
break;
case CURLUPART_PASSWORD:
ptr = u->password;
ifmissing = CURLUE_NO_PASSWORD;
break;
case CURLUPART_OPTIONS:
ptr = u->options;
ifmissing = CURLUE_NO_OPTIONS;
break;
case CURLUPART_HOST:
ptr = u->host;
ifmissing = CURLUE_NO_HOST;
punycode = (flags & CURLU_PUNYCODE)?1:0;
depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
break;
case CURLUPART_ZONEID:
ptr = u->zoneid;
ifmissing = CURLUE_NO_ZONEID;
break;
case CURLUPART_PORT:
ptr = u->port;
ifmissing = CURLUE_NO_PORT;
urldecode = FALSE; /* never for port */
if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
/* there is no stored port number, but asked to deliver
a default one for the scheme */
const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
if(h) {
msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
ptr = portbuf;
}
}
else if(ptr && u->scheme) {
/* there is a stored port number, but ask to inhibit if
it matches the default one for the scheme */
const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
if(h && (h->defport == u->portnum) &&
(flags & CURLU_NO_DEFAULT_PORT))
ptr = NULL;
}
break;
case CURLUPART_PATH:
ptr = u->path;
if(!ptr)
ptr = "/";
break;
case CURLUPART_QUERY:
ptr = u->query;
ifmissing = CURLUE_NO_QUERY;
plusdecode = urldecode;
if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY))
/* there was a blank query and the user do not ask for it */
ptr = NULL;
break;
case CURLUPART_FRAGMENT:
ptr = u->fragment;
ifmissing = CURLUE_NO_FRAGMENT;
if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY)
/* there was a blank fragment and the user asks for it */
ptr = "";
break;
case CURLUPART_URL: {
char *url;
char *scheme;
char *options = u->options;
char *port = u->port;
char *allochost = NULL;
bool show_fragment =
u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY);
bool show_query =
(u->query && u->query[0]) ||
(u->query_present && flags & CURLU_GET_EMPTY);
punycode = (flags & CURLU_PUNYCODE)?1:0;
depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
if(u->scheme && strcasecompare("file", u->scheme)) {
url = aprintf("file://%s%s%s",
u->path,
show_fragment ? "#": "",
u->fragment ? u->fragment : "");
}
else if(!u->host)
return CURLUE_NO_HOST;
else {
const struct Curl_handler *h = NULL;
char schemebuf[MAX_SCHEME_LEN + 5];
if(u->scheme)
scheme = u->scheme;
else if(flags & CURLU_DEFAULT_SCHEME)
scheme = (char *) DEFAULT_SCHEME;
else
return CURLUE_NO_SCHEME;
h = Curl_get_scheme_handler(scheme);
if(!port && (flags & CURLU_DEFAULT_PORT)) {
/* there is no stored port number, but asked to deliver
a default one for the scheme */
if(h) {
msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
port = portbuf;
}
}
else if(port) {
/* there is a stored port number, but asked to inhibit if it matches
the default one for the scheme */
if(h && (h->defport == u->portnum) &&
(flags & CURLU_NO_DEFAULT_PORT))
port = NULL;
}
if(h && !(h->flags & PROTOPT_URLOPTIONS))
options = NULL;
if(u->host[0] == '[') {
if(u->zoneid) {
/* make it '[ host %25 zoneid ]' */
struct dynbuf enc;
size_t hostlen = strlen(u->host);
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
u->zoneid))
return CURLUE_OUT_OF_MEMORY;
allochost = Curl_dyn_ptr(&enc);
}
}
else if(urlencode) {
allochost = curl_easy_escape(NULL, u->host, 0);
if(!allochost)
return CURLUE_OUT_OF_MEMORY;
}
else if(punycode) {
if(!Curl_is_ASCII_name(u->host)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
CURLcode result = Curl_idn_decode(u->host, &allochost);
if(result)
return (result == CURLE_OUT_OF_MEMORY) ?
CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
#endif
}
}
else if(depunyfy) {
if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
CURLcode result = Curl_idn_encode(u->host, &allochost);
if(result)
/* this is the most likely error */
return (result == CURLE_OUT_OF_MEMORY) ?
CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
#endif
}
}
if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme)
msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme);
else
schemebuf[0] = 0;
url = aprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
schemebuf,
u->user ? u->user : "",
u->password ? ":": "",
u->password ? u->password : "",
options ? ";" : "",
options ? options : "",
(u->user || u->password || options) ? "@": "",
allochost ? allochost : u->host,
port ? ":": "",
port ? port : "",
u->path ? u->path : "/",
show_query ? "?": "",
u->query ? u->query : "",
show_fragment ? "#": "",
u->fragment? u->fragment : "");
free(allochost);
}
if(!url)
return CURLUE_OUT_OF_MEMORY;
*part = url;
return CURLUE_OK;
}
default:
ptr = NULL;
break;
}
if(ptr) {
size_t partlen = strlen(ptr);
size_t i = 0;
*part = Curl_memdup0(ptr, partlen);
if(!*part)
return CURLUE_OUT_OF_MEMORY;
if(plusdecode) {
/* convert + to space */
char *plus = *part;
for(i = 0; i < partlen; ++plus, i++) {
if(*plus == '+')
*plus = ' ';
}
}
if(urldecode) {
char *decoded;
size_t dlen;
/* this unconditional rejection of control bytes is documented
API behavior */
CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL);
free(*part);
if(res) {
*part = NULL;
return CURLUE_URLDECODE;
}
*part = decoded;
partlen = dlen;
}
if(urlencode) {
struct dynbuf enc;
CURLUcode uc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY);
if(uc)
return uc;
free(*part);
*part = Curl_dyn_ptr(&enc);
}
else if(punycode) {
if(!Curl_is_ASCII_name(u->host)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
char *allochost;
CURLcode result = Curl_idn_decode(*part, &allochost);
if(result)
return (result == CURLE_OUT_OF_MEMORY) ?
CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
free(*part);
*part = allochost;
#endif
}
}
else if(depunyfy) {
if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
char *allochost;
CURLcode result = Curl_idn_encode(*part, &allochost);
if(result)
return (result == CURLE_OUT_OF_MEMORY) ?
CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
free(*part);
*part = allochost;
#endif
}
}
return CURLUE_OK;
}
else
return ifmissing;
}