static void write_linked_location()

in libs/curl/src/tool_cb_hdr.c [63:273]


static void write_linked_location(CURL *curl, const char *location,
    size_t loclen, FILE *stream);
#endif

/*
** callback for CURLOPT_HEADERFUNCTION
*/

size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
{
  struct per_transfer *per = userdata;
  struct HdrCbData *hdrcbdata = &per->hdrcbdata;
  struct OutStruct *outs = &per->outs;
  struct OutStruct *heads = &per->heads;
  struct OutStruct *etag_save = &per->etag_save;
  const char *str = ptr;
  const size_t cb = size * nmemb;
  const char *end = (char *)ptr + cb;
  const char *scheme = NULL;

  if(!per->config)
    return CURL_WRITEFUNC_ERROR;

#ifdef DEBUGBUILD
  if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) {
    warnf(per->config->global, "Header data exceeds single call write limit");
    return CURL_WRITEFUNC_ERROR;
  }
#endif

#ifdef _WIN32
  /* Discard incomplete UTF-8 sequence buffered from body */
  if(outs->utf8seq[0])
    memset(outs->utf8seq, 0, sizeof(outs->utf8seq));
#endif

  /*
   * Write header data when curl option --dump-header (-D) is given.
   */

  if(per->config->headerfile && heads->stream) {
    size_t rc = fwrite(ptr, size, nmemb, heads->stream);
    if(rc != cb)
      return rc;
    /* flush the stream to send off what we got earlier */
    if(fflush(heads->stream)) {
      errorf(per->config->global, "Failed writing headers to %s",
             per->config->headerfile);
      return CURL_WRITEFUNC_ERROR;
    }
  }

  curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme);
  scheme = proto_token(scheme);
  if((scheme == proto_http || scheme == proto_https)) {
    long response = 0;
    curl_easy_getinfo(per->curl, CURLINFO_RESPONSE_CODE, &response);

    if((response/100 != 2) && (response/100 != 3))
      /* only care about etag and content-disposition headers in 2xx and 3xx
         responses */
      ;
    /*
     * Write etag to file when --etag-save option is given.
     */
    else if(per->config->etag_save_file && etag_save->stream &&
            /* match only header that start with etag (case insensitive) */
            checkprefix("etag:", str)) {
      const char *etag_h = &str[5];
      const char *eot = end - 1;
      if(*eot == '\n') {
        while(ISBLANK(*etag_h) && (etag_h < eot))
          etag_h++;
        while(ISSPACE(*eot))
          eot--;

        if(eot >= etag_h) {
          size_t etag_length = eot - etag_h + 1;
          /*
           * Truncate the etag save stream, it can have an existing etag value.
           */
#ifdef HAVE_FTRUNCATE
          if(ftruncate(fileno(etag_save->stream), 0)) {
            return CURL_WRITEFUNC_ERROR;
          }
#else
          if(fseek(etag_save->stream, 0, SEEK_SET)) {
            return CURL_WRITEFUNC_ERROR;
          }
#endif

          fwrite(etag_h, size, etag_length, etag_save->stream);
          /* terminate with newline */
          fputc('\n', etag_save->stream);
          (void)fflush(etag_save->stream);
        }
      }
    }

    /*
     * This callback sets the filename where output shall be written when
     * curl options --remote-name (-O) and --remote-header-name (-J) have
     * been simultaneously given and additionally server returns an HTTP
     * Content-Disposition header specifying a filename property.
     */

    else if(hdrcbdata->honor_cd_filename &&
            (cb > 20) && checkprefix("Content-disposition:", str)) {
      const char *p = str + 20;

      /* look for the 'filename=' parameter
         (encoded filenames (*=) are not supported) */
      for(;;) {
        char *filename;
        size_t len;

        while((p < end) && *p && !ISALPHA(*p))
          p++;
        if(p > end - 9)
          break;

        if(memcmp(p, "filename=", 9)) {
          /* no match, find next parameter */
          while((p < end) && *p && (*p != ';'))
            p++;
          if((p < end) && *p)
            continue;
          else
            break;
        }
        p += 9;

        len = cb - (size_t)(p - str);
        filename = parse_filename(p, len);
        if(filename) {
          if(outs->stream) {
            /* indication of problem, get out! */
            free(filename);
            return CURL_WRITEFUNC_ERROR;
          }

          if(per->config->output_dir) {
            outs->filename = aprintf("%s/%s", per->config->output_dir,
                                     filename);
            free(filename);
            if(!outs->filename)
              return CURL_WRITEFUNC_ERROR;
          }
          else
            outs->filename = filename;

          outs->is_cd_filename = TRUE;
          outs->s_isreg = TRUE;
          outs->fopened = FALSE;
          outs->alloc_filename = TRUE;
          hdrcbdata->honor_cd_filename = FALSE; /* done now! */
          if(!tool_create_output_file(outs, per->config))
            return CURL_WRITEFUNC_ERROR;
        }
        break;
      }
      if(!outs->stream && !tool_create_output_file(outs, per->config))
        return CURL_WRITEFUNC_ERROR;
    }
  }
  if(hdrcbdata->config->writeout) {
    char *value = memchr(ptr, ':', cb);
    if(value) {
      if(per->was_last_header_empty)
        per->num_headers = 0;
      per->was_last_header_empty = FALSE;
      per->num_headers++;
    }
    else if(ptr[0] == '\r' || ptr[0] == '\n')
      per->was_last_header_empty = TRUE;
  }
  if(hdrcbdata->config->show_headers &&
    (scheme == proto_http || scheme == proto_https ||
     scheme == proto_rtsp || scheme == proto_file)) {
    /* bold headers only for selected protocols */
    char *value = NULL;

    if(!outs->stream && !tool_create_output_file(outs, per->config))
      return CURL_WRITEFUNC_ERROR;

    if(hdrcbdata->global->isatty &&
#ifdef _WIN32
       tool_term_has_bold &&
#endif
       hdrcbdata->global->styled_output)
      value = memchr(ptr, ':', cb);
    if(value) {
      size_t namelen = value - ptr;
      fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", (int)namelen, ptr);
#ifndef LINK
      fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
#else
      if(curl_strnequal("Location", ptr, namelen)) {
        write_linked_location(per->curl, &value[1], cb - namelen - 1,
            outs->stream);
      }
      else
        fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
#endif
    }
    else
      /* not "handled", just show it */
      fwrite(ptr, cb, 1, outs->stream);
  }
  return cb;
}