Skip to content
Snippets Groups Projects
main.c 110 KiB
Newer Older
  • Learn to ignore specific revisions
  • /***************************************************************************
    
     *                                  _   _ ____  _
     *  Project                     ___| | | |  _ \| |
     *                             / __| | | | |_) | |
     *                            | (__| |_| |  _ <| |___
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *                             \___|\___/|_| \_\_____|
     *
    
     * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
     * This software is licensed as described in the file COPYING, which
     * you should have received as part of this distribution. The terms
     * are also available at http://curl.haxx.se/docs/copyright.html.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     * copies of the Software, and permit persons to whom the Software is
    
     * furnished to do so, under the terms of the COPYING file.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     * KIND, either express or implied.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * $Id$
    
     ***************************************************************************/
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    /* This is now designed to have its own local setup.h */
    #include "setup.h"
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    
    #include <sys/types.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <sys/stat.h>
    #include <ctype.h>
    
    #include <errno.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    #include <curl/curl.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include "urlglob.h"
    
    #include "writeout.h"
    
    #ifdef USE_MANUAL
    
    #include "hugehelp.h"
    
    #ifdef USE_ENVIRONMENT
    #include "writeenv.h"
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #define CURLseparator	"--_curl_--"
    
    #if defined(WIN32)&&!defined(__CYGWIN32__)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
    
    #ifdef __NOVELL_LIBC__
    #include <screen.h>
    #endif
    
    
    #ifdef TIME_WITH_SYS_TIME
    /* We can include both fine */
    
    #include <time.h>
    #else
    #ifdef HAVE_SYS_TIME_H
    # include <sys/time.h>
    #else
    # include <time.h>
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include "version.h"
    
    #ifdef HAVE_IO_H /* typical win32 habit */
    #include <io.h>
    #endif
    
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    
    
    #ifdef HAVE_FCNTL_H
    #include <fcntl.h>
    #endif
    
    
    #ifdef HAVE_SYS_UTIME_H
    #include <sys/utime.h>
    #endif
    
    #ifdef HAVE_LIMITS_H
    #include <limits.h>
    #endif
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef HAVE_SYS_POLL_H
    #include <sys/poll.h>
    
    #define ENABLE_CURLX_PRINTF
    /* make the curlx header define all printf() functions to use the curlx_*
       versions instead */
    #include <curlx.h> /* header from the libcurl directory */
    
    /* The last #include file should be: */
    
    #ifdef CURLDEBUG
    
    #ifndef CURLTOOLDEBUG
    #define MEMDEBUG_NODEFINES
    #endif
    
    /* This is low-level hard-hacking memory leak tracking and similar. Using
       the library level code from this client-side is ugly, but we do this
       anyway for convenience. */
    
    #define DEFAULT_MAXREDIRS  50L
    
    #include <dos.h>
    
    
    char *msdosify(char *);
    char *rename_if_dos_device_name(char *);
    
    
    /* we want to glob our own argv[] */
    char **__crt0_glob_function (char *arg)
    {
      (void)arg;
      return (char**)0;
    }
    
    #ifndef __cplusplus
    
    #ifndef typedef_bool
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    typedef char bool;
    
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #define CURL_PROGRESS_STATS 0 /* default progress display */
    #define CURL_PROGRESS_BAR   1
    
    
    /**
     * @def MIN
     * standard MIN macro
     */
    #ifndef MIN
    #define MIN(X,Y)	(((X) < (Y)) ? (X) : (Y))
    #endif
    
    
    typedef enum {
      HTTPREQ_UNSPEC,
      HTTPREQ_GET,
      HTTPREQ_HEAD,
      HTTPREQ_POST,
      HTTPREQ_SIMPLEPOST,
      HTTPREQ_CUSTOM,
      HTTPREQ_LAST
    } HttpReq;
    
    
    /* Just a set of bits */
    #define CONF_DEFAULT  0
    
    
    #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
    
    #define CONF_VERBOSE  (1<<5) /* talk a lot */
    #define CONF_HEADER   (1<<8) /* throw the header out too */
    #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */
    #define CONF_NOBODY   (1<<11) /* use HEAD to get http document */
    #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */
    #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */
    #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */
    #define CONF_NETRC    (1<<22)  /* read user+password from .netrc */
    #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */
    
    #define CONF_GETTEXT  (1<<24) /* use ASCII/text for transfer */
    
    #define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
    #define CONF_MUTE     (1<<28) /* force NOPROGRESS */
    
    
    #define CONF_NETRC_OPT (1<<29)  /* read user+password from either
                                     * .netrc or URL*/
    
    #define CONF_UNRESTRICTED_AUTH (1<<30)
    /* Send authentication (user+password) when following
     * locations, even when hostname changed */
    
    #ifndef HAVE_STRDUP
    /* Ultrix doesn't have strdup(), so make a quick clone: */
    char *strdup(char *str)
    {
      int len;
      char *newstr;
    
      len = strlen(str);
      newstr = (char *) malloc((len+1)*sizeof(char));
      if (!newstr)
        return (char *)NULL;
    
      strcpy(newstr,str);
    
      return newstr;
    
    }
    
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef WIN32
    #include <direct.h>
    #define F_OK 0
    #define mkdir(x,y) (mkdir)(x)
    #endif
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef	VMS
    
    /*
     * This is the main global constructor for the app. Call this before
     * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
     * used, or havoc may be the result.
     */
    
    static CURLcode main_init(void)
    
      return curl_global_init(CURL_GLOBAL_DEFAULT);
    
    }
    
    /*
     * This is the main global destructor for the app. Call this after
     * _all_ libcurl usage is done.
     */
    
    static void main_free(void)
    
    static int SetHTTPrequest(HttpReq req, HttpReq *store)
    
    {
      if((*store == HTTPREQ_UNSPEC) ||
         (*store == req)) {
        *store = req;
    
      }
      fprintf(stderr, "You can only select one HTTP request!\n");
    
    static void helpf(const char *fmt, ...)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      va_list ap;
      if(fmt) {
        va_start(ap, fmt);
        fputs("curl: ", stderr); /* prefix it */
        vfprintf(stderr, fmt, ap);
        va_end(ap);
      }
    
      fprintf(stderr, "curl: try 'curl --help' "
    #ifdef USE_MANUAL
              "or 'curl --manual' "
    #endif
              "for more information\n");
    
    /*
     * A chain of these nodes contain URL to get and where to put the URL's
     * contents.
     */
    struct getout {
    
      struct getout *next; /* next one */
      char *url;     /* the URL we deal with */
      char *outfile; /* where to store the output */
      char *infile;  /* file to upload, if GETOUT_UPLOAD is set */
      int flags;     /* options */
    
    #define GETOUT_OUTFILE (1<<0)   /* set when outfile is deemed done */
    #define GETOUT_URL     (1<<1)   /* set when URL is deemed done */
    
    #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
    
    #define GETOUT_UPLOAD  (1<<3)   /* if set, -T has been used */
    #define GETOUT_NOUPLOAD  (1<<4) /* if set, -T "" has been used */
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    static void help(void)
    {
    
      static const char *helptext[]={
    
        "Usage: curl [options...] <url>",
        "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
        " -a/--append        Append to target file when uploading (F)",
        " -A/--user-agent <string> User-Agent to send to server (H)",
        "    --anyauth       Tell curl to choose authentication method (H)",
        " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
        "    --basic         Enable HTTP Basic Authentication (H)",
        " -B/--use-ascii     Use ASCII/text transfer",
    
        " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
        " -C/--continue-at <offset> Resumed transfer offset",
    
        " -d/--data <data>   HTTP POST data (H)",
        "    --data-ascii <data>   HTTP POST ASCII data (H)",
        "    --data-binary <data>  HTTP POST binary data (H)",
    
        "    --negotiate     Enable HTTP Negotiate Authentication (H)",
    
        "    --digest        Enable HTTP Digest Authentication (H)",
    
        "    --disable-eprt  Prevent curl from using EPRT or LPRT (F)",
        "    --disable-epsv  Prevent curl from using EPSV (F)",
    
        " -D/--dump-header <file> Write the headers to this file",
        "    --egd-file <file> EGD socket path for random data (SSL)",
    
        "    --tcp-nodelay   Set the TCP_NODELAY option",
    
    #ifdef USE_ENVIRONMENT
    
        "    --environment   Write result codes to environment variables (RISC OS)",
    
        " -e/--referer       Referer URL (H)",
        " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
        "    --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
        "    --key <key>     Private key file name (SSL)",
        "    --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
        "    --pass  <pass>  Pass phrase for the private key (SSL)",
        "    --engine <eng>  Crypto engine to use (SSL)",
    
        "    --cacert <file> CA certificate to verify peer against (SSL)",
        "    --capath <directory> CA directory (made using c_rehash) to verify",
        "                    peer against (SSL)",
    
        "    --ciphers <list> SSL ciphers to use (SSL)",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        "    --compressed    Request compressed response (using deflate or gzip)",
    
        "    --connect-timeout <seconds> Maximum time allowed for connection",
    
        "    --create-dirs   Create necessary local directory hierarchy",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        "    --crlf          Convert LF to CRLF in upload",
    
        " -f/--fail          Fail silently (no output at all) on errors (H)",
        "    --ftp-create-dirs Create the remote dirs if not present (F)",
    
        "    --ftp-pasv      Use PASV instead of PORT (F)",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        "    --ftp-ssl       Enable SSL/TLS for the ftp transfer (F)",
    
        " -F/--form <name=content> Specify HTTP multipart POST data (H)",
    
        " -g/--globoff       Disable URL sequences and ranges using {} and []",
        " -G/--get           Send the -d data with a HTTP GET (H)",
        " -h/--help          This help text",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        " -H/--header <line> Custom header to pass to server (H)",
    
        " -i/--include       Include protocol headers in the output (H/F)",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        " -I/--head          Show document info only",
    
        " -j/--junk-session-cookies Ignore session cookies read from file (H)",
    
        "    --interface <interface> Specify network interface to use",
    
        "    --krb4 <level>  Enable krb4 with specified security level (F)",
        " -k/--insecure      Allow curl to connect to SSL sites without certs (H)",
        " -K/--config        Specify which config file to read",
        " -l/--list-only     List only names of an FTP directory (F)",
    
        "    --limit-rate <rate> Limit transfer speed to this rate",
    
        " -L/--location      Follow Location: hints (H)",
    
        "    --location-trusted Follow Location: and send authentication even ",
        "                    to other hostnames (H)",
    
        " -m/--max-time <seconds> Maximum time allowed for the transfer",
    
        "    --max-redirs <num> Maximum number of redirects allowed (H)",
        "    --max-filesize <bytes> Maximum file size to download (H/F)",
        " -M/--manual        Display the full manual",
    
        " -n/--netrc         Must read .netrc for user name and password",
    
        "    --netrc-optional Use either .netrc or URL; overrides -n",
    
        "    --ntlm          Enable HTTP NTLM authentication (H)",
    
        " -N/--no-buffer     Disable buffering of the output stream",
    
        " -o/--output <file> Write output to <file> instead of stdout",
        " -O/--remote-name   Write output to a file named as the remote file",
    
        " -p/--proxytunnel   Operate through a HTTP proxy tunnel (using CONNECT)",
    
        "    --proxy-basic   Enable Basic authentication on the proxy (H)",
    
        "    --proxy-digest  Enable Digest authentication on the proxy (H)",
    
        "    --proxy-ntlm    Enable NTLM authentication on the proxy (H)",
    
        " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
    
        " -q                 If used as the first parameter disables .curlrc",
        " -Q/--quote <cmd>   Send command(s) to server before file transfer (F)",
    
        " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
    
        "    --random-file <file> File for reading random data from (SSL)",
    
        " -R/--remote-time   Set the remote file's time on the local output",
        " -s/--silent        Silent mode. Don't output anything",
        " -S/--show-error    Show error. With -s, make curl show errors when they occur",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        "    --socks <host[:port]> Use SOCKS5 proxy on given host + port",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        "    --stderr <file> Where to redirect stderr. - means stdout",
    
        " -t/--telnet-option <OPT=val> Set telnet option",
        "    --trace <file>  Dump a network/debug trace to the given file",
    
        "    --trace-ascii <file> Like --trace but without the hex output",
    
        " -T/--upload-file <file> Transfer/upload <file> to remote site",
        "    --url <URL>     Another way to specify URL to work with",
        " -u/--user <user[:password]> Specify user and password to use",
        "                    Overrides -n and --netrc-optional",
        " -U/--proxy-user <user[:password]> Specify Proxy authentication",
    
        " -v/--verbose       Make the operation more talkative",
        " -V/--version       Show version number and quit",
    
        "    --wdebug        Turn on Watt-32 debugging under DJGPP",
    
        " -w/--write-out [format] What to output after completion",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        " -x/--proxy <host[:port]> Use HTTP proxy on given port",
    
        " -X/--request <command> Specify request command to use",
    
        " -y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30",
        " -Y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs",
    
        " -z/--time-cond <time> Transfer based on a time condition",
        " -0/--http1.0       Use HTTP 1.0 (H)",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        " -1/--tlsv1         Use TLSv1 (SSL)",
        " -2/--sslv2         Use SSLv2 (SSL)",
        " -3/--sslv3         Use SSLv3 (SSL)",
    
        " -4/--ipv4          Resolve name to IPv4 address",
        " -6/--ipv6          Resolve name to IPv6 address",
    
        " -#/--progress-bar  Display transfer progress as a progress bar",
        NULL
      };
    
      for(i=0; helptext[i]; i++) {
    
    #ifdef __NOVELL_LIBC__
        if (i && ((i % 23) == 0))
          pressanykey();
    #endif
      }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    struct LongShort {
    
      const char *letter;
      const char *lname;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool extraparam;
    };
    
    struct Configurable {
    
      char *random_file;
      char *egd_file;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *useragent;
    
      char *cookie;     /* single line with specified cookies */
      char *cookiejar;  /* write to this file */
      char *cookiefile; /* read from this file */
    
      bool cookiesession; /* new session? */
    
      bool encoding;    /* Accept-Encoding please */
    
      long authtype;    /* auth bitmask */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool use_resume;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool disable_epsv;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool disable_eprt;
    
      curl_off_t resume_from;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *postfields;
    
      long postfieldsize;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *referer;
      long timeout;
    
      long connecttimeout;
    
      curl_off_t max_filesize;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *headerfile;
      char *ftpport;
    
      char *iface;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      unsigned short porttouse;
      char *range;
    
      long low_speed_limit;
      long low_speed_time;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool showerror;
      char *userpwd;
      char *proxyuserpwd;
      char *proxy;
    
      bool proxytunnel;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      long conf;
    
    
      struct getout *url_list; /* point to the first node */
      struct getout *url_last; /* point to the last/current node */
    
      struct getout *url_get;  /* point to the node to fill in URL */
      struct getout *url_out;  /* point to the node to fill in outfile */
    
    
      char *cipher_list;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *cert;
    
      char *cert_type;
    
      char *cacert;
    
      char *capath;
    
      char *key;
      char *key_type;
      char *key_passwd;
      char *engine;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool crlf;
      char *customrequest;
    
      char *krb4level;
    
      char *trace_dump; /* file to dump the network trace to, or NULL */
      FILE *trace_stream;
    
      bool trace_fopened;
    
      bool trace_ascii;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool progressmode;
    
      bool insecure_ok; /* set TRUE to allow insecure SSL connects */
    
      bool ftp_create_dirs;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool proxyntlm;
    
      bool proxydigest;
    
      bool proxybasic;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      char *writeout; /* %-styled format string to output */
    
      bool writeenv; /* write results to environment, if available */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      FILE *errors; /* if stderr redirect is requested */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      struct curl_slist *quote;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      struct curl_slist *postquote;
    
      struct curl_slist *prequote;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      long ssl_version;
    
      long ip_version;
    
      curl_TimeCond timecond;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      time_t condtime;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      struct curl_httppost *httppost;
      struct curl_httppost *last_post;
    
      struct curl_slist *telnet_options;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      /* for bandwidth limiting features: */
    
      curl_off_t sendpersecond; /* send to peer */
      curl_off_t recvpersecond; /* receive from peer */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      size_t lastsendsize;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      size_t lastrecvsize;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      bool ftp_ssl;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *socks5proxy;
    
      bool tcp_nodelay;
    
    /* global variable to hold info about libcurl */
    static curl_version_info_data *curlinfo;
    
    
    static void parseconfig(const char *filename,
                            struct Configurable *config);
    
    static int create_dir_hierarchy(char *outfile);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    static void GetStr(char **string,
    		   char *value)
    {
      if(*string)
        free(*string);
    
        *string = strdup(value);
      else
        *string = NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    }
    
    static char *file2string(FILE *file)
    {
      char buffer[256];
      char *ptr;
      char *string=NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      size_t len=0;
      size_t stringlen;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(file) {
        while(fgets(buffer, sizeof(buffer), file)) {
          ptr= strchr(buffer, '\r');
          if(ptr)
            *ptr=0;
          ptr= strchr(buffer, '\n');
          if(ptr)
            *ptr=0;
          stringlen=strlen(buffer);
          if(string)
            string = realloc(string, len+stringlen+1);
          else
            string = malloc(stringlen+1);
    
          strcpy(string+len, buffer);
    
          len+=stringlen;
        }
        return string;
      }
      else
        return NULL; /* no string */
    }
    
    
    static char *file2memory(FILE *file, long *size)
    {
      char buffer[1024];
      char *string=NULL;
      char *newstring=NULL;
    
      size_t len=0;
    
      long stringlen=0;
    
      if(file) {
    
        while((len = fread(buffer, 1, sizeof(buffer), file))) {
    
          if(string) {
            newstring = realloc(string, len+stringlen);
            if(newstring)
              string = newstring;
            else
              break; /* no more strings attached! :-) */
          }
          else
            string = malloc(len);
          memcpy(&string[stringlen], buffer, len);
          stringlen+=len;
        }
        *size = stringlen;
        return string;
      }
      else
        return NULL; /* no string */
    }
    
    
    static void clean_getout(struct Configurable *config)
    
    {
      struct getout *node=config->url_list;
      struct getout *next;
    
      while(node) {
        next = node->next;
        if(node->url)
          free(node->url);
        if(node->outfile)
          free(node->outfile);
    
        free(node);
    
        node = next; /* GOTO next */
      }
    }
    
    
    static struct getout *new_getout(struct Configurable *config)
    
    {
      struct getout *node =malloc(sizeof(struct getout));
      struct getout *last= config->url_last;
      if(node) {
        /* clear the struct */
        memset(node, 0, sizeof(struct getout));
    
        /* append this new node last in the list */
        if(last)
          last->next = node;
        else
          config->url_list = node; /* first node */
    
        /* move the last pointer */
        config->url_last = node;
      }
      return node;
    }
    
    
    /* Structure for storing the information needed to build a multiple files
     * section
    */
    struct multi_files {
      struct curl_forms   form;
      struct multi_files *next;
    };
    
    /* Add a new list entry possibly with a type_name
     */
    
    static struct multi_files *
    AddMultiFiles (const char *file_name,
                   const char *type_name,
                   const char *show_filename,
                   struct multi_files **multi_start,
                   struct multi_files **multi_current)
    
    {
      struct multi_files *multi;
      struct multi_files *multi_type = NULL;
    
      struct multi_files *multi_name = NULL;
    
      multi = (struct multi_files *)malloc(sizeof(struct multi_files));
      if (multi) {
        memset(multi, 0, sizeof(struct multi_files));
        multi->form.option = CURLFORM_FILE;
        multi->form.value = file_name;
      }
      else
        return NULL;
    
      if (type_name) {
        multi_type = (struct multi_files *)malloc(sizeof(struct multi_files));
        if (multi_type) {
          memset(multi_type, 0, sizeof(struct multi_files));
          multi_type->form.option = CURLFORM_CONTENTTYPE;
          multi_type->form.value = type_name;
          multi->next = multi_type;
    
      if (show_filename) {
        multi_name = (struct multi_files *)malloc(sizeof(struct multi_files));
        if (multi_name) {
          memset(multi_name, 0, sizeof(struct multi_files));
          multi_name->form.option = CURLFORM_FILENAME;
          multi_name->form.value = show_filename;
          multi->next = multi_name;
    
          multi = multi_name;
    
    
      if (*multi_current)
        (*multi_current)->next = multi;
    
      *multi_current = multi;
    
    
    static void FreeMultiInfo (struct multi_files *multi_start)
    
      while (multi_start) {
        multi = multi_start;
        multi_start = multi_start->next;
    
        free (multi);
      }
    }
    
    /***************************************************************************
     *
     * formparse()
    
     * Reads a 'name=value' paramter and builds the appropriate linked list.
     *
     * Specify files to upload with 'name=@filename'. Supports specified
     * given Content-Type of the files. Such as ';type=<content-type>'.
     *
     * You may specify more than one file for a single name (field). Specify
     * multiple files by writing it like:
     *
     * 'name=@filename,filename2,filename3'
     *
     * If you want content-types specified for each too, write them like:
     *
     * 'name=@filename;type=image/gif,filename2,filename3'
     *
    
     * If you want custom headers added for a single part, write them in a separate
     * file and do like this:
     *
     * 'name=foo;headers=@headerfile' or why not
     * 'name=@filemame;headers=@headerfile'
     *
     * To upload a file, but to fake the file name that will be included in the
     * formpost, do like this:
     *
     * 'name=@filename;filename=/dev/null'
     *
     * This function uses curl_formadd to fulfill it's job. Is heavily based on
     * the old curl_formparse code.
    
     *
     ***************************************************************************/
    
    #define FORM_FILE_SEPARATOR ','
    #define FORM_TYPE_SEPARATOR ';'
    
    static int formparse(char *input,
    
                         struct curl_httppost **httppost,
                         struct curl_httppost **last_post)
    
    {
      /* nextarg MUST be a string in the format 'name=contents' and we'll
         build a linked list with the info */
      char name[256];
      char *contents;
      char major[128];
      char minor[128];
      char *contp;
      const char *type = NULL;
      char *sep;
      char *sep2;
    
    
      if((1 == sscanf(input, "%255[^=]=", name)) &&
         (contp = strchr(input, '='))) {
    
        /* the input was using the correct format */
    
        /* Allocate the contents */
        contents = strdup(contp+1);
    
        if(!contents) {
          fprintf(stderr, "out of memory\n");
          return 1;
        }
    
        contp = contents;
    
        if('@' == contp[0]) {
          struct multi_files *multi_start = NULL, *multi_current = NULL;
          /* we use the @-letter to indicate file name(s) */
          contp++;
    
          multi_start = multi_current=NULL;
    
          do {
    	/* since this was a file, it may have a content-type specifier
    
    	   at the end too, or a filename. Or both. */
            char *ptr;
            char *filename=NULL;
    
    
    	sep=strchr(contp, FORM_TYPE_SEPARATOR);
    	sep2=strchr(contp, FORM_FILE_SEPARATOR);
    
    	/* pick the closest */
    	if(sep2 && (sep2 < sep)) {
    	  sep = sep2;
    
    	  /* no type was specified! */
    	}
    
    	if(sep) {
    
    	  /* if we got here on a comma, don't do much */
    
    	  if(FORM_FILE_SEPARATOR == *sep)
    	    ptr = NULL;
    
    
    	  *sep=0; /* terminate file name at separator */
    
    
    	  while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
    
                /* pass all white spaces */
                while(isspace((int)*ptr))
                  ptr++;
    
    
                if(curlx_strnequal("type=", ptr, 5)) {
    
                  /* verify that this is a fine type specifier */
                  if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
                                 major, minor)) {
                    fprintf(stderr, "Illegally formatted content-type field!\n");
                    free(contents);
                    FreeMultiInfo (multi_start);
                    return 2; /* illegal content-type syntax! */
                  }
                  /* now point beyond the content-type specifier */
                  sep = (char *)type + strlen(major)+strlen(minor)+1;
    
                  *sep=0; /* zero terminate type string */
    
                  ptr=sep+1;
                }
    
                else if(curlx_strnequal("filename=", ptr, 9)) {
    
                  filename = &ptr[9];
                  ptr=strchr(filename, FORM_TYPE_SEPARATOR);
                  if(!ptr) {
                    ptr=strchr(filename, FORM_FILE_SEPARATOR);
                  }
                  if(ptr) {
                    *ptr=0; /* zero terminate */
                    ptr++;
                  }
                }
                else
                  /* confusion, bail out of loop */
                  break;
    
              /* find the following comma */
              if(ptr)
                sep=strchr(ptr, FORM_FILE_SEPARATOR);
              else
                sep=NULL;
    
    	}
    	else {
    	  sep=strchr(contp, FORM_FILE_SEPARATOR);
    	}
    	if(sep) {
    	  /* the next file name starts here */
    	  *sep =0;
    	  sep++;
    	}
            /* if type == NULL curl_formadd takes care of the problem */
    
    
            if (!AddMultiFiles (contp, type, filename, &multi_start,
                                &multi_current)) {
    
              fprintf(stderr, "Error building form post!\n");
              free(contents);
    
              return 3;
            }
    	contp = sep; /* move the contents pointer to after the separator */
    
          } while(sep && *sep); /* loop if there's another file name */
    
          /* now we add the multiple files section */
          if (multi_start) {
            struct curl_forms *forms = NULL;
            struct multi_files *ptr = multi_start;
            unsigned int i, count = 0;
            while (ptr) {
              ptr = ptr->next;
              ++count;
            }
            forms =
              (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms));
            if (!forms)
            {
              fprintf(stderr, "Error building form post!\n");
              free(contents);
    
              return 4;
            }
            for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
            {
              forms[i].option = ptr->form.option;
              forms[i].value = ptr->form.value;
            }
            forms[count].option = CURLFORM_END;
    
            if (curl_formadd(httppost, last_post,
                             CURLFORM_COPYNAME, name,
                             CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
    
              fprintf(stderr, "curl_formadd failed!\n");
              free(forms);
              free(contents);
              return 5;
            }
            free(forms);
          }
        }
        else {
          if( contp[0]=='<' ) {
    
            if (curl_formadd(httppost, last_post,
                             CURLFORM_COPYNAME, name,
                             CURLFORM_FILECONTENT, contp+1, CURLFORM_END) != 0) {
    
              fprintf(stderr, "curl_formadd failed!\n");
              free(contents);
              return 6;
            }
          }
          else {
    
            if (curl_formadd(httppost, last_post,
                             CURLFORM_COPYNAME, name,
                             CURLFORM_COPYCONTENTS, contp, CURLFORM_END) != 0) {
    
              fprintf(stderr, "curl_formadd failed!\n");
              free(contents);
              return 7;
            }
          }
        }
    
      }
      else {
        fprintf(stderr, "Illegally formatted input field!\n");
        return 1;
      }
      free(contents);
      return 0;
    }
    
    typedef enum {
      PARAM_OK,
      PARAM_OPTION_AMBIGUOUS,
      PARAM_OPTION_UNKNOWN,
    
      PARAM_REQUIRES_PARAMETER,
    
      PARAM_BAD_USE,
      PARAM_HELP_REQUESTED,
      PARAM_GOT_EXTRA_PARAMETER,
    
      PARAM_LIBCURL_DOESNT_SUPPORT,
    
    static const char *param2text(int res)
    
      ParameterError error = (ParameterError)res;
    
      switch(error) {
      case PARAM_GOT_EXTRA_PARAMETER:
        return "had unsupported trailing garbage";
      case PARAM_OPTION_UNKNOWN:
        return "is unknown";
      case PARAM_OPTION_AMBIGUOUS:
        return "is ambiguous";
      case PARAM_REQUIRES_PARAMETER:
        return "requires parameter";
      case PARAM_BAD_USE:
        return "is badly used here";
      case PARAM_BAD_NUMERIC:
        return "expected a proper numerical parameter";
      case PARAM_LIBCURL_DOESNT_SUPPORT:
        return "the installed libcurl version doesn't support this";
    
      case PARAM_NO_MEM:
        return "out of memory";
    
      default:
        return "unknown error";
      }
    }
    
    
    static void cleanarg(char *str)
    {
    #ifdef HAVE_WRITABLE_ARGV
      /* now that GetStr has copied the contents of nextarg, wipe the next
       * argument out so that the username:password isn't displayed in the
       * system process list */
      if (str) {
        size_t len = strlen(str);
        memset(str, ' ', len);