blob: d0bbdcdd7e3fbd7f68266eb4fba1418eaeb516bb [file] [log] [blame]
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* 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.
*
* 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <curl/curl.h>
#include "urlglob.h"
#include "writeout.h"
#include "getpass.h"
#include "homedir.h"
#include "curlutil.h"
#ifdef USE_MANUAL
#include "hugehelp.h"
#endif
#ifdef USE_ENVIRONMENT
#include "writeenv.h"
#endif
#define CURLseparator "--_curl_--"
#ifdef NETWARE
#ifdef __NOVELL_LIBC__
#include <screen.h>
#else
#include <nwconio.h>
#define mkdir mkdir_510
#endif
#endif
#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_UTIME_H
#include <utime.h>
#else
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#endif /* HAVE_UTIME_H */
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h> /* for setlocale() */
#endif
#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 */
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
#include <iconv.h>
/* set default codesets for iconv */
#ifndef CURL_ICONV_CODESET_OF_NETWORK
#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
#endif
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
/* 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. */
#include "memdebug.h"
#endif
#define DEFAULT_MAXREDIRS 50L
#if defined(O_BINARY) && defined(HAVE_SETMODE)
#ifdef __HIGHC__
#define SET_BINMODE(file) _setmode(file,O_BINARY)
#else
#define SET_BINMODE(file) setmode(fileno(file),O_BINARY)
#endif
#else
#define SET_BINMODE(file) ((void)0)
#endif
#ifdef MSDOS
#include <dos.h>
const char *msdosify(const char *);
char *rename_if_dos_device_name(char *);
#ifdef DJGPP
/* we want to glob our own argv[] */
char **__crt0_glob_function (char *arg)
{
(void)arg;
return (char**)0;
}
#endif /* __DJGPP__ */
#endif /* MSDOS */
#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 */
#ifndef CONF_DEFAULT
#define CONF_DEFAULT 0
#endif
#define CONF_ISATTY (1<<0) /* output to tty! */
#define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
#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_DIRLISTONLY (1<<16) /* request nonverbose directory listing */
#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_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 */
#ifdef WIN32
#include <direct.h>
#define F_OK 0
#define mkdir(x,y) (mkdir)(x)
#endif
#ifdef VMS
#include "curlmsg_vms.h"
#endif
/* Support uploading and resuming of >2GB files
*/
#if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
#define struct_stat struct _stati64
#define stat(file,st) _stati64(file,st)
#else
#define struct_stat struct stat
#endif
#ifdef CURL_DOES_CONVERSIONS
#ifdef HAVE_ICONV
iconv_t inbound_cd = (iconv_t)-1;
iconv_t outbound_cd = (iconv_t)-1;
/*
* convert_to_network() is an internal function to convert
* from the host encoding to ASCII on non-ASCII platforms.
*/
static CURLcode
convert_to_network(char *buffer, size_t length)
{
CURLcode rc;
/* translate from the host encoding to the network encoding */
char *input_ptr, *output_ptr;
size_t in_bytes, out_bytes;
/* open an iconv conversion descriptor if necessary */
if(outbound_cd == (iconv_t)-1) {
outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
CURL_ICONV_CODESET_OF_HOST);
if(outbound_cd == (iconv_t)-1) {
return CURLE_CONV_FAILED;
}
}
/* call iconv */
input_ptr = output_ptr = buffer;
in_bytes = out_bytes = length;
rc = iconv(outbound_cd, &input_ptr, &in_bytes,
&output_ptr, &out_bytes);
if ((rc == -1) || (in_bytes != 0)) {
return CURLE_CONV_FAILED;
}
return CURLE_OK;
}
/*
* convert_from_network() is an internal function
* for performing ASCII conversions on non-ASCII platforms.
*/
static CURLcode
convert_from_network(char *buffer, size_t length)
{
CURLcode rc;
/* translate from the network encoding to the host encoding */
char *input_ptr, *output_ptr;
size_t in_bytes, out_bytes;
/* open an iconv conversion descriptor if necessary */
if(inbound_cd == (iconv_t)-1) {
inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
CURL_ICONV_CODESET_OF_NETWORK);
if(inbound_cd == (iconv_t)-1) {
return CURLE_CONV_FAILED;
}
}
/* call iconv */
input_ptr = output_ptr = buffer;
in_bytes = out_bytes = length;
rc = iconv(inbound_cd, &input_ptr, &in_bytes,
&output_ptr, &out_bytes);
if ((rc == -1) || (in_bytes != 0)) {
return CURLE_CONV_FAILED;
}
return CURLE_OK;
}
#endif /* HAVE_ICONV */
static
char convert_char(curl_infotype infotype, char this_char)
{
/* determine how this specific character should be displayed */
switch(infotype) {
case CURLINFO_DATA_IN:
case CURLINFO_DATA_OUT:
case CURLINFO_SSL_DATA_IN:
case CURLINFO_SSL_DATA_OUT:
/* data, treat as ASCII */
if ((this_char >= 0x20) && (this_char < 0x7f)) {
/* printable ASCII hex value: convert to host encoding */
convert_from_network(&this_char, 1);
}
else {
/* non-printable ASCII, use a replacement character */
return UNPRINTABLE_CHAR;
}
/* fall through to default */
default:
/* treat as host encoding */
if (ISPRINT(this_char)
&& (this_char != '\t')
&& (this_char != '\r')
&& (this_char != '\n')) {
/* printable characters excluding tabs and line end characters */
return this_char;
}
break;
}
/* non-printable, use a replacement character */
return UNPRINTABLE_CHAR;
}
#endif /* CURL_DOES_CONVERSIONS */
#ifdef WIN32
/*
* Truncate a file handle at a 64-bit position 'where'.
* Borland doesn't even support 64-bit types.
*/
#ifdef __BORLANDC__
#define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence)
#endif
static int ftruncate64 (int fd, curl_off_t where)
{
curl_off_t curr;
int rc = 0;
if ((curr = _lseeki64(fd, 0, SEEK_CUR)) < 0)
return -1;
if (_lseeki64(fd, where, SEEK_SET) < 0)
return -1;
if (write(fd, 0, 0) < 0)
rc = -1;
_lseeki64(fd, curr, SEEK_SET);
return rc;
}
#define ftruncate(fd,where) ftruncate64(fd,where)
#endif
typedef enum {
TRACE_BIN, /* tcpdump inspired look */
TRACE_ASCII, /* like *BIN but without the hex output */
TRACE_PLAIN /* -v/--verbose type */
} trace;
struct OutStruct {
char *filename;
FILE *stream;
struct Configurable *config;
curl_off_t bytes; /* amount written so far */
curl_off_t init; /* original size (non-zero when appending) */
};
struct Configurable {
CURL *easy; /* once we have one, we keep it here */
bool remote_time;
char *random_file;
char *egd_file;
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 */
bool use_resume;
bool resume_from_current;
bool disable_epsv;
bool disable_eprt;
curl_off_t resume_from;
char *postfields;
long postfieldsize;
char *referer;
long timeout;
long connecttimeout;
long maxredirs;
curl_off_t max_filesize;
char *headerfile;
char *ftpport;
char *iface;
int localport;
int localportrange;
unsigned short porttouse;
char *range;
long low_speed_limit;
long low_speed_time;
bool showerror;
char *userpwd;
char *proxyuserpwd;
char *proxy;
bool proxytunnel;
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;
char *cert;
char *cert_type;
char *cacert;
char *capath;
char *key;
char *key_type;
char *key_passwd;
char *pubkey;
char *hostpubmd5;
char *engine;
bool list_engines;
bool crlf;
char *customrequest;
char *krblevel;
char *trace_dump; /* file to dump the network trace to, or NULL */
FILE *trace_stream;
bool trace_fopened;
trace tracetype;
bool tracetime; /* include timestamp? */
long httpversion;
bool progressmode;
bool nobuffer;
bool globoff;
bool use_httpget;
bool insecure_ok; /* set TRUE to allow insecure SSL connects */
bool create_dirs;
bool ftp_create_dirs;
bool ftp_skip_ip;
bool proxynegotiate;
bool proxyntlm;
bool proxydigest;
bool proxybasic;
bool proxyanyauth;
char *writeout; /* %-styled format string to output */
bool writeenv; /* write results to environment, if available */
FILE *errors; /* if stderr redirect is requested */
bool errors_fopened;
struct curl_slist *quote;
struct curl_slist *postquote;
struct curl_slist *prequote;
long ssl_version;
long ip_version;
curl_TimeCond timecond;
time_t condtime;
struct curl_slist *headers;
struct curl_httppost *httppost;
struct curl_httppost *last_post;
struct curl_slist *telnet_options;
HttpReq httpreq;
/* for bandwidth limiting features: */
curl_off_t sendpersecond; /* send to peer */
curl_off_t recvpersecond; /* receive from peer */
struct timeval lastsendtime;
size_t lastsendsize;
struct timeval lastrecvtime;
size_t lastrecvsize;
bool ftp_ssl;
bool ftp_ssl_reqd;
bool ftp_ssl_control;
bool ftp_ssl_ccc;
int ftp_ssl_ccc_mode;
char *socksproxy; /* set to server string */
int socksver; /* set to CURLPROXY_SOCKS* define */
bool tcp_nodelay;
long req_retry; /* number of retries */
long retry_delay; /* delay between retries (in seconds) */
long retry_maxtime; /* maximum time to keep retrying */
char *ftp_account; /* for ACCT */
char *ftp_alternative_to_user; /* send command if USER/PASS fails */
int ftp_filemethod;
bool ignorecl; /* --ignore-content-length */
bool disable_sessionid;
char *libcurl; /* output libcurl code to this file name */
bool raw;
bool post301;
struct OutStruct *outs;
};
#define WARN_PREFIX "Warning: "
#define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX))
/* produce this text message to the user unless mute was selected */
static void warnf(struct Configurable *config, const char *fmt, ...)
{
if(!(config->conf & CONF_MUTE)) {
va_list ap;
int len;
char *ptr;
char print_buffer[256];
va_start(ap, fmt);
va_start(ap, fmt);
len = vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
va_end(ap);
ptr = print_buffer;
while(len > 0) {
fputs(WARN_PREFIX, config->errors);
if(len > (int)WARN_TEXTWIDTH) {
int cut = WARN_TEXTWIDTH-1;
while(!ISSPACE(ptr[cut]) && cut) {
cut--;
}
if(0 == cut)
/* not a single cutting position was found, just cut it at the
max text width then! */
cut = WARN_TEXTWIDTH-1;
fwrite(ptr, cut + 1, 1, config->errors);
fputs("\n", config->errors);
ptr += cut+1; /* skip the space too */
len -= cut;
}
else {
fputs(ptr, config->errors);
len = 0;
}
}
}
}
/*
* 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)
{
#ifdef DJGPP
/* stop stat() wasting time */
_djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
#endif
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)
{
curl_global_cleanup();
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
/* close iconv conversion descriptor */
if(inbound_cd != (iconv_t)-1)
iconv_close(inbound_cd);
if(outbound_cd != (iconv_t)-1)
iconv_close(outbound_cd);
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
}
static int SetHTTPrequest(struct Configurable *config,
HttpReq req, HttpReq *store)
{
if((*store == HTTPREQ_UNSPEC) ||
(*store == req)) {
*store = req;
return 0;
}
warnf(config, "You can only select one HTTP request!\n");
return 1;
}
static void helpf(const 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' "
#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 */
static void help(void)
{
int i;
static const char * const 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 Pick \"any\" authentication method (H)",
" -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
" --basic Use 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)",
" --data-urlencode <name=data/name@filename> HTTP POST data url encoded (H)",
" --negotiate Use HTTP Negotiate Authentication (H)",
" --digest Use HTTP Digest Authentication (H)",
" --disable-eprt Inhibit using EPRT or LPRT (F)",
" --disable-epsv Inhibit 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 Use the TCP_NODELAY option",
#ifdef USE_ENVIRONMENT
" --environment Write results to environment variables (RISC OS)",
#endif
" -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/SSH)",
" --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
" --pass <pass> Pass phrase for the private key (SSL/SSH)",
" --pubkey <key> Public key file name (SSH)",
" --engine <eng> Crypto engine to use (SSL). \"--engine list\" for list",
" --cacert <file> CA certificate to verify peer against (SSL)",
" --capath <directory> CA directory (made using c_rehash) to verify",
" peer against (SSL)",
" --hostpubmd5 <md5> Hex encoded MD5 string of the host public key. (SSH)",
" --ciphers <list> SSL ciphers to use (SSL)",
" --compressed Request compressed response (using deflate or gzip)",
" --connect-timeout <seconds> Maximum time allowed for connection",
" --create-dirs Create necessary local directory hierarchy",
" --crlf Convert LF to CRLF in upload",
" -f/--fail Fail silently (no output at all) on HTTP errors (H)",
" --ftp-account <data> Account data to send when requested by server (F)",
" --ftp-alternative-to-user String to replace \"USER [name]\" (F)",
" --ftp-create-dirs Create the remote dirs if not present (F)",
" --ftp-method [multicwd/nocwd/singlecwd] Control CWD usage (F)",
" --ftp-pasv Use PASV/EPSV instead of PORT (F)",
" --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n"
" --ftp-ssl Try SSL/TLS for ftp transfer (F)",
" --ftp-ssl-control Require SSL/TLS for ftp login, clear for transfer (F)",
" --ftp-ssl-reqd Require SSL/TLS for ftp transfer (F)",
" --ftp-ssl-ccc Send CCC after authenticating (F)",
" --ftp-ssl-ccc-mode [active/passive] Set CCC mode (F)",
" -F/--form <name=content> Specify HTTP multipart POST data (H)",
" --form-string <name=string> 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",
" -H/--header <line> Custom header to pass to server (H)",
" --ignore-content-length Ignore the HTTP Content-Length header",
" -i/--include Include protocol headers in the output (H/F)",
" -I/--head Show document info only",
" -j/--junk-session-cookies Ignore session cookies read from file (H)",
" --interface <interface> Specify network interface/address to use",
" --krb <level> Enable kerberos with specified security level (F)",
" -k/--insecure Allow connections to SSL sites without certs (H)",
" -K/--config Specify which config file to read",
" --libcurl <file> Dump libcurl equivalent code of this command line",
" -l/--list-only List only names of an FTP directory (F)",
" --limit-rate <rate> Limit transfer speed to this rate",
" --local-port <num>[-num] Force use of these local port numbers\n",
" -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 Use HTTP NTLM authentication (H)",
" -N/--no-buffer Disable buffering of the output stream",
" --no-sessionid Disable SSL session-ID reusing (SSL)",
" -o/--output <file> Write output to <file> instead of stdout",
" -O/--remote-name Write output to a file named as the remote file",
" --post301 Do not switch to GET after following a 301 redirect (H)",
" -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)",
" --proxy-anyauth Pick \"any\" proxy authentication method (H)",
" --proxy-basic Use Basic authentication on the proxy (H)",
" --proxy-digest Use Digest authentication on the proxy (H)",
" --proxy-negotiate Use Negotiate authentication on the proxy (H)",
" --proxy-ntlm Use 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/SFTP)",
" -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)",
" --raw Pass HTTP \"raw\", without any transfer decoding (H)",
" -R/--remote-time Set the remote file's time on the local output",
" --retry <num> Retry request <num> times if transient problems occur",
" --retry-delay <seconds> When retrying, wait this many seconds between each",
" --retry-max-time <seconds> Retry only within this period",
" -s/--silent Silent mode. Don't output anything",
" -S/--show-error Show error. With -s, make curl show errors when they occur",
" --socks4 <host[:port]> Use SOCKS4 proxy on given host + port",
" --socks5 <host[:port]> Use SOCKS5 proxy on given host + port",
" --stderr <file> Where to redirect stderr. - means stdout",
" -t/--telnet-option <OPT=val> Set telnet option",
" --trace <file> Write a debug trace to the given file",
" --trace-ascii <file> Like --trace but without the hex output",
" --trace-time Add time stamps to trace/verbose output",
" -T/--upload-file <file> Transfer <file> to remote site",
" --url <URL> Set URL to work with",
" -u/--user <user[:password]> Set server user and password",
" -U/--proxy-user <user[:password]> Set proxy user and password",
" -v/--verbose Make the operation more talkative",
" -V/--version Show version number and quit",
#ifdef MSDOS
" --wdebug Turn on Watt-32 debugging under DJGPP",
#endif
" -w/--write-out [format] What to output after completion",
" -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)",
" -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++) {
puts(helptext[i]);
#ifdef NETWARE
if (i && ((i % 23) == 0))
pressanykey();
#endif
}
}
struct LongShort {
const char *letter;
const char *lname;
bool extraparam;
};
/* global variable to hold info about libcurl */
static curl_version_info_data *curlinfo;
static int parseconfig(const char *filename,
struct Configurable *config);
static char *my_get_line(FILE *fp);
static int create_dir_hierarchy(const char *outfile);
static void GetStr(char **string,
char *value)
{
if(*string)
free(*string);
if(value)
*string = strdup(value);
else
*string = NULL;
}
static char *file2string(FILE *file)
{
char buffer[256];
char *ptr;
char *string=NULL;
size_t len=0;
size_t stringlen;
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+1);
if(newstring)
string = newstring;
else
break; /* no more strings attached! :-) */
}
else
string = malloc(len+1);
memcpy(&string[stringlen], buffer, len);
stringlen+=len;
}
if (string) {
/* NUL terminate the buffer in case it's treated as a string later */
string[stringlen] = 0;
}
*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);
if(node->infile)
free(node->infile);
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 (!*multi_start)
*multi_start = multi;
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;
multi = multi_type;
}
else {
free (multi);
return NULL;
}
}
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;
}
else {
free (multi);
return NULL;
}
}
if (*multi_current)
(*multi_current)->next = multi;
*multi_current = multi;
return *multi_current;
}
/* Free the items of the list.
*/
static void FreeMultiInfo (struct multi_files *multi_start)
{
struct multi_files *multi;
while (multi_start) {
multi = multi_start;
multi_start = multi_start->next;
free (multi);
}
}
/* Print list of OpenSSL engines supported.
*/
static void list_engines (const struct curl_slist *engines)
{
puts ("Build-time engines:");
if (!engines) {
puts (" <none>");
return;
}
for ( ; engines; engines = engines->next)
printf (" %s\n", engines->data);
}
/***************************************************************************
*
* 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>'.
*
* If literal_value is set, any initial '@' or '<' in the value string
* loses its special meaning, as does any embedded ';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(struct Configurable *config,
char *input,
struct curl_httppost **httppost,
struct curl_httppost **last_post,
bool literal_value)
{
/* 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] && !literal_value) {
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! */
}
type = NULL;
if(sep) {
/* if we got here on a comma, don't do much */
if(FORM_FILE_SEPARATOR == *sep)
ptr = NULL;
else
ptr = sep+1;
*sep=0; /* terminate file name at separator */
while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
/* pass all white spaces */
while(ISSPACE(*ptr))
ptr++;
if(curlx_strnequal("type=", ptr, 5)) {
/* set type pointer */
type = &ptr[5];
/* verify that this is a fine type specifier */
if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
major, minor)) {
warnf(config, "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;
if(*sep) {
*sep=0; /* zero terminate type string */
ptr=sep+1;
}
else
ptr = NULL; /* end */
}
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)) {
warnf(config, "Error building form post!\n");
free(contents);
FreeMultiInfo (multi_start);
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);
FreeMultiInfo (multi_start);
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;
FreeMultiInfo (multi_start);
if (curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
warnf(config, "curl_formadd failed!\n");
free(forms);
free(contents);
return 5;
}
free(forms);
}
}
else {
struct curl_forms info[4];
int i = 0;
char *ct = literal_value? NULL: strstr(contp, ";type=");
info[i].option = CURLFORM_COPYNAME;
info[i].value = name;
i++;
if(ct) {
info[i].option = CURLFORM_CONTENTTYPE;
info[i].value = &ct[6];
i++;
ct[0]=0; /* zero terminate here */
}
if( contp[0]=='<' && !literal_value) {
info[i].option = CURLFORM_FILECONTENT;
info[i].value = contp+1;
i++;
info[i].option = CURLFORM_END;
if (curl_formadd(httppost, last_post,
CURLFORM_ARRAY, info, CURLFORM_END ) != 0) {
warnf(config, "curl_formadd failed, possibly the file %s is bad!\n",
contp+1);
free(contents);
return 6;
}
}
else {
#ifdef CURL_DOES_CONVERSIONS
convert_to_network(contp, strlen(contp));
#endif
info[i].option = CURLFORM_COPYCONTENTS;
info[i].value = contp;
i++;
info[i].option = CURLFORM_END;
if (curl_formadd(httppost, last_post,
CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
warnf(config, "curl_formadd failed!\n");
free(contents);
return 7;
}
}
}
}
else {
warnf(config, "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_BAD_NUMERIC,
PARAM_LIBCURL_DOESNT_SUPPORT,
PARAM_NO_MEM,
PARAM_LAST
} ParameterError;
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);
}
#else
(void)str;
#endif
}
/*
* Parse the string and write the integer in the given address. Return
* non-zero on failure, zero on success.
*
* The string must start with a digit to be valid.
*
* Since this function gets called with the 'nextarg' pointer from within the
* getparameter a lot, we must check it for NULL before accessing the str
* data.
*/
static int str2num(long *val, char *str)
{
int retcode = 0;
if(str && ISDIGIT(*str))
*val = atoi(str);
else
retcode = 1; /* badness */
return retcode;
}
/**
* Parses the given string looking for an offset (which may be
* a larger-than-integer value).
*
* @param val the offset to populate
* @param str the buffer containing the offset
* @return zero if successful, non-zero if failure.
*/
static int str2offset(curl_off_t *val, char *str)
{
#if SIZEOF_CURL_OFF_T > 4
/* Ugly, but without going through a bunch of rigmarole, we don't have the
* definitions for LLONG_{MIN,MAX} or LONG_LONG_{MIN,MAX}.
*/
#ifndef LLONG_MAX
#if defined(_MSC_VER)
#define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFi64
#define LLONG_MIN (curl_off_t)0x8000000000000000i64
#elif defined(_CRAYC)
#define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFF
#define LLONG_MIN (curl_off_t)0x8000000000000000
#else
#define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFLL
#define LLONG_MIN (curl_off_t)0x8000000000000000LL
#endif
#endif
/* this is a duplicate of the function that is also used in libcurl */
*val = curlx_strtoofft(str, NULL, 0);
if ((*val == LLONG_MAX || *val == LLONG_MIN) && ERRNO == ERANGE)
return 1;
#else
*val = strtol(str, NULL, 0);
if ((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
return 1;
#endif
return 0;
}
static void checkpasswd(const char *kind, /* for what purpose */
char **userpwd) /* pointer to allocated string */
{
char *ptr;
if(!*userpwd)
return;
ptr = strchr(*userpwd, ':');
if(!ptr) {
/* no password present, prompt for one */
char passwd[256]="";
char prompt[256];
size_t passwdlen;
size_t userlen = strlen(*userpwd);
char *passptr;
/* build a nice-looking prompt */
curlx_msnprintf(prompt, sizeof(prompt),
"Enter %s password for user '%s':",
kind, *userpwd);
/* get password */
getpass_r(prompt, passwd, sizeof(passwd));
passwdlen = strlen(passwd);
/* extend the allocated memory area to fit the password too */
passptr = realloc(*userpwd,
passwdlen + 1 + /* an extra for the colon */
userlen + 1); /* an extra for the zero */
if(passptr) {
/* append the password separated with a colon */
passptr[userlen]=':';
memcpy(&passptr[userlen+1], passwd, passwdlen+1);
*userpwd = passptr;
}
}
}
static ParameterError add2list(struct curl_slist **list,
char *ptr)
{
struct curl_slist *newlist = curl_slist_append(*list, ptr);
if(newlist)
*list = newlist;
else
return PARAM_NO_MEM;
return PARAM_OK;
}
static int ftpfilemethod(struct Configurable *config, char *str)
{
if(curlx_strequal("singlecwd", str))
return CURLFTPMETHOD_SINGLECWD;
if(curlx_strequal("nocwd", str))
return CURLFTPMETHOD_NOCWD;
if(curlx_strequal("multicwd", str))
return CURLFTPMETHOD_MULTICWD;
warnf(config, "unrecognized ftp file method '%s', using default\n", str);
return CURLFTPMETHOD_MULTICWD;
}
static int ftpcccmethod(struct Configurable *config, char *str)
{
if(curlx_strequal("passive", str))
return CURLFTPSSL_CCC_PASSIVE;
if(curlx_strequal("active", str))
return CURLFTPSSL_CCC_ACTIVE;
warnf(config, "unrecognized ftp CCC method '%s', using default\n", str);
return CURLFTPSSL_CCC_PASSIVE;
}
static ParameterError 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 subletter=0; /* subletters can only occur on long options */
int rc; /* generic return code variable */
const char *parse=NULL;
unsigned int j;
time_t now;
int hit=-1;
bool longopt=FALSE;
bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
ParameterError err;
/* single-letter,
long-name,
boolean whether it takes an additional argument
*/
static const struct LongShort aliases[]= {
/* all these ones, starting with "*" or "$" as a short-option have *no*
short option to mention. */
{"*", "url", TRUE},
{"*a", "random-file", TRUE},
{"*b", "egd-file", TRUE},
{"*c", "connect-timeout", TRUE},
{"*d", "ciphers", TRUE},
{"*e", "disable-epsv", FALSE},
#ifdef USE_ENVIRONMENT
{"*f", "environment", FALSE},
#endif
{"*g", "trace", TRUE},
{"*h", "trace-ascii", TRUE},
{"*i", "limit-rate", TRUE},
{"*j", "compressed", FALSE}, /* might take an arg someday */
{"*k", "digest", FALSE},
{"*l", "negotiate", FALSE},
{"*m", "ntlm", FALSE},
{"*n", "basic", FALSE},
{"*o", "anyauth", FALSE},
#ifdef MSDOS
{"*p", "wdebug", FALSE},
#endif
{"*q", "ftp-create-dirs", FALSE},
{"*r", "create-dirs", FALSE},
{"*s", "max-redirs", TRUE},
{"*t", "proxy-ntlm", FALSE},
{"*u", "crlf", FALSE},
{"*v", "stderr", TRUE},
{"*w", "interface", TRUE},
{"*x", "krb" , TRUE},
{"*x", "krb4" , TRUE}, /* this is the previous name */
{"*y", "max-filesize", TRUE},
{"*z", "disable-eprt", FALSE},
{"$a", "ftp-ssl", FALSE},
{"$b", "ftp-pasv", FALSE},
{"$c", "socks5", TRUE},
{"$c", "socks", TRUE}, /* this is how the option was documented but
we prefer the --socks5 version for explicit
version */
{"$d", "tcp-nodelay",FALSE},
{"$e", "proxy-digest", FALSE},
{"$f", "proxy-basic", FALSE},
{"$g", "retry", TRUE},
{"$h", "retry-delay", TRUE},
{"$i", "retry-max-time", TRUE},
{"$k", "proxy-negotiate", FALSE},
{"$m", "ftp-account", TRUE},
{"$n", "proxy-anyauth", FALSE},
{"$o", "trace-time", FALSE},
{"$p", "ignore-content-length", FALSE},
{"$q", "ftp-skip-pasv-ip", FALSE},
{"$r", "ftp-method", TRUE},
{"$s", "local-port", TRUE},
{"$t", "socks4", TRUE},
{"$u", "ftp-alternative-to-user", TRUE},
{"$v", "ftp-ssl-reqd", FALSE},
{"$w", "no-sessionid", FALSE},
{"$x", "ftp-ssl-control", FALSE},
{"$y", "ftp-ssl-ccc", FALSE},
{"$j", "ftp-ssl-ccc-mode", TRUE},
{"$z", "libcurl", TRUE},
{"$#", "raw", FALSE},
{"$0", "post301", FALSE},
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"2", "sslv2", FALSE},
{"3", "sslv3", FALSE},
{"4", "ipv4", FALSE},
{"6", "ipv6", FALSE},
{"a", "append", FALSE},
{"A", "user-agent", TRUE},
{"b", "cookie", TRUE},
{"B", "use-ascii", FALSE},
{"c", "cookie-jar", TRUE},
{"C", "continue-at", TRUE},
{"d", "data", TRUE},
{"da", "data-ascii", TRUE},
{"db", "data-binary", TRUE},
{"de", "data-urlencode", TRUE},
{"D", "dump-header", TRUE},
{"e", "referer", TRUE},
{"E", "cert", TRUE},
{"Ea", "cacert", TRUE},
{"Eb","cert-type", TRUE},
{"Ec","key", TRUE},
{"Ed","key-type", TRUE},
{"Ee","pass", TRUE},
{"Ef","engine", TRUE},
{"Eg","capath ", TRUE},
{"Eh","pubkey", TRUE},
{"Ei", "hostpubmd5", TRUE},
{"f", "fail", FALSE},
{"F", "form", TRUE},
{"Fs","form-string", TRUE},
{"g", "globoff", FALSE},
{"G", "get", FALSE},
{"h", "help", FALSE},
{"H", "header", TRUE},
{"i", "include", FALSE},
{"I", "head", FALSE},
{"j", "junk-session-cookies", FALSE},
{"k", "insecure", FALSE},
{"K", "config", TRUE},
{"l", "list-only", FALSE},
{"L", "location", FALSE},
{"Lt", "location-trusted", FALSE},
{"m", "max-time", TRUE},
{"M", "manual", FALSE},
{"n", "netrc", FALSE},
{"no", "netrc-optional", FALSE},
{"N", "no-buffer", FALSE},
{"o", "output", TRUE},
{"O", "remote-name", FALSE},
{"p", "proxytunnel", FALSE},
{"P", "ftpport", TRUE}, /* older version */
{"P", "ftp-port", TRUE},
{"q", "disable", FALSE},
{"Q", "quote", TRUE},
{"r", "range", TRUE},
{"R", "remote-time", FALSE},
{"s", "silent", FALSE},
{"S", "show-error", FALSE},
{"t", "telnet-options", TRUE},
{"T", "upload-file", TRUE},
{"u", "user", TRUE},
{"U", "proxy-user", TRUE},
{"v", "verbose", FALSE},
{"V", "version", FALSE},
{"w", "write-out", TRUE},
{"x", "proxy", TRUE},
{"X", "request", TRUE},
{"X", "http-request", TRUE}, /* OBSOLETE VERSION */
{"Y", "speed-limit", TRUE},
{"y", "speed-time", TRUE},
{"z", "time-cond", TRUE},
{"#", "progress-bar",FALSE},
};
if(('-' != flag[0]) ||
(('-' == flag[0]) && ('-' == flag[1]))) {
/* this should be a long name */
char *word=('-' == flag[0])?flag+2:flag;
size_t fnam=strlen(word);
int numhits=0;
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
if(curlx_strnequal(aliases[j].lname, word, fnam)) {
longopt = TRUE;
numhits++;
if(curlx_strequal(aliases[j].lname, word)) {
parse = aliases[j].letter;
hit = j;
numhits = 1; /* a single unique hit */
break;
}
parse = aliases[j].letter;
hit = j;
}
}
if(numhits>1) {
/* this is at least the second match! */
return PARAM_OPTION_AMBIGUOUS;
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
}
}
else {
flag++; /* prefixed with one dash, pass it */
hit=-1;
parse = flag;
}
do {
/* we can loop here if we have multiple single-letters */
if(!longopt) {
if(NULL != parse) {
letter = (char)*parse;
}
else {
letter = '\0';
}
subletter='\0';
}
else {
letter = parse[0];
subletter = parse[1];
}
*usedarg = FALSE; /* default is that we don't use the arg */
if(hit < 0) {
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
if(letter == aliases[j].letter[0]) {
hit = j;
break;
}
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
}
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
}
if(!longopt && aliases[hit].extraparam && parse[1]) {
nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
singleopt=TRUE; /* don't loop anymore after this */
}
else if(!nextarg && aliases[hit].extraparam) {
return PARAM_REQUIRES_PARAMETER;
}
else if(nextarg && aliases[hit].extraparam)
*usedarg = TRUE; /* mark it as used */
switch(letter) {
case '*': /* options without a short option */
switch(subletter) {
case 'a': /* random-file */
GetStr(&config->random_file, nextarg);
break;
case 'b': /* egd-file */
GetStr(&config->egd_file, nextarg);
break;
case 'c': /* connect-timeout */
if(str2num(&config->connecttimeout, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'd': /* ciphers */
GetStr(&config->cipher_list, nextarg);
break;
case 'e': /* --disable-epsv */
config->disable_epsv ^= TRUE;
break;
#ifdef USE_ENVIRONMENT
case 'f':
config->writeenv ^= TRUE;
break;
#endif
case 'g': /* --trace */
GetStr(&config->trace_dump, nextarg);
if(config->tracetype && (config->tracetype != TRACE_BIN))
warnf(config, "--trace overrides an earlier trace/verbose option\n");
config->tracetype = TRACE_BIN;
break;
case 'h': /* --trace-ascii */
GetStr(&config->trace_dump, nextarg);
if(config->tracetype && (config->tracetype != TRACE_ASCII))
warnf(config,
"--trace-ascii overrides an earlier trace/verbose option\n");
config->tracetype = TRACE_ASCII;
break;
case 'i': /* --limit-rate */
{
/* We support G, M, K too */
char *unit;
curl_off_t value = curlx_strtoofft(nextarg, &unit, 0);
if(!*unit)
unit=(char *)"b";
else if(strlen(unit) > 1)
unit=(char *)"w"; /* unsupported */
switch(*unit) {
case 'G':
case 'g':
value *= 1024*1024*1024;
break;
case 'M':
case 'm':
value *= 1024*1024;
break;
case 'K':
case 'k':
value *= 1024;
break;
case 'b':
case 'B':
/* for plain bytes, leave as-is */
break;
default:
warnf(config, "unsupported rate unit. Use G, M, K or B!\n");
return PARAM_BAD_USE;
}
config->recvpersecond = value;
config->sendpersecond = value;
}
break;
case 'j': /* --compressed */
config->encoding ^= TRUE;
break;
case 'k': /* --digest */
config->authtype = CURLAUTH_DIGEST;
break;
case 'l': /* --negotiate */
if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
config->authtype = CURLAUTH_GSSNEGOTIATE;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'm': /* --ntlm */
if(curlinfo->features & CURL_VERSION_NTLM)
config->authtype = CURLAUTH_NTLM;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'n': /* --basic for completeness */
config->authtype = CURLAUTH_BASIC;
break;
case 'o': /* --anyauth, let libcurl pick it */
config->authtype = CURLAUTH_ANY;
break;
#ifdef MSDOS
case 'p': /* --wdebug */
dbug_init();
break;
#endif
case 'q': /* --ftp-create-dirs */
config->ftp_create_dirs ^= TRUE;
break;
case 'r': /* --create-dirs */
config->create_dirs = TRUE;
break;
case 's': /* --max-redirs */
/* specified max no of redirects (http(s)) */
if(str2num(&config->maxredirs, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 't': /* --proxy-ntlm */
if(curlinfo->features & CURL_VERSION_NTLM)
config->proxyntlm ^= TRUE;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'u': /* --crlf */
/* LF -> CRLF conversinon? */
config->crlf = TRUE;
break;
case 'v': /* --stderr */
if(strcmp(nextarg, "-")) {
FILE *newfile = fopen(nextarg, "wt");
if(!newfile)
warnf(config, "Failed to open %s!\n", nextarg);
else {
config->errors = newfile;
config->errors_fopened = TRUE;
}
}
else
config->errors = stdout;
break;
case 'w': /* --interface */
/* interface */
GetStr(&config->iface, nextarg);
break;
case 'x': /* --krb */
/* kerberos level string */
if(curlinfo->features & (CURL_VERSION_KERBEROS4 | CURL_VERSION_GSSNEGOTIATE))
GetStr(&config->krblevel, nextarg);
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'y': /* --max-filesize */
if(str2offset(&config->max_filesize, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'z': /* --disable-eprt */
config->disable_eprt ^= TRUE;
break;
default: /* the URL! */
{
struct getout *url;
if(config->url_get || (config->url_get=config->url_list)) {
/* there's a node here, if it already is filled-in continue to find
an "empty" node */
while(config->url_get && (config->url_get->flags&GETOUT_URL))
config->url_get = config->url_get->next;
}
/* now there might or might not be an available node to fill in! */
if(config->url_get)
/* existing node */
url = config->url_get;
else
/* there was no free node, create one! */
url=new_getout(config);
if(url) {
/* fill in the URL */
GetStr(&url->url, nextarg);
url->flags |= GETOUT_URL;
}
}
}
break;
case '$': /* more options without a short option */
switch(subletter) {
case 'a': /* --ftp-ssl */
config->ftp_ssl ^= TRUE;
break;
case 'b': /* --ftp-pasv */
if(config->ftpport)
free(config->ftpport);
config->ftpport = NULL;
break;
case 'c': /* --socks5 specifies a socks5 proxy to use */
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS5;
break;
case 't': /* --socks4 specifies a socks4 proxy to use */
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS4;
break;
case 'd': /* --tcp-nodelay option */
config->tcp_nodelay ^= TRUE;
break;
case 'e': /* --proxy-digest */
config->proxydigest ^= TRUE;
break;
case 'f': /* --proxy-basic */
config->proxybasic ^= TRUE;
break;
case 'g': /* --retry */
if(str2num(&config->req_retry, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'h': /* --retry-delay */
if(str2num(&config->retry_delay, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'i': /* --retry-max-time */
if(str2num(&config->retry_maxtime, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'k': /* --proxy-negotiate */
if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
config->proxynegotiate ^= TRUE;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'm': /* --ftp-account */
GetStr(&config->ftp_account, nextarg);
break;
case 'n': /* --proxy-anyauth */
config->proxyanyauth ^= TRUE;
break;
case 'o': /* --trace-time */
config->tracetime ^= TRUE;
break;
case 'p': /* --ignore-content-length */
config->ignorecl ^= TRUE;
break;
case 'q': /* --ftp-skip-pasv-ip */
config->ftp_skip_ip ^= TRUE;
break;
case 'r': /* --ftp-method (undocumented at this point) */
config->ftp_filemethod = ftpfilemethod(config, nextarg);
break;
case 's': /* --local-port */
rc = sscanf(nextarg, "%d - %d",
&config->localport,
&config->localportrange);
if(!rc)
return PARAM_BAD_USE;
else if(rc == 1)
config->localportrange = 1; /* default number of ports to try */
else {
config->localportrange -= config->localport;
if(config->localportrange < 1) {
warnf(config, "bad range input\n");
return PARAM_BAD_USE;
}
}
break;
case 'u': /* --ftp-alternative-to-user */
GetStr(&config->ftp_alternative_to_user, nextarg);
break;
case 'v': /* --ftp-ssl-reqd */
config->ftp_ssl_reqd ^= TRUE;
break;
case 'w': /* --no-sessionid */
config->disable_sessionid ^= TRUE;
break;
case 'x': /* --ftp-ssl-control */
config->ftp_ssl_control ^= TRUE;
break;
case 'y': /* --ftp-ssl-ccc */
config->ftp_ssl_ccc ^= TRUE;
if(!config->ftp_ssl_ccc_mode)
config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
break;
case 'j': /* --ftp-ssl-ccc-mode */
config->ftp_ssl_ccc = TRUE;
config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
break;
case 'z': /* --libcurl */
GetStr(&config->libcurl, nextarg);
break;
case '#': /* --raw */
config->raw ^= TRUE;
break;
case '0': /* --post301 */
config->post301 ^= TRUE;
break;
}
break;
case '#': /* --progress-bar */
config->progressmode ^= CURL_PROGRESS_BAR;
break;
case '0':
/* HTTP version 1.0 */
config->httpversion = CURL_HTTP_VERSION_1_0;
break;
case '1':
/* TLS version 1 */
config->ssl_version = CURL_SSLVERSION_TLSv1;
break;
case '2':
/* SSL version 2 */
config->ssl_version = CURL_SSLVERSION_SSLv2;
break;
case '3':
/* SSL version 3 */
config->ssl_version = CURL_SSLVERSION_SSLv3;
break;
case '4':
/* IPv4 */
config->ip_version = 4;
break;
case '6':
/* IPv6 */
config->ip_version = 6;
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(nextarg[0] == '@') {
nextarg++;
}
else if(strchr(nextarg, '=')) {
/* A cookie string must have a =-letter */
GetStr(&config->cookie, nextarg);
break;
}
/* We have a cookie file to read from! */
GetStr(&config->cookiefile, nextarg);
break;
case 'B':
/* use ASCII/text when transfering */
config->conf ^= CONF_GETTEXT;
break;
case 'c':
/* get the file name to dump all cookies in */
GetStr(&config->cookiejar, nextarg);
break;
case 'C':
/* This makes us continue an ftp transfer at given position */
if(!curlx_strequal(nextarg, "-")) {
if(str2offset(&config->resume_from, nextarg))
return PARAM_BAD_NUMERIC;
config->resume_from_current = FALSE;
}
else {
config->resume_from_current = TRUE;
config->resume_from = 0;
}
config->use_resume=TRUE;
break;
case 'd':
/* postfield data */
{
char *postdata=NULL;
FILE *file;
if(subletter == 'e') { /* --data-urlencode*/
/* [name]=[content], we encode the content part only
* [name]@[file name]
*
* Case 2: we first load the file using that name and then encode
* the content.
*/
char *p = strchr(nextarg, '=');
long size = 0;
size_t nlen;
if(!p)
p = strchr(nextarg, '@');
if(!p) {
warnf(config, "bad use of --data-urlencode\n");
return PARAM_BAD_USE;
}
nlen = p - nextarg; /* length of the name part */
if('@' == *p) {
/* a '@' letter, it means that a file name or - (stdin) follows */
p++; /* pass the separator */
if(curlx_strequal("-", p)) {
file = stdin;
SET_BINMODE(stdin);
}
else {
file = fopen(p, "rb");
if(!file)
warnf(config,
"Couldn't read data from file \"%s\", this makes "
"an empty POST.\n", nextarg);
}
postdata = file2memory(file, &size);
if(file && (file != stdin))
fclose(file);
}
else {
GetStr(&postdata, ++p);
size = strlen(postdata);
}
if(!postdata) {
/* no data from the file, point to a zero byte string to make this
get sent as a POST anyway */
postdata=strdup("");
}
else {
char *enc = curl_easy_escape(config->easy, postdata, size);
if(enc) {
/* now make a string with the name from above and append the
encoded string */
size_t outlen = nlen + strlen(enc) + 2;
char *n = malloc(outlen);
if(!n)
return PARAM_NO_MEM;
snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
curl_free(enc);
free(postdata);
if(n) {
postdata = n;
}
else
return PARAM_NO_MEM;
}
else
return PARAM_NO_MEM;
}
}
else if('@' == *nextarg) {
/* the data begins with a '@' letter, it means that a file name
or - (stdin) follows */
nextarg++; /* pass the @ */
if(curlx_strequal("-", nextarg)) {
file = stdin;
if(subletter == 'b') /* forced data-binary */
SET_BINMODE(stdin);
}
else {
file = fopen(nextarg, "rb");
if(!file)
warnf(config, "Couldn't read data from file \"%s\", this makes "
"an empty POST.\n", nextarg);
}
if(subletter == 'b') /* forced binary */
postdata = file2memory(file, &config->postfieldsize);
else
postdata = file2string(file);
if(file && (file != stdin))
fclose(file);
if(!postdata) {
/* no data from the file, point to a zero byte string to make this
get sent as a POST anyway */
postdata=strdup("");
}
}
else {
GetStr(&postdata, nextarg);
}
#ifdef CURL_DOES_CONVERSIONS
if(subletter != 'b') { /* NOT forced binary, convert to ASCII */
convert_to_network(postdata, strlen(postdata));
}
#endif
if(config->postfields) {
/* we already have a string, we append this one
with a separating &-letter */
char *oldpost=config->postfields;
size_t newlen = strlen(oldpost) + strlen(postdata) + 2;
config->postfields=malloc(newlen);
if(!config->postfields) {
free(postdata);
return PARAM_NO_MEM;
}
/* use ASCII value 0x26 for '&' to accommodate non-ASCII platforms */
snprintf(config->postfields, newlen, "%s\x26%s", oldpost, postdata);
free(oldpost);
free(postdata);
}
else
config->postfields=postdata;
}
/*
We can't set the request type here, as this data might be used in
a simple GET if -G is used. Already or soon.
if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
return PARAM_BAD_USE;
*/
break;
case 'D':
/* dump-header to given file name */
GetStr(&config->headerfile, nextarg);
break;
case 'e':
{
char *ptr = strstr(nextarg, ";auto");
if(ptr) {
/* Automatic referer requested, this may be combined with a
set initial one */
config->conf |= CONF_AUTO_REFERER;
*ptr = 0; /* zero terminate here */
}
GetStr(&config->referer, nextarg);
}
break;
case 'E':
switch(subletter) {
case 'a': /* CA info PEM file */
/* CA info PEM file */
GetStr(&config->cacert, nextarg);
break;
case 'b': /* cert file type */
GetStr(&config->cert_type, nextarg);
break;
case 'c': /* private key file */
GetStr(&config->key, nextarg);
break;
case 'd': /* private key file type */
GetStr(&config->key_type, nextarg);
break;
case 'e': /* private key passphrase */
GetStr(&config->key_passwd, nextarg);
cleanarg(nextarg);
break;
case 'f': /* crypto engine */
GetStr(&config->engine, nextarg);
if (config->engine && curlx_strequal(config->engine,"list"))
config->list_engines = TRUE;
break;
case 'g': /* CA info PEM file */
/* CA cert directory */
GetStr(&config->capath, nextarg);
break;
case 'h': /* --pubkey public key file */
GetStr(&config->pubkey, nextarg);
break;
case 'i': /* --hostpubmd5 md5 of the host public key */
GetStr(&config->hostpubmd5, nextarg);
if (!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
return PARAM_BAD_USE;
break;
default: /* certificate file */
{
char *ptr = strchr(nextarg, ':');
/* Since we live in a world of weirdness and confusion, the win32
dudes can use : when using drive letters and thus
c:\file:password needs to work. In order not to break
compatibility, we still use : as separator, but we try to detect
when it is used for a file name! On windows. */
#ifdef WIN32
if(ptr &&
(ptr == &nextarg[1]) &&
(nextarg[2] == '\\' || nextarg[2] == '/') &&
(ISALPHA(nextarg[0])) )
/* colon in the second column, followed by a backslash, and the
first character is an alphabetic letter:
this is a drive letter colon */
ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
#endif
if(ptr) {
/* we have a password too */
*ptr=0;
ptr++;
GetStr(&config->key_passwd, ptr);
}
GetStr(&config->cert, nextarg);
cleanarg(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(formparse(config,
nextarg,
&config->httppost,
&config->last_post,
(bool) (subletter=='s'))) /* 's' means literal string */
return PARAM_BAD_USE;
if(SetHTTPrequest(config, HTTPREQ_POST, &config->httpreq))
return PARAM_BAD_USE;
break;
case 'g': /* g disables URLglobbing */
config->globoff ^= TRUE;
break;
case 'G': /* HTTP GET */
config->use_httpget = TRUE;
break;
case 'h': /* h for help */
help();
return PARAM_HELP_REQUESTED;
case 'H':
/* A custom header to append to a list */
err = add2list(&config->headers, nextarg);
if(err)
return err;
break;
case 'i':
config->conf ^= CONF_HEADER; /* include the HTTP header as well */
break;
case 'j':
config->cookiesession ^= TRUE;
break;
case 'I':
/*
* This is a bit tricky. We either SET both bits, or we clear both
* bits. Let's not make any other outcomes from this.
*/
if((CONF_HEADER|CONF_NOBODY) !=
(config->conf&(CONF_HEADER|CONF_NOBODY)) ) {
/* one of them weren't set, set both */
config->conf |= (CONF_HEADER|CONF_NOBODY);
if(SetHTTPrequest(config, HTTPREQ_HEAD, &config->httpreq))
return PARAM_BAD_USE;
}
else {
/* both were set, clear both */
config->conf &= ~(CONF_HEADER|CONF_NOBODY);
if(SetHTTPrequest(config, HTTPREQ_GET, &config->httpreq))
return PARAM_BAD_USE;
}
break;
case 'k': /* allow insecure SSL connects */
config->insecure_ok ^= TRUE;
break;
case 'K': /* parse config file */
if(parseconfig(nextarg, config))
warnf(config, "error trying read config from the '%s' file\n",
nextarg);
break;
case 'l':
config->conf ^= CONF_DIRLISTONLY; /* only list the names of the FTP dir */
break;
case 'L':
config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
switch (subletter) {
case 't':
/* Continue to send authentication (user+password) when following
* locations, even when hostname changed */
config->conf ^= CONF_UNRESTRICTED_AUTH;
break;
}
break;
case 'm':
/* specified max time */
if(str2num(&config->timeout, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'M': /* M for manual, huge help */
#ifdef USE_MANUAL
hugehelp();
return PARAM_HELP_REQUESTED;
#else
warnf(config,
"built-in manual was disabled at build-time!\n");
return PARAM_OPTION_UNKNOWN;
#endif
case 'n':
switch(subletter) {
case 'o': /* CA info PEM file */
/* use .netrc or URL */
config->conf ^= CONF_NETRC_OPT;
break;
default:
/* pick info from .netrc, if this is used for http, curl will
automatically enfore user+password with the request */
config->conf ^= CONF_NETRC;
break;
}
break;
case 'N':
/* disable the output I/O buffering */
config->nobuffer ^= 1;
break;
case 'o':
case 'O':
/* output file */
{
struct getout *url;
if(config->url_out || (config->url_out=config->url_list)) {
/* there's a node here, if it already is filled-in continue to find
an "empty" node */
while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
config->url_out = config->url_out->next;
}
/* now there might or might not be an available node to fill in! */
if(config->url_out)
/* existing node */
url = config->url_out;
else
/* there was no free node, create one! */
url=new_getout(config);
if(url) {
/* fill in the outfile */
if('o' == letter)
GetStr(&url->outfile, nextarg);
else {
url->outfile=NULL; /* leave it */
url->flags |= GETOUT_USEREMOTE;
}
url->flags |= GETOUT_OUTFILE;
}
}
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!
*/
GetStr(&config->ftpport, nextarg);
break;
case 'p':
/* proxy tunnel for non-http protocols */
config->proxytunnel ^= TRUE;
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 */
err = PARAM_OK;
switch(nextarg[0]) {
case '-':
/* prefixed with a dash makes it a POST TRANSFER one */
nextarg++;
err = add2list(&config->postquote, nextarg);
break;
case '+':
/* prefixed with a plus makes it a just-before-transfer one */
nextarg++;
err = add2list(&config->prequote, nextarg);
break;
default:
err = add2list(&config->quote, nextarg);
break;
}
if(err)
return err;
break;
case 'r':
/* Specifying a range WITHOUT A DASH will create an illegal HTTP range
(and won't actually be range by definition). The man page previously
claimed that to be a good way, why this code is added to work-around
it. */
if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
char buffer[32];
curl_off_t off;
warnf(config,
"A specfied range MUST include at least one dash (-). "
"Appending one for you!\n");
off = curlx_strtoofft(nextarg, NULL, 10);
snprintf(buffer, sizeof(buffer), "%Od-", off);
GetStr(&config->range, buffer);
}
{
/* byte range requested */
char* tmp_range;
tmp_range=nextarg;
while(*tmp_range != '\0') {
if(!ISDIGIT(*tmp_range)&&*tmp_range!='-'&&*tmp_range!=',') {
warnf(config,"Invalid character is found in given range. "
"A specified range MUST have only digits in "
"\'start\'-\'stop\'. The server's response to this "
"request is uncertain.\n");
break;
}
tmp_range++;
}
/* byte range requested */
GetStr(&config->range, nextarg);
}
break;
case 'R':
/* use remote file's time */
config->remote_time ^= TRUE;
break;
case 's':
/* don't show progress meter, don't show errors : */
config->conf ^= (CONF_MUTE|CONF_NOPROGRESS);
config->showerror ^= TRUE; /* toggle off */
break;
case 'S':
/* show errors */
config->showerror ^= TRUE; /* toggle on if used with -s */
break;
case 't':
/* Telnet options */
err = add2list(&config->telnet_options, nextarg);
if(err)
return err;
break;
case 'T':
/* we are uploading */
{
struct getout *url;
if(config->url_out || (config->url_out=config->url_list)) {
/* there's a node here, if it already is filled-in continue to find
an "empty" node */
while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
config->url_out = config->url_out->next;
}
/* now there might or might not be an available node to fill in! */
if(config->url_out)
/* existing node */
url = config->url_out;
else
/* there was no free node, create one! */
url=new_getout(config);
if(url) {
url->flags |= GETOUT_UPLOAD; /* mark -T used */
if(!*nextarg)
url->flags |= GETOUT_NOUPLOAD;
else {
/* "-" equals stdin, but keep the string around for now */
GetStr(&url->infile, nextarg);
}
}
}
break;
case 'u':
/* user:password */
GetStr(&config->userpwd, nextarg);
cleanarg(nextarg);
checkpasswd("host", &config->userpwd);
break;
case 'U':
/* Proxy user:password */
GetStr(&config->proxyuserpwd, nextarg);
cleanarg(nextarg);
checkpasswd("proxy", &config->proxyuserpwd);
break;
case 'v':
/* the '%' thing here will cause the trace get sent to stderr */
GetStr(&config->trace_dump, (char *)"%");
if(config->tracetype && (config->tracetype != TRACE_PLAIN))
warnf(config,
"-v/--verbose overrides an earlier trace/verbose option\n");
config->tracetype = TRACE_PLAIN;
break;
case 'V':
{
const char * const *proto;
printf(CURL_ID "%s\n", curl_version());
if (curlinfo->protocols) {
printf("Protocols: ");
for (proto=curlinfo->protocols; *proto; ++proto) {
printf("%s ", *proto);
}
puts(""); /* newline */
}
if(curlinfo->features) {
unsigned int i;
struct feat {
const char *name;
int bitmask;
};
static const struct feat feats[] = {
{"AsynchDNS", CURL_VERSION_ASYNCHDNS},
{"Debug", CURL_VERSION_DEBUG},
{"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
{"IDN", CURL_VERSION_IDN},
{"IPv6", CURL_VERSION_IPV6},
{"Largefile", CURL_VERSION_LARGEFILE},
{"NTLM", CURL_VERSION_NTLM},
{"SPNEGO", CURL_VERSION_SPNEGO},
{"SSL", CURL_VERSION_SSL},
{"SSPI", CURL_VERSION_SSPI},
{"krb4", CURL_VERSION_KERBEROS4},
{"libz", CURL_VERSION_LIBZ},
{"CharConv", CURL_VERSION_CONV}
};
printf("Features: ");
for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
if(curlinfo->features & feats[i].bitmask)
printf("%s ", feats[i].name);
}
puts(""); /* newline */
}
}
return PARAM_HELP_REQUESTED;
case 'w':
/* get the output string */
if('@' == *nextarg) {
/* the data begins with a '@' letter, it means that a file name
or - (stdin) follows */
FILE *file;
nextarg++; /* pass the @ */
if(curlx_strequal("-", nextarg))
file = stdin;
else
file = fopen(nextarg, "r");
config->writeout = file2string(file);
if(!config->writeout)
warnf(config, "Failed to read %s", file);
if(file && (file != stdin))
fclose(file);
}
else
GetStr(&config->writeout, nextarg);
break;
case 'x':
/* proxy */
GetStr(&config->proxy, nextarg);
break;
case 'X':
/* set custom request */
GetStr(&config->customrequest, nextarg);
break;
case 'y':
/* low speed time */
if(str2num(&config->low_speed_time, nextarg))
return PARAM_BAD_NUMERIC;
if(!config->low_speed_limit)
config->low_speed_limit = 1;
break;
case 'Y':
/* low speed limit */
if(str2num(&config->low_speed_limit, nextarg))
return PARAM_BAD_NUMERIC;
if(!config->low_speed_time)
config->low_speed_time=30;
break;
case 'z': /* time condition coming up */
switch(*nextarg) {
case '+':
nextarg++;
default:
/* If-Modified-Since: (section 14.28 in RFC2068) */
config->timecond = CURL_TIMECOND_IFMODSINCE;
break;
case '-':
/* If-Unmodified-Since: (section 14.24 in RFC2068) */
config->timecond = CURL_TIMECOND_IFUNMODSINCE;
nextarg++;
break;
case '=':
/* Last-Modified: (section 14.29 in RFC2068) */
config->timecond = CURL_TIMECOND_LASTMOD;
nextarg++;
break;
}
now=time(NULL);
config->condtime=curl_getdate(nextarg, &now);
if(-1 == (int)config->condtime) {
/* now let's see if it is a file name to get the time from instead! */
struct_stat statbuf;
if(-1 == stat(nextarg, &statbuf)) {
/* failed, remove time condition */
config->timecond = CURL_TIMECOND_NONE;
warnf(config,
"Illegal date format for -z/--timecond (and not "
"a file name). Disabling time condition. "
"See curl_getdate(3) for valid date syntax.\n");
}
else {
/* pull the time out from the file */
config->condtime = statbuf.st_mtime;
}
}
break;
default: /* unknown flag */
return PARAM_OPTION_UNKNOWN;
}
hit = -1;
} while(!longopt && !singleopt && *++parse && !*usedarg);
return PARAM_OK;
}
/* return 0 on everything-is-fine, and non-zero otherwise */
static int parseconfig(const char *filename,
struct Configurable *config)
{
int res;
FILE *file;
char filebuffer[512];
bool usedarg;
char *home;
if(!filename || !*filename) {
/* NULL or no file name attempts to load .curlrc from the homedir! */
#define CURLRC DOT_CHAR "curlrc"
#ifndef __AMIGA__
filename = CURLRC; /* sensible default */
home = homedir(); /* portable homedir finder */
if(home) {
if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
snprintf(filebuffer, sizeof(filebuffer),
"%s%s%s", home, DIR_CHAR, CURLRC);
#ifdef WIN32
/* Check if the file exists - if not, try CURLRC in the same
* directory as our executable
*/
file = fopen(filebuffer, "r");
if (file != NULL) {
fclose(file);
filename = filebuffer;
}
else {
/* Get the filename of our executable. GetModuleFileName is
* already declared via inclusions done in setup header file.
* We assume that we are using the ASCII version here.
*/
int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer));
if (n > 0 && n < (int)sizeof(filebuffer)) {
/* We got a valid filename - get the directory part */
char *lastdirchar = strrchr(filebuffer, '\\');
if (lastdirchar) {
int remaining;
*lastdirchar = 0;
/* If we have enough space, build the RC filename */
remaining = sizeof(filebuffer) - strlen(filebuffer);
if ((int)strlen(CURLRC) < remaining - 1) {
snprintf(lastdirchar, remaining,
"%s%s", DIR_CHAR, CURLRC);
/* Don't bother checking if it exists - we do
* that later
*/
filename = filebuffer;
}
}
}
}
#else /* WIN32 */
filename = filebuffer;
#endif /* WIN32 */
}
free(home); /* we've used it, now free it */
}
# else /* __AMIGA__ */
/* On AmigaOS all the config files are into env:
*/