/*****************************************************************************
 *                                  _   _ ____  _     
 *  Project                     ___| | | |  _ \| |    
 *                             / __| | | | |_) | |    
 *                            | (__| |_| |  _ <| |___ 
 *                             \___|\___/|_| \_\_____|
 *
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *  License for the specific language governing rights and limitations
 *  under the License.
 *
 *  The Original Code is Curl.
 *
 *  The Initial Developer of the Original Code is Daniel Stenberg.
 *
 *  Portions created by the Initial Developer are Copyright (C) 1998.
 *  All Rights Reserved.
 *
 * ------------------------------------------------------------
 * Main author:
 * - Daniel Stenberg <Daniel.Stenberg@sth.frontec.se>
 *
 * 	http://www.fts.frontec.se/~dast/curl/
 *
 * $Source: /cvs/curl/curl/src/main.c,v $
 * $Revision: 1.10 $
 * $Date: 1999/04/19 22:23:43 $
 * $Author: dast $
 * $State: Exp $
 * $Locker:  $
 *
 * ------------------------------------------------------------
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <ctype.h>

#include "../include/setup.h"
#include "../include/curl.h"
#include "../include/mprintf.h"

#include "version.h"

#ifdef WIN32
#include <io.h>
#else
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif

extern void hugehelp(void);

static void helpf(char *fmt, ...)
{
  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' for more information\n");
}

static void help(void)
{
  printf(CURL_ID "%s\n"
       "Usage: curl [options...] <url>\n"
       "Options: (H) means HTTP/HTTPS only, (F) means FTP only\n"
       " -a/--append        Append to target file when uploading (F)\n"
       " -A/--user-agent <string> User-Agent to send to server (H)\n"
       " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)\n"
       " -B/--ftp-ascii     Use ASCII transfer (F)\n"
       " -c/--continue      Resume a previous transfer where we left it (F)\n"
       " -C/--continue-at <offset> Specify absolute resume offset (F)\n"
       " -d/--data          POST data (H)\n"
       " -D/--dump-header <file> Write the HTTP headers to this file (H)\n"
       " -e/--referer       Referer page (H)\n"
       " -E/--cert <cert:passwd> Specifies your certificate file and password (HTTPS)\n"
       " -f/--fail          Fail silently (no output at all) on errors (H)\n"
       " -F/--form <name=content> Specify HTTP POST data (H)\n"
#ifdef MULTIDOC
       " -g/--get-too <path> Specifies an extra file path to get (H)\n"
       " -G/--get-store <file> File name to store the extra file as (H)\n"
#endif
       " -h/--help          This help text\n"
       " -H/--header <line> Custom header to pass to server. (H)\n"
       " -i/--include       Include the HTTP-header in the output (H)\n"
       " -I/--head          Fetch the HTTP-header only (HEAD) (H)\n"
       " -K/--config        Specify which config file to read\n"
       " -l/--list-only     List only names of an FTP directory (F)\n"
       " -L/--location      Follow Location: hints (H)\n"
       " -m/--max-time <seconds> Maximum time allowed for the transfer\n"
       " -M/--manual        Display huge help text\n"
       " -n/--netrc         Read .netrc for user name and password\n"
       " -o/--output <file> Write output to <file> instead of stdout\n"
       " -O/--remote-name   Write output to a file named as the remote file\n"
       " -p/--port <port>   Use port other than default for current protocol.\n"
       " -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)\n"
       " -q                 When used as the first parameter disables .curlrc\n"
       " -Q/--quote <cmd>   Send QUOTE command to FTP before file transfer (F)\n"
       " -r/--range <range> Retrieve a byte range from a HTTP/1.1 server (H)\n"
       " -s/--silent        Silent mode. Don't show progress info\n"
       " -t/--upload        Transfer/upload stdin to remote site\n"
       " -T/--upload-file <file> Transfer/upload <file> to remote site\n"
       " -u/--user <user:password> Specify user and password to use\n"
       " -U/--proxy-user <user:password> Specify Proxy authentication\n"
       " -v/--verbose       Makes the operation more talkative\n"
       " -V/--version       Outputs version number then quits\n"
       " -x/--proxy <host>  Use proxy. (Default port is 1080)\n"
       " -y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs\n"
       " -Y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30\n"
       " -2/--sslv2         Force usage of SSLv2 (H)\n"
       " -3/--sslv3         Force usage of SSLv3 (H)\n"
       "    --crlf          Convert LF to CRLF in upload. Useful for MVS (OS/390)\n",
         curl_version()
         );
}

struct LongShort {
  char *letter;
  char *lname;
  bool extraparam;
};

struct Configurable {
  char *useragent;
  char *cookie;
  bool use_resume;
  int resume_from;
  char *postfields;
  char *referer;
  long timeout;
  char *outfile;
  char *headerfile;
  char remotefile;
  char *ftpport;
  unsigned short porttouse;
  char *range;
  int low_speed_limit;
  int low_speed_time;
  bool showerror;
  char *infile;
  char *userpwd;
  char *proxyuserpwd;
  char *proxy;
  bool configread;
  long conf;
  char *url;
  char *cert;
  char *cert_passwd;
  bool crlf;
  char *cookiefile;

  struct slist *quote;

  long ssl_version;

#ifdef MULTIDOC
  struct MoreDoc *newdoc;
  struct MoreDoc *last_newdoc;
#endif
  struct HttpHeader *headers;
  struct HttpHeader *last_header;

  struct HttpPost *httppost;
  struct HttpPost *last_post;
};

static int parseconfig(char *filename,
		       struct Configurable *config);

static void GetStr(char **string,
		   char *value)
{
  if(*string)
    free(*string);
  *string = strdup(value);
}

static int getparameter(char *flag, /* f or -long-flag */
			char *nextarg, /* NULL if unset */
			bool *usedarg, /* set to TRUE if the arg has been
					  used */
			struct Configurable *config)
{
  char letter;
  char *parse=NULL;
  int res;
  struct HttpHeader *head;
  int j;
  int hit=-1;

  /* single-letter,
     long-name,
     boolean whether it takes an additional argument
     */
  struct LongShort aliases[]= {
    {"9", "crlf",        FALSE},
    {"2", "sslv2",       FALSE},
    {"3", "sslv3",       FALSE},
    {"a", "append",      FALSE},
    {"A", "user-agent",  TRUE},
    {"b", "cookie",      TRUE},
    {"B", "ftp-ascii",   FALSE},
    {"c", "continue",    FALSE},
    {"C", "continue-at", TRUE},
    {"d", "data",        TRUE},
    {"D", "dump-header", TRUE},
    {"e", "referer",     TRUE},
    {"E", "cert",        TRUE},
    {"f", "fail",        FALSE},
    {"F", "form",        TRUE},
#ifdef MULTIDOC
    {"g", "get-too",     TRUE},
    {"G", "get-store",   TRUE},
#endif
    {"h", "help",        FALSE},
    {"H", "header",      TRUE},
    {"i", "include",     FALSE},
    {"I", "head",        FALSE},
    {"K", "config",      TRUE},
    {"l", "list-only",   FALSE},
    {"L", "location",    FALSE},
    {"m", "max-time",    TRUE},
    {"M", "manual",      FALSE},
    {"n", "netrc",       FALSE},
    {"o", "output",      TRUE},
    {"O", "remote-name", FALSE},
    {"p", "port",        TRUE},
    {"P", "ftpport",     TRUE},
    {"q", "disable",     FALSE},
    {"Q", "quote",       TRUE},
    {"r", "range",       TRUE},
    {"s", "silent",      FALSE},
    {"t", "upload",      FALSE},
    {"T", "upload-file", TRUE},
    {"u", "user",        TRUE},
    {"U", "proxy-user",  TRUE},
    {"v", "verbose",     FALSE},
    {"V", "version",     FALSE},
    {"x", "proxy",       TRUE},
    {"y", "speed-time",  TRUE},
    {"Y", "speed-limit", TRUE},
  };

  if('-' == flag[0]) {
    /* try a long name */
    int fnam=strlen(&flag[1]);
    for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
      if(strnequal(aliases[j].lname, &flag[1], fnam)) {
        if(strequal(aliases[j].lname, &flag[1])) {
          parse = aliases[j].letter;
          hit = j;
          break;
        }
	if(parse) {
	  /* this is the second match, we can't continue! */
	  helpf("option --%s is ambiguous\n", &flag[1]);
	  return URG_FAILED_INIT;
	}
	parse = aliases[j].letter;
	hit = j;
      }
    }
    if(hit < 0) {
      helpf("unknown option -%s.\n", flag);
      return URG_FAILED_INIT;
    }    
  }
  else {
    hit=-1;
    parse = flag;
  }

  do {
    /* we can loop here if we have multiple single-letters */

    letter = parse?*parse:'\0';
    *usedarg = FALSE; /* default is that we don't use the arg */

#if 0
    fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
#endif
    if(hit < 0) {
      for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
	if(letter == *aliases[j].letter) {
	  hit = j;
	  break;
	}
      }
      if(hit < 0) {
	helpf("unknown option -%c.\n", letter);
	return URG_FAILED_INIT;      
      }
    }
    if(hit < 0) {
      helpf("unknown option -%c.\n", letter);
      return URG_FAILED_INIT;
    }    
    if(!nextarg && aliases[hit].extraparam) {
      helpf("option -%s/--%s requires an extra argument!\n",
	    aliases[hit].letter,
	    aliases[hit].lname);
      return URG_FAILED_INIT;
    }
    else if(nextarg && aliases[hit].extraparam)
      *usedarg = TRUE; /* mark it as used */

    switch(letter) {
    case '9': /* there is no short letter for this */
      /* LF -> CRLF conversinon? */
      config->crlf = TRUE;
      break;
    case '2': 
      /* SSL version 2 */
      config->ssl_version = 2;
      break;
    case '3': 
      /* SSL version 2 */
      config->ssl_version = 3;
      break;
    case 'a':
      /* This makes the FTP sessions use APPE instead of STOR */
      config->conf ^= CONF_FTPAPPEND;
      break;
    case 'A':
      /* This specifies the User-Agent name */
      GetStr(&config->useragent, nextarg);
      break;
    case 'b': /* cookie string coming up: */
      if(strchr(nextarg, '=')) {
        /* A cookie string must have a =-letter */
        GetStr(&config->cookie, nextarg);
      }
      else {
        /* We have a cookie file to read from! */
        GetStr(&config->cookiefile, nextarg);
      }
      break;
    case 'B':
      /* use type ASCII when transfering ftp files */
      config->conf ^= CONF_FTPASCII;
      break;
    case 'c':
      /* This makes us continue an ftp transfer */
      config->use_resume^=TRUE;
      break;
    case 'C':
      /* This makes us continue an ftp transfer at given position */
      config->resume_from= atoi(nextarg);
      config->use_resume=TRUE;
      break;
    case 'd':
      /* postfield data */
      GetStr(&config->postfields, nextarg);
      /* printf("%s\n",postfields); fflush(stdout); */
      config->conf |= CONF_POST;
      break;
    case 'D':
      /* dump-header to given file name */
      GetStr(&config->headerfile, nextarg);
      break;
    case 'e':
      GetStr(&config->referer, nextarg);
      config->conf |= CONF_REFERER;
      break;
    case 'E':
      {
	char *ptr = strchr(nextarg, ':');
	if(ptr) {
	  /* we have a password too */
	  *ptr=0;
	  ptr++;
	  GetStr(&config->cert_passwd, ptr);
	}
	GetStr(&config->cert, nextarg);
      }
      break;
    case 'f':
      /* fail hard on errors  */
      config->conf ^= CONF_FAILONERROR;
      break;
    case 'F':
      /* "form data" simulation, this is a little advanced so lets do our best
	 to sort this out slowly and carefully */
      if(curl_FormParse(nextarg,
                        &config->httppost,
                        &config->last_post))
	return URG_FAILED_INIT;    
      config->conf |= CONF_HTTPPOST; /* no toggle, OR! */
      break;
#ifdef MULTIDOC
    case 'g':
      /* specify an additional document to get from the same server as the
         main URL */
      {
        struct MoreDoc *newdoc = (struct MoreDoc *)malloc(sizeof(struct MoreDoc));
        if(newdoc) {
          newdoc->next = NULL;
          newdoc->store = NULL; /* FILE * to store this file at */
          newdoc->path = NULL; /* first zero this */
          GetStr(&newdoc->path, nextarg); /* now get the path */

          /* point on our new one */
          if(config->last_newdoc)
            config->last_newdoc->next = newdoc;
          else {
            config->newdoc = newdoc;
          }
          config->last_newdoc = newdoc;
        }
      }
    break;
    case 'G':
      {
        FILE *docfile;
        if(!config->last_newdoc ||
           config->last_newdoc->store) {
          /* bad usage, just ignore */
        }
        else {
          docfile = fopen(nextarg, "w");
          if(docfile)
            config->last_newdoc->store = docfile;
          else
            fprintf(stderr, "Couldn't open file %s\n", nextarg);
        }
      }
    break;
#endif
    case 'h': /* h for help */
      help();
      return URG_FAILED_INIT;
    case 'H':
      head = (struct HttpHeader *)malloc(sizeof(struct HttpHeader));
      if(head) {
	head->next = NULL;
	head->header = NULL; /* first zero this */
	GetStr(&head->header, nextarg); /* now get the header line */

	/* point on our new one */
	if(config->last_header)
	  config->last_header->next = head;
	else {
	  config->headers = head;
	}

	config->last_header = head;
      }
      break;
    case 'i':
      config->conf ^= CONF_HEADER; /* include the HTTP header as well */
      break;
    case 'I':
      config->conf ^= CONF_HEADER; /* include the HTTP header in the output */
      config->conf ^= CONF_NOBODY; /* don't fetch the body at all */
      break;
    case 'K':
      res = parseconfig(nextarg, config);
      config->configread = TRUE;
      if(res)
	return res;
      break;
    case 'l':
      config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
      break;
    case 'L':
      config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
      break;
    case 'm':
      /* specified max time */
      config->timeout = atoi(nextarg);
      break;
    case 'M': /* M for manual, huge help */
      hugehelp();
      return URG_FAILED_INIT;
    case 'n':
      /* pick info from .netrc, if this is used for http, curl will
	 automatically enfore user+password with the request */
      config->conf ^= CONF_NETRC;
      break;
    case 'o':
      /* output file */
      GetStr(&config->outfile, nextarg); /* write to this file */
      break;
    case 'O':
      /* output file */
      config->remotefile ^= TRUE;
      break;
    case 'P':
      /* This makes the FTP sessions use PORT instead of PASV */
      /* use <eth0> or <192.168.10.10> style addresses. Anything except
	 this will make us try to get the "default" address.
	 NOTE: this is a changed behaviour since the released 4.1!
	 */
      config->conf |= CONF_FTPPORT;
      GetStr(&config->ftpport, nextarg);
      break;
    case 'p':
      /* specified port */
      fputs("You've used the -p option, it will be removed in a future version\n",
	    stderr);
      config->porttouse = atoi(nextarg);
      config->conf |= CONF_PORT; /* changed port */
      break;
    case 'q': /* if used first, already taken care of, we do it like
		 this so we don't cause an error! */
      break;
    case 'Q':
      /* QUOTE command to send to FTP server */
      config->quote = curl_slist_append(config->quote, nextarg);
      break;
    case 'r':
      /* byte range requested */
      GetStr(&config->range, nextarg);
      config->conf |= CONF_RANGE;
      break;
    case 's':
      /* don't show progress meter: */
      config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
      config->showerror=FALSE;
      break;
    case 't':
      /* we are uploading */
      config->conf ^= CONF_UPLOAD;
      break;
    case 'T':
      /* we are uploading */
      config->conf |= CONF_UPLOAD;
      GetStr(&config->infile, nextarg);
      break;
    case 'u':
      /* user:password  */
      GetStr(&config->userpwd, nextarg);
      config->conf |= CONF_USERPWD;
      break;
    case 'U':
      /* Proxy user:password  */
      GetStr(&config->proxyuserpwd, nextarg);
      config->conf |= CONF_PROXYUSERPWD;
      break;
    case 'v':
      config->conf ^= CONF_VERBOSE; /* talk a lot */
      break;
    case 'V':
      printf(CURL_ID "%s\n", curl_version());
      return URG_FAILED_INIT;
    case 'x':
      /* proxy */
      if(!*nextarg) {
	/* disable proxy when no proxy is given */
	config->conf &= ~CONF_PROXY;
      }
      else { 
	config->conf |= CONF_PROXY;
	GetStr(&config->proxy, nextarg);
      }
    break;
    case 'Y':
      /* low speed time */
      config->low_speed_time = atoi(nextarg);
      if(!config->low_speed_limit)
	config->low_speed_limit = 1;
      break;
    case 'y':
      /* low speed limit */
      config->low_speed_limit = atoi(nextarg);
      if(!config->low_speed_time)
	config->low_speed_time=30;
      break;

    default: /* unknown flag */
      if(letter)	
	helpf("Unknown option '%c'\n", letter);
      else
	helpf("Unknown option\n"); /* short help blurb */
      return URG_FAILED_INIT;
    }
    hit = -1;

  } while(*++parse && !*usedarg);

  return URG_OK;
}


static int parseconfig(char *filename,
		       struct Configurable *config)
{
  int res;
  FILE *file;
  char configbuffer[4096];
  char filebuffer[256];
  bool usedarg;
  
  if(!filename || !*filename) {
    /* NULL or no file name attempts to load .curlrc from the homedir! */

#define CURLRC DOT_CHAR "curlrc"

    char *home = curl_GetEnv("HOME"); /* portable environment reader */

    if(!home || (strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))))
      return URG_OK;

    sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);

    filename = filebuffer;
  }

  if(strcmp(filename,"-"))
    file = fopen(filename, "r");
  else
    file = stdin;
  
  if(file) {
    char *tok;
    char *tok2;
    while(fgets(configbuffer, sizeof(configbuffer), file)) {
      /* lines with # in the fist column is a comment! */

#if 0
      fprintf(stderr, "%s", configbuffer);
#endif
      if('#' == configbuffer[0])
	continue;
      tok = configbuffer;

      while(*tok && isspace((int)*tok))
	tok++;
/*      tok=strtok(configbuffer, " \t\n"); */
#if 0
      fprintf(stderr, "TOK: %s\n", tok);
#endif
      if('-' != tok[0]) {
	char *nl;
	if(config->url)
	  free(config->url);
	config->url = strdup(tok);
	nl = strchr(config->url, '\n');
	if(nl)
	  *nl=0;
      }
      while(('-' == tok[0])) {
	/* this is a flag */
	char *firsttok = strdup(tok);
	char *nl;

	/* remove newline from firsttok */
	nl = strchr(firsttok, '\n');
	if(nl)
	  *nl=0;

	/* pass the -flag */
	tok2=tok;
	while(*tok2 && !isspace((int)*tok2))
	  tok2++;

	/* pass the following white space */
	while(*tok2 && isspace((int)*tok2))
	  tok2++;
	
	while(!*tok2 &&
	      fgets(configbuffer, sizeof(configbuffer), file)) {
	  /* lines with # in the fist column is a comment! */
#if 0
	  fprintf(stderr, "%s", configbuffer);
#endif
	  if('#' == configbuffer[0])
	    continue;
	  tok2 = configbuffer;
	  /*	    tok2=strtok(configbuffer, " \t\n"); */
	  /* pass white space */
	  while(*tok2 && isspace((int)*tok2))
	    tok2++;
	}
	/* remove newline from tok2 */
	nl = strchr(tok2, '\n');
	if(nl)
	  *nl=0;

	res = getparameter(firsttok+1,
			   *tok2?tok2:NULL,
			   &usedarg,
			   config);
	free(firsttok);
#if 0
	fprintf(stderr, "TOK %s TOK2: %s RES: %d\n",
		firsttok, tok2?tok2:"NULL", res);
#endif
	if(res)
	  return res;
	if(!usedarg) {
	  /* tok2 is unused,  */
	  tok = tok2;
	}
	else 
	  break; /* we've used both our words */
      }
    }
    if(file != stdin)
      fclose(file);
  }
  return URG_OK;
}



int main(int argc, char *argv[])
{
  char errorbuffer[URLGET_ERROR_SIZE];

  char *url = NULL;
  
  FILE *outfd = stdout;
  FILE *infd = stdin;
  FILE *headerfilep = NULL;
  char *urlbuffer=NULL;
  int infilesize=-1; /* -1 means unknown */
  bool stillflags=TRUE;

  int res;
  int i;

  struct Configurable config;

  memset(&config, 0, sizeof(struct Configurable));
  
  /* set non-zero default values: */
  config.useragent= maprintf(CURL_NAME "/" CURL_VERSION " (" OS ") "
                             "%s", curl_version());
  config.showerror=TRUE;
  config.conf=CONF_DEFAULT;
  config.crlf=FALSE;
  config.quote=NULL;

  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!
     */
#if 0
    fprintf(stderr, "I TURNED OFF THE CRAP\n");
#endif
    ;
  }
  else {
    res = parseconfig(NULL, &config);
    if(res)
      return res;
  }

  if ((argc < 2)  && !config.url) {
    helpf(NULL);
    return URG_FAILED_INIT;
  }

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

      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)
	  return res;

	if(passarg) /* we're supposed to skip this */
	  i++;
      }
    }
    else {
      if(url) {
	helpf("only one URL is supported!\n");
	return URG_FAILED_INIT;
      }
      url = argv[i];
    }
  }

  /* if no URL was specified and there was one in the config file, get that
     one */
  if(!url && config.url)
    url = config.url;
  
  if(!url) {
    helpf("no URL specified!\n");
    return URG_FAILED_INIT;
  }
#if 0
    fprintf(stderr, "URL: %s PROXY: %s\n", url, config.proxy?config.proxy:"none");
#endif


  if(config.outfile && config.infile) {
    helpf("you can't both upload and download!\n");
    return URG_FAILED_INIT;
  }
 
  if (config.outfile || config.remotefile) {
    /* 
     * We have specified a file name to store the result in, or we have
     * decided we want to use the remote file name.
     */

    if(config.remotefile) {
      /* Find and get the remote file name */
      config.outfile=strstr(url, "://");
      if(config.outfile)
        config.outfile+=3;
      else
        config.outfile=url;
      config.outfile = strrchr(config.outfile, '/');
      if(!config.outfile || !strlen(++config.outfile)) {
        helpf("Remote file name has no length!\n");
        return URG_WRITE_ERROR;
      }
    }

    if((0 == config.resume_from) && config.use_resume) {
      /* we're told to continue where we are now, then we get the size of the
	 file as it is now and open it for append instead */
      struct stat fileinfo;

      if(0 == stat(config.outfile, &fileinfo)) {
	/* set offset to current file size: */
	config.resume_from = fileinfo.st_size;
      }
      /* else let offset remain 0 */
    }

    /* open file for output: */
    outfd=(FILE *) fopen(config.outfile, config.resume_from?"ab":"wb");
    if (!outfd) {
      helpf("Can't open '%s'!\n", config.outfile);
      return URG_WRITE_ERROR;
    }
  }
  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;
    else
      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.*/
      urlbuffer=(char *)malloc(strlen(url) + strlen(config.infile) + 3);
      if(!urlbuffer) {
        helpf("out of memory\n");
        return URG_OUT_OF_MEMORY;
      }
      if(ptr)
        /* there is a trailing slash on the URL */
        sprintf(urlbuffer, "%s%s", url, config.infile);
      else
        /* thers is no trailing slash on the URL */
        sprintf(urlbuffer, "%s/%s", url, config.infile);

      url = urlbuffer; /* use our new URL instead! */
    }

    infd=(FILE *) fopen(config.infile, "rb");
    if (!infd || stat(config.infile, &fileinfo)) {
      helpf("Can't open '%s'!\n", config.infile);
      return URG_READ_ERROR;
    }
    infilesize=fileinfo.st_size;

  }
  if((config.conf&CONF_UPLOAD) &&
     config.use_resume &&
     (0==config.resume_from)) {
    config.resume_from = -1; /* -1 will then force get-it-yourself */
  }
  if(config.headerfile) {
    /* open file for output: */
    if(strcmp(config.headerfile,"-"))
    {
      headerfilep=(FILE *) fopen(config.headerfile, "wb");
      if (!headerfilep) {
	helpf("Can't open '%s'!\n", config.headerfile);
	return URG_WRITE_ERROR;
      }
    }
    else
      headerfilep=stdout;
  }

  /* This was previously done in urlget, but that was wrong place to do it */
  if(isatty(fileno(outfd)))
    /* we send the output to a tty, and therefor we switch off the progress
       meter right away */
    config.conf |= CONF_NOPROGRESS;

  res = curl_urlget(URGTAG_FILE, outfd,  /* where to store */
                    URGTAG_INFILE, infd, /* for uploads */
                    URGTAG_INFILESIZE, infilesize, /* size of uploaded file */
                    URGTAG_URL, url,     /* what to fetch */
                    URGTAG_PORT, config.porttouse, /* from which port */
                    URGTAG_PROXY, config.proxy, /* proxy to use */
                    URGTAG_FLAGS, config.conf, /* flags */
                    URGTAG_USERPWD, config.userpwd, /* user + passwd */
                    URGTAG_PROXYUSERPWD, config.proxyuserpwd, /* Proxy user + passwd */
                    URGTAG_RANGE, config.range, /* range of document */
                    URGTAG_ERRORBUFFER, errorbuffer,
                    URGTAG_TIMEOUT, config.timeout,
                    URGTAG_POSTFIELDS, config.postfields,
                    URGTAG_REFERER, config.referer,
                    URGTAG_USERAGENT, config.useragent,
                    URGTAG_FTPPORT, config.ftpport,
                    URGTAG_LOW_SPEED_LIMIT, config.low_speed_limit,
                    URGTAG_LOW_SPEED_TIME, config.low_speed_time,
                    URGTAG_RESUME_FROM, config.use_resume?config.resume_from:0,
                    URGTAG_COOKIE, config.cookie,
                    URGTAG_HTTPHEADER, config.headers,
                    URGTAG_HTTPPOST, config.httppost,
                    URGTAG_SSLCERT, config.cert,
                    URGTAG_SSLCERTPASSWD, config.cert_passwd,
                    URGTAG_CRLF, config.crlf,
                    URGTAG_QUOTE, config.quote,
                    URGTAG_WRITEHEADER, headerfilep,
#ifdef MULTIDOC
                    URGTAG_MOREDOCS, config.newdoc,
#endif
                    URGTAG_COOKIEFILE, config.cookiefile,
                    URGTAG_SSLVERSION, config.ssl_version,
                    URGTAG_DONE); /* always terminate the list of tags */

  if((res!=URG_OK) && config.showerror)
    fprintf(stderr, "curl: %s\n", errorbuffer);

  if(urlbuffer)
    free(urlbuffer);
  if (config.outfile)
    fclose(outfd);
  if (config.infile)
    fclose(infd);
  if(headerfilep)
    fclose(headerfilep);

  if(config.url)
    free(config.url);

  curl_slist_free_all(config.quote); /* the checks for config.quote == NULL */

  return(res);
}

