CURLUcode curl_url_get()

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;
}