Skip to content
Snippets Groups Projects
main.c 89.3 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    config->lastsendtime = now;

    if(size*nmemb > config->sendpersecond) {
      /* lower the size to actually read */
      nmemb = config->sendpersecond;
      size = 1;
    }
    config->lastsendsize = size*nmemb;    
  }


  return fread(buffer, size, nmemb, in->stream);
}

  FILE *out; /* where to write everything to */
                double dltotal,
                double dlnow,
                double ultotal,
                double ulnow)
{
  /* The original progress-bar source code was written for curl by Lars Aas,
     and this new edition inherites some of his concepts. */
  
  char line[256];
  char outline[256];
  char format[40];
  int barwidth;
  int num;
  int i;

  struct ProgressData *bar = (struct ProgressData *)clientp;
  double total = dltotal + ultotal;

  bar->point = dlnow + ulnow; /* we've come this far */

    int prevblock = (int)bar->prev / 1024;
    int thisblock = (int)bar->point / 1024;
      fprintf( bar->out, "#" );
    frac = bar->point / total;
    percent = frac * 100.0f;
    barwidth = bar->width - 7;
    num = (int) (((double)barwidth) * frac);
    i = 0;
    for ( i = 0; i < num; i++ ) {
      line[i] = '#';
    }
    line[i] = '\0';
    sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
    sprintf( outline, format, line, percent );
    fprintf( bar->out, "\r%s", outline );
static
void progressbarinit(struct ProgressData *bar,
                     struct Configurable *config)
{
#ifdef __EMX__
  /* 20000318 mgs */
  int scr_size [2];
#endif
  char *colp;

  memset(bar, 0, sizeof(struct ProgressData));

/* TODO: get terminal width through ansi escapes or something similar.
         try to update width when xterm is resized... - 19990617 larsa */
#ifndef __EMX__
  /* 20000318 mgs
   * OS/2 users most likely won't have this env var set, and besides that
   * we're using our own way to determine screen width */
  colp = curl_getenv("COLUMNS");
  if (colp != NULL) {
    bar->width = atoi(colp);
    free(colp);
  }
  else
    bar->width = 79;
#else
  /* 20000318 mgs
   * We use this emx library call to get the screen width, and subtract
   * one from what we got in order to avoid a problem with the cursor
   * advancing to the next line if we print a string that is as long as
   * the screen is wide. */
 
  _scrsize(scr_size);
  bar->width = scr_size[0] - 1;
#endif

  bar->out = config->errors;
Daniel Stenberg's avatar
Daniel Stenberg committed

          FILE *stream, unsigned char *ptr, size_t size,
          bool nohex)
  unsigned int width=0x10;

  if(nohex)
    /* without the hex output, we can fit more on screen */
    width = 0x40;
  fprintf(stream, "%s, %d bytes (0x%x)\n", text, size, size);
  for(i=0; i<size; i+= width) {
    if(!nohex) {
      /* hex not disabled, show it */
      for(c = 0; c < width; c++)
        if(i+c < size)
          fprintf(stream, "%02x ", ptr[i+c]);
        else
          fputs("   ", stream);
    }

    for(c = 0; (c < width) && (i+c < size); c++) {
      /* check for 0D0A; if found, skip past and start a new line of output */
      if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
        i+=(c+2-width);
        break;
      }
      fprintf(stream, "%c",
              (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
      /* check again for 0D0A, to avoid an extra \n if it's at width */
      if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
        i+=(c+3-width);
        break;
      }
    }
  fflush(stream);
}

static
int my_trace(CURL *handle, curl_infotype type,
             unsigned char *data, size_t size,
             void *userp)
{
  struct Configurable *config = (struct Configurable *)userp;
  FILE *output=config->errors;
  const char *text;
  if(!config->trace_stream) {
    if(strequal("-", config->trace_dump))
      config->trace_stream = stdout;
    else {
      config->trace_stream = fopen(config->trace_dump, "w");
      config->trace_fopened = TRUE;
    }
  }

  if(config->trace_stream)
    output = config->trace_stream;

  switch (type) {
  case CURLINFO_TEXT:
    fprintf(output, "== Info: %s", data);
  default: /* in case a new one is introduced to shock us */
    return 0;

    text = "=> Send header";
    text = "=> Send data";
    text = "<= Recv header";
    text = "<= Recv data";

  dump(text, output, data, size, config->trace_ascii);
void free_config_fields(struct Configurable *config)
  if(config->random_file)
    free(config->random_file);
  if(config->egd_file)
    free(config->egd_file);
  if(config->userpwd)
    free(config->userpwd);
  if(config->postfields)
    free(config->postfields);
  if(config->proxy)
    free(config->proxy);
  if(config->proxyuserpwd)
    free(config->proxyuserpwd);
  if(config->cookie)
    free(config->cookie);
  if(config->cookiefile)
    free(config->cookiefile);
  if(config->krb4level)
    free(config->krb4level);
  if(config->headerfile)
    free(config->headerfile);
  if(config->ftpport)
    free(config->ftpport);
  if(config->infile)
    free(config->infile);
  if(config->range)
    free(config->range);
  if(config->customrequest)
    free(config->customrequest);
  if(config->writeout)
    free(config->writeout);
  if(config->httppost)
    curl_formfree(config->httppost);
  if(config->cacert)
    free(config->cacert);
  if(config->capath)
    free(config->capath);
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(config->cookiejar)
    free(config->cookiejar);

  curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
  curl_slist_free_all(config->postquote); /*  */
  curl_slist_free_all(config->headers); /*  */
#if defined(WIN32) && !defined(__CYGWIN32__)

/* Function to find CACert bundle on a Win32 platform using SearchPath.
 * (SearchPath is defined in windows.h, which is #included into libcurl)
 * (Use the ASCII version instead of the unicode one!)
 * The order of the directories it searches is:
 *  1. application's directory
 *  2. current working directory
 *  3. Windows System directory (e.g. C:\windows\system32)
 *  4. Windows Directory (e.g. C:\windows)
 *  5. all directories along %PATH%
 */
static void FindWin32CACert(struct Configurable *config, 
                            const char *bundle_file)
{
  curl_version_info_data *info;
  info = curl_version_info(CURLVERSION_NOW);

  /* only check for cert file if "we" support SSL */
  if(info->features & CURL_VERSION_SSL) {
    DWORD buflen;
    char *ptr = NULL;
    char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
    if (!retval)
      return;
    retval[0] = '\0';
    buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
    if (buflen > 0) {
      GetStr(&config->cacert, retval);
    }
    free(retval);
  }
}

#endif
operate(struct Configurable *config, int argc, char *argv[])
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  char errorbuffer[CURL_ERROR_SIZE];
  char useragent[128]; /* buah, we don't want a larger default user agent */
  struct getout *urlnode;
  struct getout *nextnode;
Daniel Stenberg's avatar
Daniel Stenberg committed

  struct OutStruct outs;
Daniel Stenberg's avatar
Daniel Stenberg committed
  struct InStruct input;
Daniel Stenberg's avatar
Daniel Stenberg committed

  char *url = NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int urlnum;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int separator = 0;
  
  FILE *infd = stdin;
  FILE *headerfilep = NULL;
  char *urlbuffer=NULL;
  int infilesize=-1; /* -1 means unknown */
  bool stillflags=TRUE;

  bool allocuseragent=FALSE;

  CURL *curl;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int i;

#ifdef MALLOCDEBUG
  /* this sends all memory debug messages to a logfile named memdump */
  env = curl_getenv("CURL_MEMDEBUG");
  if(env) {
    free(env);
    curl_memdebug("memdump");
  }
  errorbuffer[0]=0; /* prevent junk from being output */

  config->showerror=TRUE;
  config->conf=CONF_DEFAULT;
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(argc>1 &&
     (!strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
     strchr(argv[1], 'q')) {
    /*
     * The first flag, that is not a verbose name, but a shortname
     * and it includes the 'q' flag!
     */
    ;
  }
  else {
    res = parseconfig(NULL, config);
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(res)
      return res;
  }

  if ((argc < 2)  && !config->url_list) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    helpf(NULL);
    return CURLE_FAILED_INIT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }

  /* Parse options */
  for (i = 1; i < argc; i++) {
    if(stillflags &&
       ('-' == argv[i][0])) {
      char *nextarg;
      bool passarg;
Daniel Stenberg's avatar
Daniel Stenberg committed
      
Daniel Stenberg's avatar
Daniel Stenberg committed

      if(strequal("--", argv[i]))
	/* this indicates the end of the flags and thus enables the
	   following (URL) argument to start with -. */
	stillflags=FALSE;
      else {
	nextarg= (i < argc - 1)? argv[i+1]: NULL;

	res = getparameter(flag, nextarg, &passarg, config);
	if(res) {
          switch(res) {
          case PARAM_OPTION_AMBIGUOUS:
            helpf("option %s is ambiguous\n", origopt);
            break;
          case PARAM_OPTION_UNKNOWN:
            helpf("option %s is unknown\n", origopt);
            break;
          case PARAM_REQUIRES_PARAMETER:
            helpf("option %s requires an extra argument!\n", origopt);
            break;
          case PARAM_BAD_USE:
            helpf("option %s was wrongly used!\n", origopt);
            break;
          case PARAM_HELP_REQUESTED:
            /* no text */
            break;
          }
          clean_getout(config);
Daniel Stenberg's avatar
Daniel Stenberg committed

	if(passarg) /* we're supposed to skip this */
	  i++;
      }
    }
    else {
      bool used;
      /* just add the URL please */
      res = getparameter((char *)"--url", argv[i], &used, config);
      if(res)
        return res;
  if(!config->url_list) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    helpf("no URL specified!\n");
    return CURLE_FAILED_INIT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  if(NULL == config->useragent) {
    /* set non-zero default values: */
    snprintf(useragent, sizeof(useragent),
             CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
    config->useragent= useragent;
  else
    allocuseragent = TRUE;
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* On WIN32 (non-cygwin), we can't set the path to curl-ca-bundle.crt
   * at compile time. So we look here for the file in two ways:
   * 1: look at the environment variable CURL_CA_BUNDLE for a path
   * 2: if #1 isn't found, use the windows API function SearchPath()
   *    to find it along the app's path (includes app's dir and CWD)
   *
   * We support the environment variable thing for non-Windows platforms
   * too. Just for the sake of it.
   */
  if (! config->cacert) {
    env = curl_getenv("CURL_CA_BUNDLE");
    if(env) {
      GetStr(&config->cacert, env);
      free(env);
    }
  }
#if defined(WIN32) && !defined(__CYGWIN32__)
  if (! config->cacert)
    FindWin32CACert(config, "curl-ca-bundle.crt");
#endif

  if (config->postfields) {
    if (config->use_httpget) {
      /* Use the postfields data for a http get */
      httpgetfields = strdup(config->postfields);
      free(config->postfields);
      config->postfields = NULL;
      if(SetHTTPrequest((config->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET),
                        &config->httpreq)) {
        free(httpgetfields);
        return PARAM_BAD_USE;
      }
    }
    else {
      if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
        return PARAM_BAD_USE;
    }
  }

  /*
   * Get a curl handle to use for all forthcoming curl transfers.  Cleanup
   * when all transfers are done. This is supported with libcurl 7.7 and
   * should not be attempted on previous versions.
   */
  curl = curl_easy_init();
  if(!curl)
    return CURLE_FAILED_INIT;

  urlnode = config->url_list;
  if(config->headerfile) {
    /* open file for output: */
    if(strcmp(config->headerfile,"-")) {
      heads.filename = config->headerfile;
      headerfilep=NULL;
    }
    else
      headerfilep=stdout;
    heads.stream = headerfilep;
    heads.config = config;
  }

  /* loop through the list of given URLs */
  while(urlnode) {
    /* get the full URL (it might be NULL) */
    url=urlnode->url;

    if(NULL == url) {
      /* This node had no URL, skip it and continue to the next */
      if(urlnode->outfile)
        free(urlnode->outfile);
    
      /* move on to the next URL */
      nextnode=urlnode->next;
      free(urlnode); /* free the node */
      urlnode = nextnode;
      continue; /* next please */
    }

    /* default output stream is stdout */
    outs.stream = stdout;
    outs.config = config;

    if(!config->globoff) {
      /* Unless explicitly shut off, we expand '{...}' and '[...]' expressions
         and return total number of URLs in pattern set */
      res = glob_url(&urls, url, &urlnum);
      if(res != CURLE_OK)
        return res;
    }


    /* save outfile pattern before expansion */
    outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;

    if ((!outfiles || strequal(outfiles, "-")) && urlnum > 1) {
      /* multiple files extracted to stdout, insert separators! */
      separator = 1;
    for(i = 0;
        (url = urls?next_url(urls):(i?NULL:strdup(url)));
        i++) {
      char *outfile;
      outfile = outfiles?strdup(outfiles):NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
 
      if((urlnode->flags&GETOUT_USEREMOTE) ||
         (outfile && !strequal("-", outfile)) ) {

        /* 
         * We have specified a file name to store the result in, or we have
         * decided we want to use the remote file name.
         */
        if(!outfile) {
          /* Find and get the remote file name */
          char * pc =strstr(url, "://");
          if(pc)
            pc+=3;
          else
            pc=url;
          pc = strrchr(pc, '/');
          outfile = (char *) NULL == pc ? NULL : strdup(pc+1) ;
          if(!outfile) {
            helpf("Remote file name has no length!\n");
            return CURLE_WRITE_ERROR;
          }
        }
          /* fill '#1' ... '#9' terms from URL pattern */
          char *storefile = outfile;
          outfile = match_url(storefile, urls);
          free(storefile);
        /* Create the directory hierarchy, if not pre-existant to a multiple
           file output call */
        
        if(config->create_dirs)
          if (-1 == create_dir_hierarchy(outfile))
            return CURLE_WRITE_ERROR;
          /* We're told to continue from where we are now. Get the
             size of the file as it is now and open it for append instead */
          
          struct stat fileinfo;

          /*VMS?? -- Danger, the filesize is only valid for stream files */
          if(0 == stat(outfile, &fileinfo))
            /* set offset to current file size: */
            config->resume_from = fileinfo.st_size;
          else
            /* let offset be 0 */
            config->resume_from = 0;
        if(config->resume_from) {
          /* open file for output: */
          outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
          if (!outs.stream) {
            helpf("Can't open '%s'!\n", outfile);
            return CURLE_WRITE_ERROR;
          }
        else {
          outs.filename = outfile;
          outs.stream = NULL; /* open when needed */
      }
      if(config->infile) {
        /*
         * We have specified a file to upload
         */
        struct stat fileinfo;

        /* If no file name part is given in the URL, we add this file name */
        char *ptr=strstr(url, "://");
        if(ptr)
          ptr+=3;
          ptr=url;
        ptr = strrchr(ptr, '/');
        if(!ptr || !strlen(++ptr)) {
          /* The URL has no file name part, add the local file name. In order
             to be able to do so, we have to create a new URL in another
             buffer.*/

          /* We only want the part of the local path that is on the right
             side of the rightmost slash and backslash. */
          char *filep = strrchr(config->infile, '/');
          char *file2 = strrchr(filep?filep:config->infile, '\\');

          if(file2)
            filep = file2+1;
          else if(filep)
            filep++;
          else
            filep = config->infile;

          urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 3);
          if(!urlbuffer) {
            helpf("out of memory\n");
            return CURLE_OUT_OF_MEMORY;
          }
          if(ptr)
            /* there is a trailing slash on the URL */
            sprintf(urlbuffer, "%s%s", url, filep);
          else
            /* thers is no trailing slash on the URL */
            sprintf(urlbuffer, "%s/%s", url, filep);
          
          url = urlbuffer; /* use our new URL instead! */
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
/*VMS??-- Reading binary from files can be a problem... */
/*VMS??   Only FIXED, VAR etc WITHOUT implied CC will work */
/*VMS??   Others need a \n appended to a line */
/*VMS??-- Stat gives a size but this is UNRELIABLE in VMS */
/*VMS??   As a f.e. a fixed file with implied CC needs to have a byte added */
/*VMS??   for every record processed, this can by derived from Filesize & recordsize */
/*VMS??   for VARiable record files the records need to be counted! */
/*VMS??   for every record add 1 for linefeed and subtract 2 for the record header */
/*VMS??   for VARIABLE header files only the bare record data needs to be considered with one appended if implied CC */
Daniel Stenberg's avatar
Daniel Stenberg committed

        infd=(FILE *) fopen(config->infile, "rb");
        if (!infd || stat(config->infile, &fileinfo)) {
          helpf("Can't open '%s'!\n", config->infile);
          return CURLE_READ_ERROR;
        }
        infilesize=fileinfo.st_size;
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      if((config->conf&CONF_UPLOAD) &&
        config->resume_from = -1; /* -1 will then force get-it-yourself */
      }
      if(outs.stream && isatty(fileno(outs.stream)) &&
         !(config->conf&(CONF_UPLOAD|CONF_HTTPPOST)))
        /* we send the output to a tty and it isn't an upload operation,
           therefore we switch off the progress meter */
        config->conf |= CONF_NOPROGRESS;
      if (urlnum > 1 && !(config->conf&CONF_MUTE)) {
        fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
                i+1, urlnum, url, outfile ? outfile : "<stdout>");
        if (separator)
          printf("%s%s\n", CURLseparator, url);
        /* Find out whether the url contains a file name */
        char *pc =strstr(url, "://");
        pc = strrchr(pc, '/'); /* check for a slash */

        if(pc) {
          /* there is a slash present in the URL */

          if(strchr(pc, '?'))
          /* Ouch, there's already a question mark in the URL string, we
             then appead the data with an amperand separator instead! */
            separator='&';
        }
        /*
         * Then append ? followed by the get fields to the url.
         */
        urlbuffer=(char *)malloc(strlen(url) + strlen(httpgetfields) + 2);
        if(!urlbuffer) {
          helpf("out of memory\n");
          return CURLE_OUT_OF_MEMORY;
        }
          sprintf(urlbuffer, "%s%c%s", url, separator, httpgetfields);
          /* Append  / before the ? to create a well-formed url
             if the url contains a hostname only
          */
          sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
 
        free(url); /* free previous URL */
        url = urlbuffer; /* use our new URL instead! */
      }
Daniel Stenberg's avatar
Daniel Stenberg committed

      if(!config->errors)
        config->errors = stderr;
Daniel Stenberg's avatar
Daniel Stenberg committed

#if defined(WIN32) && !defined(__CYGWIN32__)
      if(!outfile && !(config->conf & CONF_GETTEXT)) {
        /* We get the output to stdout and we have not got the ASCII/text flag,
           then set stdout to be binary */
        setmode( 1, O_BINARY );
      }
      curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
      curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
Daniel Stenberg's avatar
Daniel Stenberg committed
      /* where to store */
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
      /* what call to write */
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
Daniel Stenberg's avatar
Daniel Stenberg committed

      /* for uploads */
      input.stream = infd;
      input.config = config;
      curl_easy_setopt(curl, CURLOPT_READDATA, &input);
      /* what call to read */
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread);

      if(config->recvpersecond) {
        /* tell libcurl to use a smaller sized buffer as it allows us to
           make better sleeps! 7.9.9 stuff! */
        curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
      }

      /* size of uploaded file: */
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize);
      curl_easy_setopt(curl, CURLOPT_URL, url);     /* what to fetch */
      curl_easy_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
      curl_easy_setopt(curl, CURLOPT_HEADER, config->conf&CONF_HEADER);
      curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config->conf&CONF_NOPROGRESS);
      curl_easy_setopt(curl, CURLOPT_NOBODY, config->conf&CONF_NOBODY);
      curl_easy_setopt(curl, CURLOPT_FAILONERROR,
                       config->conf&CONF_FAILONERROR);
      curl_easy_setopt(curl, CURLOPT_UPLOAD, config->conf&CONF_UPLOAD);
      curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
                       config->conf&CONF_FTPLISTONLY);
      curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);

      if (config->conf&CONF_NETRC_OPT)
        curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
      else if (config->conf&CONF_NETRC)
        curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
      else
        curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);

      curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
                       config->conf&CONF_FOLLOWLOCATION);
      curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
      curl_easy_setopt(curl, CURLOPT_MUTE, config->conf&CONF_MUTE);
      curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd);
      curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
      curl_easy_setopt(curl, CURLOPT_RANGE, config->range);
      curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
      curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
      /* new in libcurl 7.2: */
      curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
      curl_easy_setopt(curl, CURLOPT_REFERER, config->referer);
      curl_easy_setopt(curl, CURLOPT_AUTOREFERER,
                       config->conf&CONF_AUTO_REFERER);
      curl_easy_setopt(curl, CURLOPT_USERAGENT, config->useragent);
      curl_easy_setopt(curl, CURLOPT_FTPPORT, config->ftpport);
      curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit);
      curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
      curl_easy_setopt(curl, CURLOPT_RESUME_FROM,
                       config->use_resume?config->resume_from:0);
      curl_easy_setopt(curl, CURLOPT_COOKIE, config->cookie);
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
      curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
      curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
      curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
      curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key);
      curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
      curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd);
      if(config->cacert || config->capath) {
        if (config->cacert)
          curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);

        if (config->capath)
          curl_easy_setopt(curl, CURLOPT_CAPATH, config->capath);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
      else {
        if(config->insecure_ok)
          /* new stuff needed for libcurl 7.10 */
          curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);

        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
      if((config->conf&CONF_NOBODY) ||
         config->remote_time) {
        /* no body or use remote time */
        curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
      }
      
      if (config->maxredirs) 
        curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); 
      else 
        curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS); 
      curl_easy_setopt(curl, CURLOPT_CRLF, config->crlf);
      curl_easy_setopt(curl, CURLOPT_QUOTE, config->quote);
      curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
      curl_easy_setopt(curl, CURLOPT_WRITEHEADER,
                       config->headerfile?&heads:NULL);
      curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
      /* cookie jar was added in 7.9 */
      curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar);
      /* cookie session added in 7.9.7 */
      curl_easy_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);

      curl_easy_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
      curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
      curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
      curl_easy_setopt(curl, CURLOPT_STDERR, config->errors);
      /* three new ones in libcurl 7.3: */
      curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
      curl_easy_setopt(curl, CURLOPT_INTERFACE, config->iface);
      curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level);
      
      if((config->progressmode == CURL_PROGRESS_BAR) &&
         !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
        /* we want the alternative style, then we have to implement it
           ourselves! */
        progressbarinit(&progressbar, config);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
      }
      /* new in libcurl 7.6.2: */
      curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);

      /* new in libcurl 7.7: */
      curl_easy_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file);
      curl_easy_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
      curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);

      if(config->cipher_list)
        curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);

      if(config->httpversion)
        curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
Daniel Stenberg's avatar
Daniel Stenberg committed

      /* new in libcurl 7.9.2: */
      if(config->disable_epsv)
        /* disable it */
        curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);

      /* new in curl 7.9.7 */
      if(config->trace_dump) {
        curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
        curl_easy_setopt(curl, CURLOPT_DEBUGDATA, config);
        config->conf |= CONF_VERBOSE; /* force verbose */
      curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE);
      /* new in curl 7.10 */
      curl_easy_setopt(curl, CURLOPT_ENCODING, 
                       (config->encoding) ? "deflate" : NULL);

      res = curl_easy_perform(curl);
      if((config->progressmode == CURL_PROGRESS_BAR) &&
         progressbar.calls) {
        /* if the custom progress bar has been displayed, we output a
           newline here */
        fputs("\n", progressbar.out);
      }

      if(config->writeout) {
        ourWriteOut(curl, config->writeout);
#ifdef USE_ENVIRONMENT
      if (config->writeenv)
        ourWriteEnv(curl);
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef	VMS
      if (!config->showerror)  {
        vms_show = VMSSTS_HIDE;
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
#else
      if((res!=CURLE_OK) && config->showerror)
        fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
Daniel Stenberg's avatar
Daniel Stenberg committed
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

      if (outfile && !strequal(outfile, "-") && outs.stream)
        fclose(outs.stream);

#ifdef HAVE_UTIME
      /* Important that we set the time _after_ the file has been 
         closed, as is done above here */
      if(config->remote_time && outs.filename) {
        /* as libcurl if we got a time. Pretty please */
        long filetime;
        curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
        if(filetime >= 0) {
          struct utimbuf times;
          times.actime = filetime;
          times.modtime = filetime;
          utime(outs.filename, &times); /* set the time we got */
        }
      }
#endif

      if (config->infile)
        fclose(infd);
      if(headerfilep)
        fclose(headerfilep);
      
      if(url)
        free(url);
Daniel Stenberg's avatar
Daniel Stenberg committed

      if(outfile)
        free(outfile);
    if(outfiles)
      free(outfiles);
    if(urls)
      /* cleanup memory used for URL globbing patterns */
      glob_cleanup(urls);
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* empty this urlnode struct */
    if(urlnode->url)
      free(urlnode->url);
    if(urlnode->outfile)
      free(urlnode->outfile);
    /* move on to the next URL */
    nextnode=urlnode->next;
    free(urlnode); /* free the node */
    urlnode = nextnode;
Daniel Stenberg's avatar
Daniel Stenberg committed

  } /* while-loop through all URLs */
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(config->headerfile && !headerfilep && heads.stream)
    fclose(heads.stream);

  if(config->trace_fopened)
  if(allocuseragent)
    free(config->useragent);
  /* cleanup the curl handle! */
  curl_easy_cleanup(curl);

  struct Configurable config;
  memset(&config, 0, sizeof(struct Configurable));