#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>
#if defined(MSDOS) || defined(WIN32)
# if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
# include <libgen.h>
# endif
#endif
#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
#include "rawstr.h"
#include "xattr.h"
#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
#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
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#elif defined(HAVE_POLL_H)
#include <poll.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#define ENABLE_CURLX_PRINTF
#include "curlx.h"
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
#include <iconv.h>
#ifndef CURL_ICONV_CODESET_OF_NETWORK
#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
#endif
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include "os-specific.h"
#ifdef CURLDEBUG
#ifndef CURLTOOLDEBUG
#define MEMDEBUG_NODEFINES
#endif
#include "memdebug.h"
#endif
#ifdef __VMS
static int vms_show = 0;
#endif
#if defined(NETWARE)
#define PRINT_LINES_PAUSE 23
#endif
#if defined(__SYMBIAN32__)
#define PRINT_LINES_PAUSE 16
#define pressanykey() getchar()
#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
#ifndef O_BINARY
#define O_BINARY 0
#endif
#if defined(MSDOS) || defined(WIN32)
static const char *msdosify(const char *);
static char *rename_if_dos_device_name(char *);
static char *sanitize_dos_name(char *);
#ifndef S_ISCHR
# ifdef S_IFCHR
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
# else
# define S_ISCHR(m) (0)
# endif
#endif
#ifdef WIN32
# define _use_lfn(f) (1)
#elif !defined(__DJGPP__) || (__DJGPP__ < 2)
# define _use_lfn(f) (0)
#endif
#endif
#ifdef MSDOS
#define USE_WATT32
#include <dos.h>
#ifdef DJGPP
char **__crt0_glob_function (char *arg)
{
(void)arg;
return (char**)0;
}
#endif
#endif
#ifndef STDIN_FILENO
#define STDIN_FILENO fileno(stdin)
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO fileno(stdout)
#endif
#ifndef STDERR_FILENO
#define STDERR_FILENO fileno(stderr)
#endif
#define CURL_PROGRESS_STATS 0
#define CURL_PROGRESS_BAR 1
typedef enum {
HTTPREQ_UNSPEC,
HTTPREQ_GET,
HTTPREQ_HEAD,
HTTPREQ_POST,
HTTPREQ_SIMPLEPOST,
HTTPREQ_CUSTOM,
HTTPREQ_LAST
} HttpReq;
#ifdef USE_WIN32_LARGE_FILES
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
# define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence)
# define fstat(fdes,stp) _fstati64(fdes, stp)
# define stat(fname,stp) _stati64(fname, stp)
# define struct_stat struct _stati64
# define LSEEK_ERROR (__int64)-1
#endif
#ifdef USE_WIN32_SMALL_FILES
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence)
# define fstat(fdes,stp) _fstat(fdes, stp)
# define stat(fname,stp) _stat(fname, stp)
# define struct_stat struct _stat
# define LSEEK_ERROR (long)-1
#endif
#ifndef struct_stat
# define struct_stat struct stat
#endif
#ifndef LSEEK_ERROR
# define LSEEK_ERROR (off_t)-1
#endif
#ifdef WIN32
# include <direct.h>
# define mkdir(x,y) (mkdir)(x)
# undef PATH_MAX
# define PATH_MAX MAX_PATH
# ifndef __POCC__
# define F_OK 0
# endif
#endif
#ifndef SIZEOF_OFF_T
# if defined(__VMS) && !defined(__VAX)
# if defined(_LARGEFILE)
# define SIZEOF_OFF_T 8
# endif
# elif defined(__OS400__) && defined(__ILEC400__)
# if defined(_LARGE_FILES)
# define SIZEOF_OFF_T 8
# endif
# elif defined(__MVS__) && defined(__IBMC__)
# if defined(_LP64) || defined(_LARGE_FILES)
# define SIZEOF_OFF_T 8
# endif
# elif defined(__370__) && defined(__IBMC__)
# if defined(_LP64) || defined(_LARGE_FILES)
# define SIZEOF_OFF_T 8
# endif
# elif defined(TPF)
# define SIZEOF_OFF_T 8
# endif
# ifndef SIZEOF_OFF_T
# define SIZEOF_OFF_T 4
# endif
#endif
#ifdef CURL_DOES_CONVERSIONS
#ifdef HAVE_ICONV
iconv_t inbound_cd = (iconv_t)-1;
iconv_t outbound_cd = (iconv_t)-1;
static CURLcode
convert_to_network(char *buffer, size_t length)
{
CURLcode rc;
char *input_ptr, *output_ptr;
size_t in_bytes, out_bytes;
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;
}
}
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;
}
static CURLcode
convert_from_network(char *buffer, size_t length)
{
CURLcode rc;
char *input_ptr, *output_ptr;
size_t in_bytes, out_bytes;
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;
}
}
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
static
char convert_char(curl_infotype infotype, char this_char)
{
switch(infotype) {
case CURLINFO_DATA_IN:
case CURLINFO_DATA_OUT:
case CURLINFO_SSL_DATA_IN:
case CURLINFO_SSL_DATA_OUT:
if((this_char >= 0x20) && (this_char < 0x7f)) {
convert_from_network(&this_char, 1);
}
else {
return UNPRINTABLE_CHAR;
}
default:
if(ISPRINT(this_char)
&& (this_char != '\t')
&& (this_char != '\r')
&& (this_char != '\n')) {
return this_char;
}
break;
}
return UNPRINTABLE_CHAR;
}
#endif
#ifdef WIN32
#ifdef __BORLANDC__
# define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence)
#endif
#ifdef __POCC__
# if(__POCC__ < 450)
# define _lseeki64(hnd,ofs,whence) _lseek(hnd,ofs,whence)
# else
# define _lseeki64(hnd,ofs,whence) _lseek64(hnd,ofs,whence)
# endif
#endif
#ifndef HAVE_FTRUNCATE
#define HAVE_FTRUNCATE 1
#endif
static int ftruncate64(int fd, curl_off_t where)
{
if(_lseeki64(fd, where, SEEK_SET) < 0)
return -1;
if(!SetEndOfFile((HANDLE)_get_osfhandle(fd)))
return -1;
return 0;
}
#define ftruncate(fd,where) ftruncate64(fd,where)
#endif
typedef enum {
TRACE_NONE,
TRACE_BIN,
TRACE_ASCII,
TRACE_PLAIN
} trace;
struct OutStruct {
char *filename;
FILE *stream;
struct Configurable *config;
curl_off_t bytes;
curl_off_t init;
};
struct Configurable {
CURL *easy;
bool remote_time;
char *random_file;
char *egd_file;
char *useragent;
char *cookie;
char *cookiejar;
char *cookiefile;
bool cookiesession;
bool encoding;
long authtype;
bool use_resume;
bool resume_from_current;
bool disable_epsv;
bool disable_eprt;
bool ftp_pret;
long proto;
bool proto_present;
long proto_redir;
bool proto_redir_present;
curl_off_t resume_from;
char *postfields;
curl_off_t 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 *tls_username;
char *tls_password;
char *tls_authtype;
char *proxyuserpwd;
char *proxy;
int proxyver;
char *noproxy;
char *mail_from;
struct curl_slist *mail_rcpt;
bool proxytunnel;
bool ftp_append;
bool mute;
bool use_ascii;
bool autoreferer;
bool failonerror;
bool include_headers;
bool no_body;
bool dirlistonly;
bool followlocation;
bool unrestricted_auth;
bool netrc_opt;
bool netrc;
bool noprogress;
bool isatty;
struct getout *url_list;
struct getout *url_last;
struct getout *url_get;
struct getout *url_out;
char *cipher_list;
char *cert;
char *cert_type;
char *cacert;
char *capath;
char *crlfile;
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 *trace_stream;
bool trace_fopened;
trace tracetype;
bool tracetime;
long httpversion;
int progressmode;
bool nobuffer;
bool readbusy;
bool globoff;
bool use_httpget;
bool insecure_ok;
bool create_dirs;
bool ftp_create_dirs;
bool ftp_skip_ip;
bool proxynegotiate;
bool proxyntlm;
bool proxydigest;
bool proxybasic;
bool proxyanyauth;
char *writeout;
bool writeenv;
FILE *errors;
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;
struct curl_slist *resolve;
HttpReq httpreq;
curl_off_t sendpersecond;
curl_off_t recvpersecond;
bool ftp_ssl;
bool ftp_ssl_reqd;
bool ftp_ssl_control;
bool ftp_ssl_ccc;
int ftp_ssl_ccc_mode;
char *socksproxy;
int socksver;
char *socks5_gssapi_service;
int socks5_gssapi_nec ;
bool tcp_nodelay;
long req_retry;
long retry_delay;
long retry_maxtime;
char *ftp_account;
char *ftp_alternative_to_user;
int ftp_filemethod;
long tftp_blksize;
bool ignorecl;
bool disable_sessionid;
char *libcurl;
bool raw;
bool post301;
bool post302;
bool nokeepalive;
long alivetime;
bool content_disposition;
int default_node_flags;
struct OutStruct *outs;
bool xattr;
};
#define WARN_PREFIX "Warning: "
#define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX))
static void warnf(struct Configurable *config, const char *fmt, ...)
{
if(!config->mute) {
va_list ap;
int len;
char *ptr;
char print_buffer[256];
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)
cut = WARN_TEXTWIDTH-1;
(void)fwrite(ptr, cut + 1, 1, config->errors);
fputs("\n", config->errors);
ptr += cut+1;
len -= cut;
}
else {
fputs(ptr, config->errors);
len = 0;
}
}
}
}
static CURLcode main_init(void)
{
#ifdef DJGPP
_djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
#endif
return curl_global_init(CURL_GLOBAL_DEFAULT);
}
static void main_free(void)
{
curl_global_cleanup();
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
if(inbound_cd != (iconv_t)-1)
iconv_close(inbound_cd);
if(outbound_cd != (iconv_t)-1)
iconv_close(outbound_cd);
#endif
}
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(FILE *errors, const char *fmt, ...)
{
va_list ap;
if(fmt) {
va_start(ap, fmt);
fputs("curl: ", errors);
vfprintf(errors, fmt, ap);
va_end(ap);
}
fprintf(errors, "curl: try 'curl --help' "
#ifdef USE_MANUAL
"or 'curl --manual' "
#endif
"for more information\n");
}
struct getout {
struct getout *next;
char *url;
char *outfile;
char *infile;
int flags;
};
#define GETOUT_OUTFILE (1<<0)
#define GETOUT_URL (1<<1)
#define GETOUT_USEREMOTE (1<<2)
#define GETOUT_UPLOAD (1<<3)
#define GETOUT_NOUPLOAD (1<<4)
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",
" --anyauth Pick \"any\" authentication method (H)",
" -a/--append Append to target file when uploading (F/SFTP)",
" --basic Use HTTP Basic Authentication (H)",
" --cacert <file> CA certificate to verify peer against (SSL)",
" --capath <directory> CA directory to verify peer against (SSL)",
" -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
" --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
" --ciphers <list> SSL ciphers to use (SSL)",
" --compressed Request compressed response (using deflate or gzip)",
" -K/--config <file> Specify which config file to read",
" --connect-timeout <seconds> Maximum time allowed for connection",
" -C/--continue-at <offset> Resumed transfer offset",
" -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
" -c/--cookie-jar <file> Write cookies to this file after operation (H)",
" --create-dirs Create necessary local directory hierarchy",
" --crlf Convert LF to CRLF in upload",
" --crlfile <file> Get a CRL list in PEM format from the given file",
" -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)",
" --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)",
" --engine <eng> Crypto engine to use (SSL). \"--engine list\" for list",
#ifdef USE_ENVIRONMENT
" --environment Write results to environment variables (RISC OS)",
#endif
" -f/--fail Fail silently (no output at all) on HTTP errors (H)",
" -F/--form <name=content> Specify HTTP multipart POST data (H)",
" --form-string <name=string> Specify HTTP multipart POST data (H)",
" --ftp-account <data> Account data to send when requested by server (F)",
" --ftp-alternative-to-user <cmd> 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)",
" -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
" --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n"
" --ftp-pret Send PRET before PASV (for drftpd) (F)",
" --ftp-ssl-ccc Send CCC after authenticating (F)",
" --ftp-ssl-ccc-mode [active/passive] Set CCC mode (F)",
" --ftp-ssl-control Require SSL/TLS for ftp login, clear for transfer (F)",
" -G/--get Send the -d data with a HTTP GET (H)",
" -g/--globoff Disable URL sequences and ranges using {} and []",
" -H/--header <line> Custom header to pass to server (H)",
" -I/--head Show document info only",
" -h/--help This help text",
" --hostpubmd5 <md5> Hex encoded MD5 string of the host public key. (SSH)",
" -0/--http1.0 Use HTTP 1.0 (H)",
" --ignore-content-length Ignore the HTTP Content-Length header",
" -i/--include Include protocol headers in the output (H/F)",
" -k/--insecure Allow connections to SSL sites without certs (H)",
" --interface <interface> Specify network interface/address to use",
" -4/--ipv4 Resolve name to IPv4 address",
" -6/--ipv6 Resolve name to IPv6 address",
" -j/--junk-session-cookies Ignore session cookies read from file (H)",
" --keepalive-time <seconds> Interval between keepalive probes",
" --key <key> Private key file name (SSL/SSH)",
" --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
" --krb <level> Enable Kerberos with specified security level (F)",
" --libcurl <file> Dump libcurl equivalent code of this command line",
" --limit-rate <rate> Limit transfer speed to this rate",
" -J/--remote-header-name Use the header-provided filename (H)",
" -l/--list-only List only names of an FTP directory (F)",
" --local-port <num>[-num] Force use of these local port numbers",
" -L/--location Follow Location: hints (H)",
" --location-trusted Follow Location: and send auth to other hosts (H)",
" -M/--manual Display the full manual",
" --mail-from <from> Mail from this address",
" --mail-rcpt <to> Mail to this receiver(s)",
" --max-filesize <bytes> Maximum file size to download (H/F)",
" --max-redirs <num> Maximum number of redirects allowed (H)",
" -m/--max-time <seconds> Maximum time allowed for the transfer",
" --negotiate Use HTTP Negotiate Authentication (H)",
" -n/--netrc Must read .netrc for user name and password",
" --netrc-optional Use either .netrc or URL; overrides -n",
" -N/--no-buffer Disable buffering of the output stream",
" --no-keepalive Disable keepalive use on the connection",
" --no-sessionid Disable SSL session-ID reusing (SSL)",
" --noproxy Comma-separated list of hosts which do not use proxy",
" --ntlm Use HTTP NTLM authentication (H)",
" -o/--output <file> Write output to <file> instead of stdout",
" --pass <pass> Pass phrase for the private key (SSL/SSH)",
" --post301 Do not switch to GET after following a 301 redirect (H)",
" --post302 Do not switch to GET after following a 302 redirect (H)",
" -#/--progress-bar Display transfer progress as a progress bar",
" --proto <protocols> Enable/disable specified protocols",
" --proto-redir <protocols> Enable/disable specified protocols on redirect",
" -x/--proxy <host[:port]> Use HTTP proxy on given port",
" --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)",
" -U/--proxy-user <user[:password]> Set proxy user and password",
" --proxy1.0 <host[:port]> Use HTTP/1.0 proxy on given port",
" -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)",
" --pubkey <key> Public key file name (SSH)",
" -Q/--quote <cmd> Send command(s) to server before file transfer (F/SFTP)",
" --random-file <file> File for reading random data from (SSL)",
" -r/--range <range> Retrieve only the bytes within a range",
" --raw Pass HTTP \"raw\", without any transfer decoding (H)",
" -e/--referer Referer URL (H)",
" -O/--remote-name Write output to a file named as the remote file",
" --remote-name-all Use the remote file name for all URLs",
" -R/--remote-time Set the remote file's time on the local output",
" -X/--request <command> Specify request command to use",
" --resolve <host:port:address> Force resolve of HOST:PORT to ADDRESS",
" --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/--show-error Show error. With -s, make curl show errors when they occur",
" -s/--silent Silent mode. Don't output anything",
" --socks4 <host[:port]> SOCKS4 proxy on given host + port",
" --socks4a <host[:port]> SOCKS4a proxy on given host + port",
" --socks5 <host[:port]> SOCKS5 proxy on given host + port",
" --socks5-hostname <host[:port]> SOCKS5 proxy, pass host name to proxy",
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
" --socks5-gssapi-service <name> SOCKS5 proxy service name for gssapi",
" --socks5-gssapi-nec Compatibility with NEC SOCKS5 server",
#endif
" -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs",
" -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30",
" --ssl Try SSL/TLS (FTP, IMAP, POP3, SMTP)",
" --ssl-reqd Require SSL/TLS (FTP, IMAP, POP3, SMTP)",
" -2/--sslv2 Use SSLv2 (SSL)",
" -3/--sslv3 Use SSLv3 (SSL)",
" --stderr <file> Where to redirect stderr. - means stdout",
" --tcp-nodelay Use the TCP_NODELAY option",
" -t/--telnet-option <OPT=val> Set telnet option",
" --tftp-blksize <value> Set TFTP BLKSIZE option (must be >512)",
" -z/--time-cond <time> Transfer based on a time condition",
" -1/--tlsv1 Use TLSv1 (SSL)",
" --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",
" -B/--use-ascii Use ASCII/text transfer",
" -u/--user <user[:password]> Set server user and password",
" --tlsuser <user> Set TLS username",
" --tlspassword <string> Set TLS password",
" --tlsauthtype <string> Set TLS authentication type (default SRP)",
" -A/--user-agent <string> User-Agent to send to server (H)",
" -v/--verbose Make the operation more talkative",
" -V/--version Show version number and quit",
#ifdef USE_WATT32
" --wdebug Turn on Watt-32 debugging",
#endif
" -w/--write-out <format> What to output after completion",
" --xattr Store metadata in extended file attributes",
" -q If used as the first parameter disables .curlrc",
NULL
};
for(i=0; helptext[i]; i++) {
puts(helptext[i]);
#ifdef PRINT_LINES_PAUSE
if(i && ((i % PRINT_LINES_PAUSE) == 0))
pressanykey();
#endif
}
}
struct LongShort {
const char *letter;
const char *lname;
bool extraparam;
};
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, FILE *errors);
static void GetStr(char **string,
const char *value)
{
if(*string)
free(*string);
if(value)
*string = strdup(value);
else
*string = NULL;
}
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;
}
}
static struct getout *new_getout(struct Configurable *config)
{
struct getout *node =malloc(sizeof(struct getout));
struct getout *last= config->url_last;
if(node) {
memset(node, 0, sizeof(struct getout));
if(last)
last->next = node;
else
config->url_list = node;
config->url_last = node;
node->flags = config->default_node_flags;
}
return node;
}
struct multi_files {
struct curl_forms form;
struct multi_files *next;
};
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 = 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 = 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 = 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;
}
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);
}
}
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);
}
#define FORM_FILE_SEPARATOR ','
#define FORM_TYPE_SEPARATOR ';'
static int formparse(struct Configurable *config,
const char *input,
struct curl_httppost **httppost,
struct curl_httppost **last_post,
bool literal_value)
{
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, '=')) != NULL)) {
contents = strdup(contp+1);
if(!contents) {
fprintf(config->errors, "out of memory\n");
return 1;
}
contp = contents;
if('@' == contp[0] && !literal_value) {
struct multi_files *multi_start = NULL, *multi_current = NULL;
contp++;
multi_start = multi_current=NULL;
do {
char *ptr;
char *filename=NULL;
sep=strchr(contp, FORM_TYPE_SEPARATOR);
sep2=strchr(contp, FORM_FILE_SEPARATOR);
if(sep2 && (sep2 < sep)) {
sep = sep2;
}
type = NULL;
if(sep) {
if(FORM_FILE_SEPARATOR == *sep)
ptr = NULL;
else
ptr = sep+1;
*sep=0;
while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
while(ISSPACE(*ptr))
ptr++;
if(checkprefix("type=", ptr)) {
type = &ptr[5];
if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
major, minor)) {
warnf(config, "Illegally formatted content-type field!\n");
free(contents);
FreeMultiInfo(multi_start);
return 2;
}
sep = (char *)type + strlen(major)+strlen(minor)+1;
if((*sep==';') && !checkprefix(";filename=", sep)) {
sep2 = strchr(sep+1, ';');
if(sep2)
sep = sep2;
else
sep = sep+strlen(sep);
}
if(*sep) {
*sep=0;
ptr=sep+1;
}
else
ptr = NULL;
}
else if(checkprefix("filename=", ptr)) {
filename = &ptr[9];
ptr=strchr(filename, FORM_TYPE_SEPARATOR);
if(!ptr) {
ptr=strchr(filename, FORM_FILE_SEPARATOR);
}
if(ptr) {
*ptr=0;
ptr++;
}
}
else
break;
}
if(ptr)
sep=strchr(ptr, FORM_FILE_SEPARATOR);
else
sep=NULL;
}
else {
sep=strchr(contp, FORM_FILE_SEPARATOR);
}
if(sep) {
*sep =0;
sep++;
}
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;
} while(sep && *sep);
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 = malloc((count+1)*sizeof(struct curl_forms));
if(!forms)
{
fprintf(config->errors, "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;
}
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 ParameterError file2string(char **bufp, FILE *file)
{
char buffer[256];
char *ptr;
char *string = NULL;
size_t stringlen = 0;
size_t buflen;
if(file) {
while(fgets(buffer, sizeof(buffer), file)) {
if((ptr = strchr(buffer, '\r')) != NULL)
*ptr = '\0';
if((ptr = strchr(buffer, '\n')) != NULL)
*ptr = '\0';
buflen = strlen(buffer);
if((ptr = realloc(string, stringlen+buflen+1)) == NULL) {
if(string)
free(string);
return PARAM_NO_MEM;
}
string = ptr;
strcpy(string+stringlen, buffer);
stringlen += buflen;
}
}
*bufp = string;
return PARAM_OK;
}
static ParameterError file2memory(char **bufp, size_t *size, FILE *file)
{
char *newbuf;
char *buffer = NULL;
size_t alloc = 512;
size_t nused = 0;
size_t nread;
if(file) {
do {
if(!buffer || (alloc == nused)) {
if(alloc+1 > ((size_t)-1)/2) {
if(buffer)
free(buffer);
return PARAM_NO_MEM;
}
alloc *= 2;
if((newbuf = realloc(buffer, alloc+1)) == NULL) {
if(buffer)
free(buffer);
return PARAM_NO_MEM;
}
buffer = newbuf;
}
nread = fread(buffer+nused, 1, alloc-nused, file);
nused += nread;
} while(nread);
buffer[nused] = '\0';
if(alloc != nused) {
if((newbuf = realloc(buffer, nused+1)) != NULL)
buffer = newbuf;
}
if(!nused) {
free(buffer);
buffer = NULL;
}
}
*size = nused;
*bufp = buffer;
return PARAM_OK;
}
static void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
if(str) {
size_t len = strlen(str);
memset(str, ' ', len);
}
#else
(void)str;
#endif
}
static int str2num(long *val, const char *str)
{
if(str && ISDIGIT(*str)) {
char *endptr;
long num = strtol(str, &endptr, 10);
if((endptr != str) && (endptr == str + strlen(str))) {
*val = num;
return 0;
}
}
return 1;
}
static long proto2num(struct Configurable *config, long *val, const char *str)
{
char *buffer;
const char *sep = ",";
char *token;
static struct sprotos {
const char *name;
long bit;
} const protos[] = {
{ "all", CURLPROTO_ALL },
{ "http", CURLPROTO_HTTP },
{ "https", CURLPROTO_HTTPS },
{ "ftp", CURLPROTO_FTP },
{ "ftps", CURLPROTO_FTPS },
{ "scp", CURLPROTO_SCP },
{ "sftp", CURLPROTO_SFTP },
{ "telnet", CURLPROTO_TELNET },
{ "ldap", CURLPROTO_LDAP },
{ "ldaps", CURLPROTO_LDAPS },
{ "dict", CURLPROTO_DICT },
{ "file", CURLPROTO_FILE },
{ "tftp", CURLPROTO_TFTP },
{ "imap", CURLPROTO_IMAP },
{ "imaps", CURLPROTO_IMAPS },
{ "pop3", CURLPROTO_POP3 },
{ "pop3s", CURLPROTO_POP3S },
{ "smtp", CURLPROTO_SMTP },
{ "smtps", CURLPROTO_SMTPS },
{ "rtsp", CURLPROTO_RTSP },
{ "gopher", CURLPROTO_GOPHER },
{ NULL, 0 }
};
if(!str)
return 1;
buffer = strdup(str);
for(token = strtok(buffer, sep);
token;
token = strtok(NULL, sep)) {
enum e_action { allow, deny, set } action = allow;
struct sprotos const *pp;
while(!ISALNUM(*token)) {
switch (*token++) {
case '=':
action = set;
break;
case '-':
action = deny;
break;
case '+':
action = allow;
break;
default:
free(buffer);
return 1;
}
}
for(pp=protos; pp->name; pp++) {
if(curlx_raw_equal(token, pp->name)) {
switch (action) {
case deny:
*val &= ~(pp->bit);
break;
case allow:
*val |= pp->bit;
break;
case set:
*val = pp->bit;
break;
}
break;
}
}
if(!(pp->name)) {
if(action == set)
*val = 0;
warnf(config, "unrecognized protocol '%s'\n", token);
}
}
free(buffer);
return 0;
}
static int str2offset(curl_off_t *val, const char *str)
{
#if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
*val = curlx_strtoofft(str, NULL, 0);
if((*val == CURL_OFF_T_MAX || *val == CURL_OFF_T_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,
char **userpwd)
{
char *ptr;
if(!*userpwd)
return;
ptr = strchr(*userpwd, ':');
if(!ptr) {
char passwd[256]="";
char prompt[256];
size_t passwdlen;
size_t userlen = strlen(*userpwd);
char *passptr;
curlx_msnprintf(prompt, sizeof(prompt),
"Enter %s password for user '%s':",
kind, *userpwd);
getpass_r(prompt, passwd, sizeof(passwd));
passwdlen = strlen(passwd);
passptr = realloc(*userpwd,
passwdlen + 1 +
userlen + 1);
if(passptr) {
passptr[userlen]=':';
memcpy(&passptr[userlen+1], passwd, passwdlen+1);
*userpwd = passptr;
}
}
}
static ParameterError add2list(struct curl_slist **list,
const 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, const char *str)
{
if(curlx_raw_equal("singlecwd", str))
return CURLFTPMETHOD_SINGLECWD;
if(curlx_raw_equal("nocwd", str))
return CURLFTPMETHOD_NOCWD;
if(curlx_raw_equal("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, const char *str)
{
if(curlx_raw_equal("passive", str))
return CURLFTPSSL_CCC_PASSIVE;
if(curlx_raw_equal("active", str))
return CURLFTPSSL_CCC_ACTIVE;
warnf(config, "unrecognized ftp CCC method '%s', using default\n", str);
return CURLFTPSSL_CCC_PASSIVE;
}
static int sockoptcallback(void *clientp, curl_socket_t curlfd,
curlsocktype purpose)
{
struct Configurable *config = (struct Configurable *)clientp;
int onoff = 1;
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPINTVL)
int keepidle = (int)config->alivetime;
#endif
switch(purpose) {
case CURLSOCKTYPE_IPCXN:
if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&onoff,
sizeof(onoff)) < 0) {
SET_SOCKERRNO(0);
warnf(clientp, "Could not set SO_KEEPALIVE!\n");
return 0;
}
else {
if(config->alivetime) {
#ifdef TCP_KEEPIDLE
if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle,
sizeof(keepidle)) < 0) {
SET_SOCKERRNO(0);
warnf(clientp, "Could not set TCP_KEEPIDLE!\n");
return 0;
}
#endif
#ifdef TCP_KEEPINTVL
if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepidle,
sizeof(keepidle)) < 0) {
SET_SOCKERRNO(0);
warnf(clientp, "Could not set TCP_KEEPINTVL!\n");
return 0;
}
#endif
#if !defined(TCP_KEEPIDLE) || !defined(TCP_KEEPINTVL)
warnf(clientp, "Keep-alive functionality somewhat crippled due to "
"missing support in your operating system!\n");
#endif
}
}
break;
default:
break;
}
return 0;
}
static ParameterError getparameter(char *flag,
char *nextarg,
bool *usedarg,
struct Configurable *config)
{
char letter;
char subletter=0;
int rc;
const char *parse=NULL;
unsigned int j;
time_t now;
int hit=-1;
bool longopt=FALSE;
bool singleopt=FALSE;
ParameterError err;
bool toggle=TRUE;
static const struct LongShort aliases[]= {
{"*", "url", TRUE},
{"*a", "random-file", TRUE},
{"*b", "egd-file", TRUE},
{"*c", "connect-timeout", TRUE},
{"*d", "ciphers", TRUE},
{"*e", "disable-epsv", FALSE},
{"*E", "epsv", FALSE},
#ifdef USE_ENVIRONMENT
{"*f", "environment", FALSE},
#endif
{"*g", "trace", TRUE},
{"*h", "trace-ascii", TRUE},
{"*i", "limit-rate", TRUE},
{"*j", "compressed", FALSE},
{"*k", "digest", FALSE},
{"*l", "negotiate", FALSE},
{"*m", "ntlm", FALSE},
{"*n", "basic", FALSE},
{"*o", "anyauth", FALSE},
#ifdef USE_WATT32
{"*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},
{"*y", "max-filesize", TRUE},
{"*z", "disable-eprt", FALSE},
{"*Z", "eprt", FALSE},
{"$a", "ftp-ssl", FALSE},
{"$a", "ssl", FALSE},
{"$b", "ftp-pasv", FALSE},
{"$c", "socks5", TRUE},
{"$c", "socks", TRUE},
{"$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},
{"$T", "socks4a", TRUE},
{"$u", "ftp-alternative-to-user", TRUE},
{"$v", "ftp-ssl-reqd", FALSE},
{"$v", "ssl-reqd", FALSE},
{"$w", "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},
{"$1", "keepalive", FALSE},
{"$2", "socks5-hostname", TRUE},
{"$3", "keepalive-time", TRUE},
{"$4", "post302", FALSE},
{"$5", "noproxy", TRUE},
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
{"$6", "socks5-gssapi-service", TRUE},
{"$7", "socks5-gssapi-nec", FALSE},
#endif
{"$8", "proxy1.0", TRUE},
{"$9", "tftp-blksize", TRUE},
{"$A", "mail-from", TRUE},
{"$B", "mail-rcpt", TRUE},
{"$C", "ftp-pret", FALSE},
{"$D", "proto", TRUE},
{"$E", "proto-redir", TRUE},
{"$F", "resolve", TRUE},
{"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},
{"Ej","crlfile", TRUE},
{"Ek","tlsuser", TRUE},
{"El","tlspassword", TRUE},
{"Em","tlsauthtype", 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},
{"J", "remote-header-name", 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", "buffer", FALSE},
{"o", "output", TRUE},
{"O", "remote-name", FALSE},
{"Oa", "remote-name-all", FALSE},
{"p", "proxytunnel", FALSE},
{"P", "ftpport", TRUE},
{"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},
{"Y", "speed-limit", TRUE},
{"y", "speed-time", TRUE},
{"z", "time-cond", TRUE},
{"#", "progress-bar",FALSE},
{"~", "xattr",FALSE},
};
if(('-' != flag[0]) ||
(('-' == flag[0]) && ('-' == flag[1]))) {
char *word=('-' == flag[0])?flag+2:flag;
size_t fnam=strlen(word);
int numhits=0;
if(!strncmp(word, "no-", 3)) {
word += 3;
toggle = FALSE;
}
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
if(curlx_strnequal(aliases[j].lname, word, fnam)) {
longopt = TRUE;
numhits++;
if(curlx_raw_equal(aliases[j].lname, word)) {
parse = aliases[j].letter;
hit = j;
numhits = 1;
break;
}
parse = aliases[j].letter;
hit = j;
}
}
if(numhits>1) {
return PARAM_OPTION_AMBIGUOUS;
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
}
}
else {
flag++;
hit=-1;
parse = flag;
}
do {
if(!longopt) {
if(NULL != parse) {
letter = (char)*parse;
}
else {
letter = '\0';
}
subletter='\0';
}
else {
letter = parse[0];
subletter = parse[1];
}
*usedarg = FALSE;
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(aliases[hit].extraparam) {
if(!longopt && parse[1]) {
nextarg=(char *)&parse[1];
singleopt=TRUE;
}
else if(!nextarg)
return PARAM_REQUIRES_PARAMETER;
else
*usedarg = TRUE;
}
switch(letter) {
case '*':
switch(subletter) {
case 'a':
GetStr(&config->random_file, nextarg);
break;
case 'b':
GetStr(&config->egd_file, nextarg);
break;
case 'c':
if(str2num(&config->connecttimeout, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'd':
GetStr(&config->cipher_list, nextarg);
break;
case 'e':
config->disable_epsv = toggle;
break;
case 'E':
config->disable_epsv = (bool)(!toggle);
break;
#ifdef USE_ENVIRONMENT
case 'f':
config->writeenv = toggle;
break;
#endif
case 'g':
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':
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':
{
char *unit;
curl_off_t value = curlx_strtoofft(nextarg, &unit, 0);
if(!*unit)
unit=(char *)"b";
else if(strlen(unit) > 1)
unit=(char *)"w";
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':
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':
if(toggle && !(curlinfo->features & CURL_VERSION_LIBZ))
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->encoding = toggle;
break;
case 'k':
if(toggle)
config->authtype |= CURLAUTH_DIGEST;
else
config->authtype &= ~CURLAUTH_DIGEST;
break;
case 'l':
if(toggle) {
if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
config->authtype |= CURLAUTH_GSSNEGOTIATE;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
}
else
config->authtype &= ~CURLAUTH_GSSNEGOTIATE;
break;
case 'm':
if(toggle) {
if(curlinfo->features & CURL_VERSION_NTLM)
config->authtype |= CURLAUTH_NTLM;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
}
else
config->authtype &= ~CURLAUTH_NTLM;
break;
case 'n':
if(toggle)
config->authtype |= CURLAUTH_BASIC;
else
config->authtype &= ~CURLAUTH_BASIC;
break;
case 'o':
if(toggle)
config->authtype = CURLAUTH_ANY;
break;
#ifdef USE_WATT32
case 'p':
dbug_init();
break;
#endif
case 'q':
config->ftp_create_dirs = toggle;
break;
case 'r':
config->create_dirs = TRUE;
break;
case 's':
if(str2num(&config->maxredirs, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 't':
if(curlinfo->features & CURL_VERSION_NTLM)
config->proxyntlm = toggle;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'u':
config->crlf = TRUE;
break;
case 'v':
if(strcmp(nextarg, "-")) {
FILE *newfile = fopen(nextarg, "wt");
if(!newfile)
warnf(config, "Failed to open %s!\n", nextarg);
else {
if(config->errors_fopened)
fclose(config->errors);
config->errors = newfile;
config->errors_fopened = TRUE;
}
}
else
config->errors = stdout;
break;
case 'w':
GetStr(&config->iface, nextarg);
break;
case 'x':
if(curlinfo->features & (CURL_VERSION_KERBEROS4 |
CURL_VERSION_GSSNEGOTIATE))
GetStr(&config->krblevel, nextarg);
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'y':
if(str2offset(&config->max_filesize, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'z':
config->disable_eprt = toggle;
break;
case 'Z':
config->disable_eprt = (bool)(!toggle);
break;
default:
{
struct getout *url;
if(config->url_get || ((config->url_get = config->url_list) != NULL)) {
while(config->url_get && (config->url_get->flags&GETOUT_URL))
config->url_get = config->url_get->next;
}
if(config->url_get)
url = config->url_get;
else
url=new_getout(config);
if(url) {
GetStr(&url->url, nextarg);
url->flags |= GETOUT_URL;
}
}
}
break;
case '$':
switch(subletter) {
case 'a':
if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->ftp_ssl = toggle;
break;
case 'b':
if(config->ftpport)
free(config->ftpport);
config->ftpport = NULL;
break;
case 'c':
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS5;
break;
case 't':
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS4;
break;
case 'T':
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS4A;
break;
case '2':
GetStr(&config->socksproxy, nextarg);
config->socksver = CURLPROXY_SOCKS5_HOSTNAME;
break;
case 'd':
config->tcp_nodelay = toggle;
break;
case 'e':
config->proxydigest = toggle;
break;
case 'f':
config->proxybasic = toggle;
break;
case 'g':
if(str2num(&config->req_retry, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'h':
if(str2num(&config->retry_delay, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'i':
if(str2num(&config->retry_maxtime, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'k':
if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
config->proxynegotiate = toggle;
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'm':
GetStr(&config->ftp_account, nextarg);
break;
case 'n':
config->proxyanyauth = toggle;
break;
case 'o':
config->tracetime = toggle;
break;
case 'p':
config->ignorecl = toggle;
break;
case 'q':
config->ftp_skip_ip = toggle;
break;
case 'r':
config->ftp_filemethod = ftpfilemethod(config, nextarg);
break;
case 's':
rc = sscanf(nextarg, "%d - %d",
&config->localport,
&config->localportrange);
if(!rc)
return PARAM_BAD_USE;
else if(rc == 1)
config->localportrange = 1;
else {
config->localportrange -= config->localport;
if(config->localportrange < 1) {
warnf(config, "bad range input\n");
return PARAM_BAD_USE;
}
}
break;
case 'u':
GetStr(&config->ftp_alternative_to_user, nextarg);
break;
case 'v':
if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->ftp_ssl_reqd = toggle;
break;
case 'w':
config->disable_sessionid = (bool)(!toggle);
break;
case 'x':
if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->ftp_ssl_control = toggle;
break;
case 'y':
config->ftp_ssl_ccc = toggle;
if(!config->ftp_ssl_ccc_mode)
config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
break;
case 'j':
config->ftp_ssl_ccc = TRUE;
config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
break;
case 'z':
GetStr(&config->libcurl, nextarg);
break;
case '#':
config->raw = toggle;
break;
case '0':
config->post301 = toggle;
break;
case '1':
config->nokeepalive = (bool)(!toggle);
break;
case '3':
if(str2num(&config->alivetime, nextarg))
return PARAM_BAD_NUMERIC;
break;
case '4':
config->post302 = toggle;
break;
case '5':
GetStr(&config->noproxy, nextarg);
break;
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
case '6':
GetStr(&config->socks5_gssapi_service, nextarg);
break;
case '7':
config->socks5_gssapi_nec = TRUE;
break;
#endif
case '8':
GetStr(&config->proxy, nextarg);
config->proxyver = CURLPROXY_HTTP_1_0;
break;
case '9':
str2num(&config->tftp_blksize, nextarg);
break;
case 'A':
GetStr(&config->mail_from, nextarg);
break;
case 'B':
err = add2list(&config->mail_rcpt, nextarg);
if(err)
return err;
break;
case 'C':
config->ftp_pret = toggle;
break;
case 'D':
config->proto_present = TRUE;
if(proto2num(config, &config->proto, nextarg))
return PARAM_BAD_USE;
break;
case 'E':
config->proto_redir_present = TRUE;
if(proto2num(config, &config->proto_redir, nextarg))
return PARAM_BAD_USE;
break;
case 'F':
err = add2list(&config->resolve, nextarg);
if(err)
return err;
break;
}
break;
case '#':
if(toggle)
config->progressmode = CURL_PROGRESS_BAR;
else
config->progressmode = CURL_PROGRESS_STATS;
break;
case '~':
config->xattr = toggle;
break;
case '0':
config->httpversion = CURL_HTTP_VERSION_1_0;
break;
case '1':
config->ssl_version = CURL_SSLVERSION_TLSv1;
break;
case '2':
config->ssl_version = CURL_SSLVERSION_SSLv2;
break;
case '3':
config->ssl_version = CURL_SSLVERSION_SSLv3;
break;
case '4':
config->ip_version = 4;
break;
case '6':
config->ip_version = 6;
break;
case 'a':
config->ftp_append = toggle;
break;
case 'A':
GetStr(&config->useragent, nextarg);
break;
case 'b':
if(nextarg[0] == '@') {
nextarg++;
}
else if(strchr(nextarg, '=')) {
GetStr(&config->cookie, nextarg);
break;
}
GetStr(&config->cookiefile, nextarg);
break;
case 'B':
config->use_ascii = toggle;
break;
case 'c':
GetStr(&config->cookiejar, nextarg);
break;
case 'C':
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':
{
char *postdata=NULL;
FILE *file;
if(subletter == 'e') {
const char *p = strchr(nextarg, '=');
size_t size = 0;
size_t nlen;
char is_file;
if(!p)
p = strchr(nextarg, '@');
if(p) {
nlen = p - nextarg;
is_file = *p++;
}
else {
nlen = is_file = 0;
p = nextarg;
}
if('@' == is_file) {
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);
}
err = file2memory(&postdata, &size, file);
if(file && (file != stdin))
fclose(file);
if(err)
return err;
}
else {
GetStr(&postdata, p);
size = strlen(postdata);
}
if(!postdata) {
postdata=strdup("");
}
else {
char *enc = curl_easy_escape(config->easy, postdata, (int)size);
free(postdata);
if(enc) {
size_t outlen = nlen + strlen(enc) + 2;
char *n = malloc(outlen);
if(!n) {
curl_free(enc);
return PARAM_NO_MEM;
}
if(nlen > 0)
snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
else
strcpy(n, enc);
curl_free(enc);
postdata = n;
}
else
return PARAM_NO_MEM;
}
}
else if('@' == *nextarg) {
size_t size = 0;
nextarg++;
if(curlx_strequal("-", nextarg)) {
file = stdin;
if(subletter == 'b')
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') {
err = file2memory(&postdata, &size, file);
config->postfieldsize = (curl_off_t)size;
}
else
err = file2string(&postdata, file);
if(file && (file != stdin))
fclose(file);
if(err)
return err;
if(!postdata) {
postdata=strdup("");
}
}
else {
GetStr(&postdata, nextarg);
}
#ifdef CURL_DOES_CONVERSIONS
if(subletter != 'b') {
convert_to_network(postdata, strlen(postdata));
}
#endif
if(config->postfields) {
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;
}
snprintf(config->postfields, newlen, "%s\x26%s", oldpost, postdata);
free(oldpost);
free(postdata);
}
else
config->postfields=postdata;
}
break;
case 'D':
GetStr(&config->headerfile, nextarg);
break;
case 'e':
{
char *ptr = strstr(nextarg, ";auto");
if(ptr) {
config->autoreferer = TRUE;
*ptr = 0;
}
else
config->autoreferer = FALSE;
GetStr(&config->referer, nextarg);
}
break;
case 'E':
switch(subletter) {
case 'a':
GetStr(&config->cacert, nextarg);
break;
case 'b':
GetStr(&config->cert_type, nextarg);
break;
case 'c':
GetStr(&config->key, nextarg);
break;
case 'd':
GetStr(&config->key_type, nextarg);
break;
case 'e':
GetStr(&config->key_passwd, nextarg);
cleanarg(nextarg);
break;
case 'f':
GetStr(&config->engine, nextarg);
if(config->engine && curlx_raw_equal(config->engine,"list"))
config->list_engines = TRUE;
break;
case 'g':
GetStr(&config->capath, nextarg);
break;
case 'h':
GetStr(&config->pubkey, nextarg);
break;
case 'i':
GetStr(&config->hostpubmd5, nextarg);
if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
return PARAM_BAD_USE;
break;
case 'j':
GetStr(&config->crlfile, nextarg);
break;
case 'k':
if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
GetStr(&config->tls_username, nextarg);
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'l':
if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
GetStr(&config->tls_password, nextarg);
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
case 'm':
if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
GetStr(&config->tls_authtype, nextarg);
if (!strequal(config->tls_authtype, "SRP"))
return PARAM_LIBCURL_DOESNT_SUPPORT;
}
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
default:
{
char *ptr = strchr(nextarg, ':');
#ifdef WIN32
if(ptr &&
(ptr == &nextarg[1]) &&
(nextarg[2] == '\\' || nextarg[2] == '/') &&
(ISALPHA(nextarg[0])) )
ptr = strchr(&nextarg[3], ':');
#endif
if(ptr) {
*ptr=0;
ptr++;
GetStr(&config->key_passwd, ptr);
}
GetStr(&config->cert, nextarg);
cleanarg(nextarg);
}
}
break;
case 'f':
config->failonerror = toggle;
break;
case 'F':
if(formparse(config,
nextarg,
&config->httppost,
&config->last_post,
(bool) (subletter=='s')))
return PARAM_BAD_USE;
if(SetHTTPrequest(config, HTTPREQ_POST, &config->httpreq))
return PARAM_BAD_USE;
break;
case 'g':
config->globoff = toggle;
break;
case 'G':
config->use_httpget = TRUE;
break;
case 'h':
if(toggle) {
help();
return PARAM_HELP_REQUESTED;
}
break;
case 'H':
err = add2list(&config->headers, nextarg);
if(err)
return err;
break;
case 'i':
config->include_headers = toggle;
break;
case 'j':
config->cookiesession = toggle;
break;
case 'I':
config->no_body = toggle;
if(SetHTTPrequest(config,
(config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
&config->httpreq))
return PARAM_BAD_USE;
break;
case 'J':
if(config->include_headers) {
warnf(config,
"--include and --remote-header-name cannot be combined.\n");
return PARAM_BAD_USE;
}
config->content_disposition = toggle;
break;
case 'k':
config->insecure_ok = toggle;
break;
case 'K':
if(parseconfig(nextarg, config))
warnf(config, "error trying read config from the '%s' file\n",
nextarg);
break;
case 'l':
config->dirlistonly = toggle;
break;
case 'L':
config->followlocation = toggle;
switch (subletter) {
case 't':
config->unrestricted_auth = toggle;
break;
}
break;
case 'm':
if(str2num(&config->timeout, nextarg))
return PARAM_BAD_NUMERIC;
break;
case 'M':
if(toggle) {
#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
}
break;
case 'n':
switch(subletter) {
case 'o':
config->netrc_opt = toggle;
break;
default:
config->netrc = toggle;
break;
}
break;
case 'N':
if(longopt)
config->nobuffer = (bool)(!toggle);
else
config->nobuffer = toggle;
break;
case 'O':
if(subletter == 'a') {
config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
break;
}
case 'o':
{
struct getout *url;
if(config->url_out || ((config->url_out = config->url_list) != NULL)) {
while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
config->url_out = config->url_out->next;
}
if(config->url_out)
url = config->url_out;
else
url=new_getout(config);
if(url) {
if('o' == letter) {
GetStr(&url->outfile, nextarg);
url->flags &= ~GETOUT_USEREMOTE;
}
else {
url->outfile=NULL;
if(toggle)
url->flags |= GETOUT_USEREMOTE;
else
url->flags &= ~GETOUT_USEREMOTE;
}
url->flags |= GETOUT_OUTFILE;
}
}
break;
case 'P':
GetStr(&config->ftpport, nextarg);
break;
case 'p':
config->proxytunnel = toggle;
break;
case 'q':
break;
case 'Q':
switch(nextarg[0]) {
case '-':
nextarg++;
err = add2list(&config->postquote, nextarg);
break;
case '+':
nextarg++;
err = add2list(&config->prequote, nextarg);
break;
default:
err = add2list(&config->quote, nextarg);
break;
}
if(err)
return err;
break;
case 'r':
if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
char buffer[32];
curl_off_t off;
warnf(config,
"A specified range MUST include at least one dash (-). "
"Appending one for you!\n");
off = curlx_strtoofft(nextarg, NULL, 10);
snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
GetStr(&config->range, buffer);
}
{
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++;
}
GetStr(&config->range, nextarg);
}
break;
case 'R':
config->remote_time = toggle;
break;
case 's':
if(toggle)
config->mute = config->noprogress = TRUE;
else
config->mute = config->noprogress = FALSE;
config->showerror = (bool)(!toggle);
break;
case 'S':
config->showerror = toggle;
break;
case 't':
err = add2list(&config->telnet_options, nextarg);
if(err)
return err;
break;
case 'T':
{
struct getout *url;
if(config->url_out || ((config->url_out = config->url_list) != NULL)) {
while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
config->url_out = config->url_out->next;
}
if(config->url_out)
url = config->url_out;
else
url=new_getout(config);
if(url) {
url->flags |= GETOUT_UPLOAD;
if(!*nextarg)
url->flags |= GETOUT_NOUPLOAD;
else {
GetStr(&url->infile, nextarg);
}
}
}
break;
case 'u':
GetStr(&config->userpwd, nextarg);
cleanarg(nextarg);
checkpasswd("host", &config->userpwd);
break;
case 'U':
GetStr(&config->proxyuserpwd, nextarg);
cleanarg(nextarg);
checkpasswd("proxy", &config->proxyuserpwd);
break;
case 'v':
if(toggle) {
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;
}
else
config->tracetype = TRACE_NONE;
break;
case 'V':
{
const char * const *proto;
if(!toggle)
break;
printf(CURL_ID "%s\n", curl_version());
if(curlinfo->protocols) {
printf("Protocols: ");
for(proto=curlinfo->protocols; *proto; ++proto) {
printf("%s ", *proto);
}
puts("");
}
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},
{"TrackMemory", CURL_VERSION_CURLDEBUG},
{"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},
{"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}
};
printf("Features: ");
for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
if(curlinfo->features & feats[i].bitmask)
printf("%s ", feats[i].name);
}
puts("");
}
}
return PARAM_HELP_REQUESTED;
case 'w':
if('@' == *nextarg) {
FILE *file;
const char *fname;
nextarg++;
if(curlx_strequal("-", nextarg)) {
fname = "<stdin>";
file = stdin;
}
else {
fname = nextarg;
file = fopen(nextarg, "r");
}
err = file2string(&config->writeout, file);
if(file && (file != stdin))
fclose(file);
if(err)
return err;
if(!config->writeout)
warnf(config, "Failed to read %s", fname);
}
else
GetStr(&config->writeout, nextarg);
break;
case 'x':
GetStr(&config->proxy, nextarg);
config->proxyver = CURLPROXY_HTTP;
break;
case 'X':
GetStr(&config->customrequest, nextarg);
break;
case 'y':
if(str2num(&config->low_speed_time, nextarg))
return PARAM_BAD_NUMERIC;
if(!config->low_speed_limit)
config->low_speed_limit = 1;
break;
case 'Y':
if(str2num(&config->low_speed_limit, nextarg))
return PARAM_BAD_NUMERIC;
if(!config->low_speed_time)
config->low_speed_time=30;
break;
case 'z':
switch(*nextarg) {
case '+':
nextarg++;
default:
config->timecond = CURL_TIMECOND_IFMODSINCE;
break;
case '-':
config->timecond = CURL_TIMECOND_IFUNMODSINCE;
nextarg++;
break;
case '=':
config->timecond = CURL_TIMECOND_LASTMOD;
nextarg++;
break;
}
now=time(NULL);
config->condtime=curl_getdate(nextarg, &now);
if(-1 == (int)config->condtime) {
struct_stat statbuf;
if(-1 == stat(nextarg, &statbuf)) {
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 {
config->condtime = statbuf.st_mtime;
}
}
break;
default:
return PARAM_OPTION_UNKNOWN;
}
hit = -1;
} while(!longopt && !singleopt && *++parse && !*usedarg);
return PARAM_OK;
}
static const char *unslashquote(const char *line, char *param)
{
while(*line && (*line != '\"')) {
if(*line == '\\') {
char out;
line++;
switch(out = *line) {
case '\0':
continue;
case 't':
out='\t';
break;
case 'n':
out='\n';
break;
case 'r':
out='\r';
break;
case 'v':
out='\v';
break;
}
*param++=out;
line++;
}
else
*param++=*line++;
}
*param=0;
return line;
}
static int parseconfig(const char *filename,
struct Configurable *config)
{
int res;
FILE *file;
char filebuffer[512];
bool usedarg;
char *home;
int rc = 0;
if(!filename || !*filename) {
#define CURLRC DOT_CHAR "curlrc"
#ifndef __AMIGA__
filename = CURLRC;
home = homedir();
if(home) {
if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
snprintf(filebuffer, sizeof(filebuffer),
"%s%s%s", home, DIR_CHAR, CURLRC);
#ifdef WIN32
file = fopen(filebuffer, "r");
if(file != NULL) {
fclose(file);
filename = filebuffer;
}
else {
int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer));
if(n > 0 && n < (int)sizeof(filebuffer)) {
char *lastdirchar = strrchr(filebuffer, '\\');
if(lastdirchar) {
size_t remaining;
*lastdirchar = 0;
remaining = sizeof(filebuffer) - strlen(filebuffer);
if(strlen(CURLRC) < remaining - 1) {
snprintf(lastdirchar, remaining,
"%s%s", DIR_CHAR, CURLRC);
filename = filebuffer;
}
}
}
}
#else
filename = filebuffer;
#endif
}
free(home);
}
# else
filename = "ENV:" CURLRC;
#endif
}
if(strcmp(filename,"-"))
file = fopen(filename, "r");
else
file = stdin;
if(file) {
char *line;
char *aline;
char *option;
char *param;
int lineno=0;
bool alloced_param;
#define ISSEP(x) (((x)=='=') || ((x) == ':'))
while(NULL != (aline = my_get_line(file))) {
lineno++;
line = aline;
alloced_param=FALSE;
while(*line && ISSPACE(*line))
line++;
switch(*line) {
case '#':
case '/':
case '\r':
case '\n':
case '*':
case '\0':
free(aline);
continue;
}
option = line;
while(*line && !ISSPACE(*line) && !ISSEP(*line))
line++;
if(*line)
*line++=0;
#ifdef DEBUG_CONFIG
fprintf(stderr, "GOT: %s\n", option);
#endif
while(*line && (ISSPACE(*line) || ISSEP(*line)))
line++;
if(*line == '\"') {
line++;
param=malloc(strlen(line)+1);
if(!param) {
free(aline);
rc = 1;
break;
}
alloced_param=TRUE;
(void)unslashquote(line, param);
}
else {
param=line;
while(*line && !ISSPACE(*line))
line++;
*line=0;
}
if(param && !*param) {
if(alloced_param)
free(param);
param = NULL;
}
#ifdef DEBUG_CONFIG
fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
#endif
res = getparameter(option, param, &usedarg, config);
if(param && *param && !usedarg)
res = PARAM_GOT_EXTRA_PARAMETER;
if(res != PARAM_OK) {
if(!strcmp(filename, "-")) {
filename=(char *)"<stdin>";
}
if(PARAM_HELP_REQUESTED != res) {
const char *reason = param2text(res);
warnf(config, "%s:%d: warning: '%s' %s\n",
filename, lineno, option, reason);
}
}
if(alloced_param)
{
free(param);
param = NULL;
}
free(aline);
}
if(file != stdin)
fclose(file);
}
else
rc = 1;
return rc;
}
static void go_sleep(long ms)
{
#ifdef HAVE_POLL_FINE
poll((void *)0, 0, (int)ms);
#else
#ifdef WIN32
Sleep(ms);
#elif defined(MSDOS)
delay(ms);
#else
struct timeval timeout;
timeout.tv_sec = ms/1000;
ms = ms%1000;
timeout.tv_usec = ms * 1000;
select(0, NULL, NULL, NULL, &timeout);
#endif
#endif
}
static size_t my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream)
{
size_t rc;
struct OutStruct *out=(struct OutStruct *)stream;
struct Configurable *config = out->config;
const size_t err_rc = (sz * nmemb) ? 0 : 1;
if(!out->stream) {
out->bytes = 0;
if(!out->filename) {
warnf(config, "Remote filename has no length!\n");
return err_rc;
}
if(config->content_disposition) {
FILE* f = fopen(out->filename, "r");
if(f) {
fclose(f);
warnf(config, "Refusing to overwrite %s: %s\n", out->filename,
strerror(EEXIST));
return err_rc;
}
}
out->stream=fopen(out->filename, "wb");
if(!out->stream) {
warnf(config, "Failed to create the file %s: %s\n", out->filename,
strerror(errno));
return err_rc;
}
}
rc = fwrite(buffer, sz, nmemb, out->stream);
if((sz * nmemb) == rc)
out->bytes += (sz * nmemb);
if(config->readbusy) {
config->readbusy = FALSE;
curl_easy_pause(config->easy, CURLPAUSE_CONT);
}
if(config->nobuffer) {
int res = fflush(out->stream);
if(res) {
return err_rc;
}
}
return rc;
}
struct InStruct {
int fd;
struct Configurable *config;
};
#define MAX_SEEK 2147483647
static int my_seek(void *stream, curl_off_t offset, int whence)
{
struct InStruct *in=(struct InStruct *)stream;
#if(CURL_SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES)
if(offset > MAX_SEEK) {
curl_off_t left = offset;
if(whence != SEEK_SET)
return 1;
if(LSEEK_ERROR == lseek(in->fd, 0, SEEK_SET))
return 1;
while(left) {
long step = (left>MAX_SEEK ? MAX_SEEK : (long)left);
if(LSEEK_ERROR == lseek(in->fd, step, SEEK_CUR))
return 1;
left -= step;
}
return 0;
}
#endif
if(LSEEK_ERROR == lseek(in->fd, offset, whence))
return CURL_SEEKFUNC_CANTSEEK;
return 0;
}
static size_t my_fread(void *buffer, size_t sz, size_t nmemb, void *userp)
{
ssize_t rc;
struct InStruct *in=(struct InStruct *)userp;
rc = read(in->fd, buffer, sz*nmemb);
if(rc < 0) {
if(errno == EAGAIN) {
errno = 0;
in->config->readbusy = TRUE;
return CURL_READFUNC_PAUSE;
}
rc = 0;
}
in->config->readbusy = FALSE;
return (size_t)rc;
}
struct ProgressData {
int calls;
curl_off_t prev;
int width;
FILE *out;
curl_off_t initial_size;
};
static int myprogress (void *clientp,
double dltotal,
double dlnow,
double ultotal,
double ulnow)
{
char line[256];
char outline[256];
char format[40];
double frac;
double percent;
int barwidth;
int num;
int i;
struct ProgressData *bar = (struct ProgressData *)clientp;
curl_off_t total = (curl_off_t)dltotal + (curl_off_t)ultotal +
bar->initial_size;
curl_off_t point = (curl_off_t)dlnow + (curl_off_t)ulnow +
bar->initial_size;
if(point > total)
total = point;
bar->calls++;
if(total < 1) {
curl_off_t prevblock = bar->prev / 1024;
curl_off_t thisblock = point / 1024;
while( thisblock > prevblock ) {
fprintf( bar->out, "#" );
prevblock++;
}
}
else {
frac = (double)point / (double)total;
percent = frac * 100.0f;
barwidth = bar->width - 7;
num = (int) (((double)barwidth) * frac);
for( i = 0; i < num; i++ ) {
line[i] = '#';
}
line[i] = '\0';
snprintf( format, sizeof(format), "%%-%ds %%5.1f%%%%", barwidth );
snprintf( outline, sizeof(outline), format, line, percent );
fprintf( bar->out, "\r%s", outline );
}
fflush(bar->out);
bar->prev = point;
return 0;
}
static
void progressbarinit(struct ProgressData *bar,
struct Configurable *config)
{
#ifdef __EMX__
int scr_size [2];
#endif
char *colp;
memset(bar, 0, sizeof(struct ProgressData));
if(config->use_resume)
bar->initial_size = config->resume_from;
#ifndef __EMX__
colp = curlx_getenv("COLUMNS");
if(colp != NULL) {
char *endptr;
long num = strtol(colp, &endptr, 10);
if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 0))
bar->width = (int)num;
else
bar->width = 79;
curl_free(colp);
}
else
bar->width = 79;
#else
_scrsize(scr_size);
bar->width = scr_size[0] - 1;
#endif
bar->out = config->errors;
}
static
void dump(const char *timebuf, const char *text,
FILE *stream, const unsigned char *ptr, size_t size,
trace tracetype, curl_infotype infotype)
{
size_t i;
size_t c;
unsigned int width=0x10;
if(tracetype == TRACE_ASCII)
width = 0x40;
fprintf(stream, "%s%s, %zd bytes (0x%zx)\n", timebuf, text, size, size);
for(i=0; i<size; i+= width) {
fprintf(stream, "%04zx: ", i);
if(tracetype == TRACE_BIN) {
for(c = 0; c < width; c++)
if(i+c < size)
fprintf(stream, "%02x ", ptr[i+c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i+c < size); c++) {
if((tracetype == TRACE_ASCII) &&
(i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
i+=(c+2-width);
break;
}
#ifdef CURL_DOES_CONVERSIONS
if((tracetype == TRACE_ASCII) &&
(i+c+1 < size) && ptr[i+c]=='\r' && ptr[i+c+1]=='\n') {
i+=(c+2-width);
break;
}
fprintf(stream, "%c", convert_char(infotype, ptr[i+c]));
#else
(void)infotype;
fprintf(stream, "%c",
(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:UNPRINTABLE_CHAR);
#endif
if((tracetype == TRACE_ASCII) &&
(i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
i+=(c+3-width);
break;
}
}
fputc('\n', stream);
}
fflush(stream);
}
static
int my_trace(CURL *handle, curl_infotype type,
unsigned char *data, size_t size,
void *userp)
{
struct Configurable *config = (struct Configurable *)userp;
FILE *output=config->errors;
const char *text;
struct timeval tv;
struct tm *now;
char timebuf[20];
time_t secs;
static time_t epoch_offset;
static int known_offset;
(void)handle;
if(config->tracetime) {
tv = cutil_tvnow();
if(!known_offset) {
epoch_offset = time(NULL) - tv.tv_sec;
known_offset = 1;
}
secs = epoch_offset + tv.tv_sec;
now = localtime(&secs);
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ",
now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec);
}
else
timebuf[0]=0;
if(!config->trace_stream) {
if(curlx_strequal("-", config->trace_dump))
config->trace_stream = stdout;
else if(curlx_strequal("%", config->trace_dump))
config->trace_stream = config->errors;
else {
config->trace_stream = fopen(config->trace_dump, "w");
config->trace_fopened = TRUE;
}
}
if(config->trace_stream)
output = config->trace_stream;
if(!output) {
warnf(config, "Failed to create/open output");
return 0;
}
if(config->tracetype == TRACE_PLAIN) {
static const char * const s_infotype[] = {
"*", "<", ">", "{", "}", "{", "}"
};
size_t i;
size_t st=0;
static bool newl = FALSE;
static bool traced_data = FALSE;
switch(type) {
case CURLINFO_HEADER_OUT:
for(i=0; i<size-1; i++) {
if(data[i] == '\n') {
if(!newl) {
fprintf(output, "%s%s ", timebuf, s_infotype[type]);
}
(void)fwrite(data+st, i-st+1, 1, output);
st = i+1;
newl = FALSE;
}
}
if(!newl)
fprintf(output, "%s%s ", timebuf, s_infotype[type]);
(void)fwrite(data+st, i-st+1, 1, output);
newl = (bool)(size && (data[size-1] != '\n'));
traced_data = FALSE;
break;
case CURLINFO_TEXT:
case CURLINFO_HEADER_IN:
if(!newl)
fprintf(output, "%s%s ", timebuf, s_infotype[type]);
(void)fwrite(data, size, 1, output);
newl = (bool)(size && (data[size-1] != '\n'));
traced_data = FALSE;
break;
case CURLINFO_DATA_OUT:
case CURLINFO_DATA_IN:
case CURLINFO_SSL_DATA_IN:
case CURLINFO_SSL_DATA_OUT:
if(!traced_data) {
if(!config->isatty ||
((output != stderr) && (output != stdout))) {
if(!newl)
fprintf(output, "%s%s ", timebuf, s_infotype[type]);
fprintf(output, "[data not shown]\n");
newl = FALSE;
traced_data = TRUE;
}
}
break;
default:
newl = FALSE;
traced_data = FALSE;
break;
}
return 0;
}
#ifdef CURL_DOES_CONVERSIONS
if((type == CURLINFO_HEADER_OUT) && (size > 4)) {
size_t i;
for(i = 0; i < size - 4; i++) {
if(memcmp(&data[i], "\r\n\r\n", 4) == 0) {
text = "=> Send header";
dump(timebuf, text, output, data, i+4, config->tracetype, type);
data += i + 3;
size -= i + 4;
type = CURLINFO_DATA_OUT;
data += 1;
break;
}
}
}
#endif
switch (type) {
case CURLINFO_TEXT:
fprintf(output, "%s== Info: %s", timebuf, data);
default:
return 0;
case CURLINFO_HEADER_OUT:
text = "=> Send header";
break;
case CURLINFO_DATA_OUT:
text = "=> Send data";
break;
case CURLINFO_HEADER_IN:
text = "<= Recv header";
break;
case CURLINFO_DATA_IN:
text = "<= Recv data";
break;
case CURLINFO_SSL_DATA_IN:
text = "<= Recv SSL data";
break;
case CURLINFO_SSL_DATA_OUT:
text = "=> Send SSL data";
break;
}
dump(timebuf, text, output, data, size, config->tracetype, type);
return 0;
}
static void free_config_fields(struct Configurable *config)
{
if(config->random_file)
free(config->random_file);
if(config->egd_file)
free(config->egd_file);
if(config->trace_dump)
free(config->trace_dump);
if(config->cipher_list)
free(config->cipher_list);
if(config->userpwd)
free(config->userpwd);
if(config->postfields)
free(config->postfields);
if(config->proxy)
free(config->proxy);
if(config->proxyuserpwd)
free(config->proxyuserpwd);
if(config->noproxy)
free(config->noproxy);
if(config->cookie)
free(config->cookie);
if(config->cookiefile)
free(config->cookiefile);
if(config->krblevel)
free(config->krblevel);
if(config->headerfile)
free(config->headerfile);
if(config->ftpport)
free(config->ftpport);
if(config->range)
free(config->range);
if(config->customrequest)
free(config->customrequest);
if(config->writeout)
free(config->writeout);
if(config->httppost)
curl_formfree(config->httppost);
if(config->cert)
free(config->cert);
if(config->cacert)
free(config->cacert);
if(config->cert_type)
free(config->cert_type);
if(config->capath)
free(config->capath);
if(config->crlfile)
free(config->crlfile);
if(config->cookiejar)
free(config->cookiejar);
if(config->ftp_account)
free(config->ftp_account);
if(config->ftp_alternative_to_user)
free(config->ftp_alternative_to_user);
if(config->iface)
free(config->iface);
if(config->socksproxy)
free(config->socksproxy);
if(config->libcurl)
free(config->libcurl);
if(config->key_passwd)
free(config->key_passwd);
if(config->key)
free(config->key);
if(config->key_type)
free(config->key_type);
if(config->pubkey)
free(config->pubkey);
if(config->referer)
free(config->referer);
if(config->hostpubmd5)
free(config->hostpubmd5);
if(config->mail_from)
free(config->mail_from);
#ifdef USE_TLS_SRP
if(config->tls_authtype)
free(config->tls_authtype);
if(config->tls_username)
free(config->tls_username);
if(config->tls_password)
free(config->tls_password);
#endif
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(config->socks5_gssapi_service)
free(config->socks5_gssapi_service);
#endif
curl_slist_free_all(config->quote);
curl_slist_free_all(config->prequote);
curl_slist_free_all(config->postquote);
curl_slist_free_all(config->headers);
curl_slist_free_all(config->telnet_options);
curl_slist_free_all(config->mail_rcpt);
curl_slist_free_all(config->resolve);
if(config->easy)
curl_easy_cleanup(config->easy);
}
#ifdef WIN32
static void FindWin32CACert(struct Configurable *config,
const char *bundle_file)
{
if(curlinfo->features & CURL_VERSION_SSL) {
DWORD buflen;
char *ptr = NULL;
char *retval = malloc(sizeof (TCHAR) * (MAX_PATH + 1));
if(!retval)
return;
retval[0] = '\0';
buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
if(buflen > 0) {
GetStr(&config->cacert, retval);
}
free(retval);
}
}
#endif
#define RETRY_SLEEP_DEFAULT 1000
#define RETRY_SLEEP_MAX 600000
static bool
output_expected(const char* url, const char* uploadfile)
{
if(!uploadfile)
return TRUE;
if(checkprefix("http://", url) || checkprefix("https://", url))
return TRUE;
return FALSE;
}
#define my_setopt(x,y,z) _my_setopt(x, FALSE, config, #y, y, z)
#define my_setopt_str(x,y,z) _my_setopt(x, TRUE, config, #y, y, z)
static struct curl_slist *easycode;
static struct curl_slist *easycode_remarks;
static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config,
const char *name, CURLoption tag, ...);
static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config,
const char *name, CURLoption tag, ...)
{
va_list arg;
CURLcode ret;
char *bufp;
char value[256];
bool remark=FALSE;
bool skip=FALSE;
va_start(arg, tag);
if(tag < CURLOPTTYPE_OBJECTPOINT) {
long lval = va_arg(arg, long);
snprintf(value, sizeof(value), "%ld", lval);
ret = curl_easy_setopt(curl, tag, lval);
if(!lval)
skip = TRUE;
}
else if(tag < CURLOPTTYPE_OFF_T) {
void *pval = va_arg(arg, void *);
unsigned char *ptr = (unsigned char *)pval;
if(tag >= CURLOPTTYPE_FUNCTIONPOINT) {
if(pval) {
strcpy(value, "functionpointer");
remark = TRUE;
}
else
skip = TRUE;
}
else if(pval && str)
snprintf(value, sizeof(value), "\"%s\"", (char *)ptr);
else if(pval) {
strcpy(value, "objectpointer");
remark = TRUE;
}
else
skip = TRUE;
ret = curl_easy_setopt(curl, tag, pval);
}
else {
curl_off_t oval = va_arg(arg, curl_off_t);
snprintf(value, sizeof(value),
"(curl_off_t)%" CURL_FORMAT_CURL_OFF_T, oval);
ret = curl_easy_setopt(curl, tag, oval);
if(!oval)
skip = TRUE;
}
if(config->libcurl && !skip) {
if(remark)
bufp = curlx_maprintf("%s set to a %s", name, value);
else
bufp = curlx_maprintf("curl_easy_setopt(hnd, %s, %s);", name, value);
if(!bufp)
ret = CURLE_OUT_OF_MEMORY;
else {
struct curl_slist *list =
curl_slist_append(remark?easycode_remarks:easycode, bufp);
if(remark)
easycode_remarks = list;
else
easycode = list;
}
if(bufp)
curl_free(bufp);
}
va_end(arg);
return ret;
}
static const char * const srchead[]={
"/********* Sample code generated by the curl command line tool **********",
" * All curl_easy_setopt() options are documented at:",
" * http://curl.haxx.se/libcurl/c/curl_easy_setopt.html",
" ************************************************************************/",
"#include <curl/curl.h>",
"",
"int main(int argc, char *argv[])",
"{",
" CURLcode ret;",
NULL
};
static void dumpeasycode(struct Configurable *config)
{
struct curl_slist *ptr;
char *o = config->libcurl;
if(o) {
FILE *out;
bool fopened = FALSE;
if(strcmp(o, "-")) {
out = fopen(o, "wt");
fopened = TRUE;
}
else
out= stdout;
if(!out)
warnf(config, "Failed to open %s to write libcurl code!\n", o);
else {
int i;
const char *c;
for(i=0; ((c = srchead[i]) != '\0'); i++)
fprintf(out, "%s\n", c);
ptr = easycode;
while(ptr) {
fprintf(out, " %s\n", ptr->data);
ptr = ptr->next;
}
ptr = easycode_remarks;
if(ptr) {
fprintf(out,
"\n /* Here is a list of options the curl code"
" used that cannot get generated\n"
" as source easily. You may select to either"
" not use them or implement\n them yourself.\n"
"\n");
while(ptr) {
fprintf(out, " %s\n", ptr->data);
ptr = ptr->next;
}
fprintf(out, "\n */\n");
}
fprintf(out,
" return (int)ret;\n"
"}\n"
"/**** End of sample code ****/\n");
if(fopened)
fclose(out);
}
}
curl_slist_free_all(easycode);
}
static bool stdin_upload(const char *uploadfile)
{
return (bool)(curlx_strequal(uploadfile, "-") ||
curlx_strequal(uploadfile, "."));
}
static char *add_file_name_to_url(CURL *curl, char *url, const char *filename)
{
char *ptr=strstr(url, "://");
if(ptr)
ptr+=3;
else
ptr=url;
ptr = strrchr(ptr, '/');
if(!ptr || !strlen(++ptr)) {
const char *filep = strrchr(filename, '/');
char *file2 = strrchr(filep?filep:filename, '\\');
char *encfile;
if(file2)
filep = file2+1;
else if(filep)
filep++;
else
filep = filename;
encfile = curl_easy_escape(curl, filep, 0 );
if(encfile) {
char *urlbuffer = malloc(strlen(url) + strlen(encfile) + 3);
if(!urlbuffer) {
free(url);
return NULL;
}
if(ptr)
sprintf(urlbuffer, "%s%s", url, encfile);
else
sprintf(urlbuffer, "%s/%s", url, encfile);
curl_free(encfile);
free(url);
url = urlbuffer;
}
}
return url;
}
static char *get_url_file_name(const char *url)
{
char *fn = NULL;
const char * pc =strstr(url, "://");
if(pc)
pc+=3;
else
pc=url;
pc = strrchr(pc, '/');
if(pc) {
pc++;
fn = *pc ? strdup(pc): NULL;
}
return fn;
}
static char*
parse_filename(char *ptr, size_t len)
{
char* copy;
char* p;
char* q;
char quote = 0;
copy = malloc(len+1);
if(!copy)
return NULL;
strncpy(copy, ptr, len);
copy[len] = 0;
p = copy;
if(*p == '\'' || *p == '"') {
quote = *p;
p++;
}
q = strrchr(copy, '/');
if(q) {
p=q+1;
if(!*p) {
free(copy);
return NULL;
}
}
q = strrchr(p, '\\');
if (q) {
p = q+1;
if (!*p) {
free(copy);
return NULL;
}
}
if(quote) {
q = strrchr(p, quote);
if(q)
*q = 0;
}
else
q = NULL;
if(!q) {
q = strchr(p, '\r');
if(q)
*q = 0;
q = strchr(p, '\n');
if(q)
*q = 0;
}
if(copy!=p)
memmove(copy, p, strlen(p)+1);
return copy;
}
static size_t
header_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct OutStruct* outs = (struct OutStruct*)stream;
const char* str = (char*)ptr;
const size_t cb = size*nmemb;
const char* end = (char*)ptr + cb;
size_t len;
if(cb > 20 && checkprefix("Content-disposition:", str)) {
char *p = (char*)str + 20;
for(;;) {
char *filename;
char *semi;
while(*p && (p < end) && !ISALPHA(*p))
p++;
if(p > end-9)
break;
if(memcmp(p, "filename=", 9)) {
while((p < end) && (*p != ';'))
p++;
continue;
}
p+=9;
semi = strchr(p, ';');
len = semi ? (semi - p) : (ssize_t)cb - (p - str);
filename = parse_filename(p, len);
if(filename) {
outs->filename = filename;
break;
}
}
}
return cb;
}
static int
operate(struct Configurable *config, int argc, argv_item_t argv[])
{
char errorbuffer[CURL_ERROR_SIZE];
char useragent[256];
struct ProgressData progressbar;
struct getout *urlnode;
struct getout *nextnode;
struct OutStruct outs;
struct OutStruct heads;
struct InStruct input;
URLGlob *urls=NULL;
URLGlob *inglob=NULL;
int urlnum;
int infilenum;
char *uploadfile=NULL;
curl_off_t uploadfilesize;
bool stillflags=TRUE;
bool allocuseragent=FALSE;
char *httpgetfields=NULL;
CURL *curl;
int res = 0;
int i;
long retry_sleep_default;
long retry_sleep;
char *env;
memset(&heads, 0, sizeof(struct OutStruct));
#ifdef CURLDEBUG
env = curlx_getenv("CURL_MEMDEBUG");
if(env) {
char *s = strdup(env);
curl_free(env);
curl_memdebug(s);
free(s);
}
env = curlx_getenv("CURL_MEMLIMIT");
if(env) {
char *endptr;
long num = strtol(env, &endptr, 10);
if((endptr != env) && (endptr == env + strlen(env)) && (num > 0))
curl_memlimit(num);
curl_free(env);
}
#endif
if(main_init() != CURLE_OK) {
helpf(config->errors, "error initializing curl library\n");
return CURLE_FAILED_INIT;
}
curl = curl_easy_init();
if(!curl) {
clean_getout(config);
return CURLE_FAILED_INIT;
}
config->easy = curl;
memset(&outs,0,sizeof(outs));
config->outs = &outs;
curlinfo = curl_version_info(CURLVERSION_NOW);
errorbuffer[0]=0;
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
config->postfieldsize = -1;
config->showerror=TRUE;
config->use_httpget=FALSE;
config->create_dirs=FALSE;
config->maxredirs = DEFAULT_MAXREDIRS;
config->proto = CURLPROTO_ALL;
config->proto_present = FALSE;
config->proto_redir =
CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP);
config->proto_redir_present = FALSE;
if(argc>1 &&
(!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
strchr(argv[1], 'q')) {
;
}
else {
parseconfig(NULL, config);
}
if((argc < 2) && !config->url_list) {
helpf(config->errors, NULL);
return CURLE_FAILED_INIT;
}
for(i = 1; i < argc; i++) {
if(stillflags &&
('-' == argv[i][0])) {
char *nextarg;
bool passarg;
char *origopt=argv[i];
char *flag = argv[i];
if(curlx_strequal("--", argv[i]))
stillflags=FALSE;
else {
nextarg= (i < argc - 1)? argv[i+1]: NULL;
res = getparameter(flag, nextarg, &passarg, config);
if(res) {
int retval = CURLE_OK;
if(res != PARAM_HELP_REQUESTED) {
const char *reason = param2text(res);
helpf(config->errors, "option %s: %s\n", origopt, reason);
retval = CURLE_FAILED_INIT;
}
clean_getout(config);
return retval;
}
if(passarg)
i++;
}
}
else {
bool used;
res = getparameter((char *)"--url", argv[i], &used, config);
if(res)
return res;
}
}
retry_sleep_default = config->retry_delay?
config->retry_delay*1000:RETRY_SLEEP_DEFAULT;
retry_sleep = retry_sleep_default;
if((!config->url_list || !config->url_list->url) && !config->list_engines) {
clean_getout(config);
helpf(config->errors, "no URL specified!\n");
return CURLE_FAILED_INIT;
}
if(NULL == config->useragent) {
snprintf(useragent, sizeof(useragent),
CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
config->useragent= useragent;
}
else
allocuseragent = TRUE;
if(!config->cacert &&
!config->capath &&
!config->insecure_ok) {
env = curlx_getenv("CURL_CA_BUNDLE");
if(env)
GetStr(&config->cacert, env);
else {
env = curlx_getenv("SSL_CERT_DIR");
if(env)
GetStr(&config->capath, env);
else {
env = curlx_getenv("SSL_CERT_FILE");
if(env)
GetStr(&config->cacert, env);
}
}
if(env)
curl_free(env);
#ifdef WIN32
else
FindWin32CACert(config, "curl-ca-bundle.crt");
#endif
}
if(config->postfields) {
if(config->use_httpget) {
httpgetfields = strdup(config->postfields);
free(config->postfields);
config->postfields = NULL;
if(SetHTTPrequest(config,
(config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
&config->httpreq)) {
free(httpgetfields);
return PARAM_BAD_USE;
}
}
else {
if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq))
return PARAM_BAD_USE;
}
}
easycode = curl_slist_append(easycode, "CURL *hnd = curl_easy_init();");
if(!easycode) {
clean_getout(config);
res = CURLE_OUT_OF_MEMORY;
goto quit_curl;
}
if(config->list_engines) {
struct curl_slist *engines = NULL;
curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
list_engines(engines);
curl_slist_free_all(engines);
res = CURLE_OK;
goto quit_curl;
}
urlnode = config->url_list;
if(config->headerfile) {
if(strcmp(config->headerfile,"-")) {
heads.filename = config->headerfile;
}
else
heads.stream=stdout;
heads.config = config;
}
while(urlnode) {
int up;
char *dourl;
char *url;
char *infiles;
char *outfiles=NULL;
dourl=urlnode->url;
url = dourl;
if(NULL == url) {
if(urlnode->outfile)
free(urlnode->outfile);
nextnode=urlnode->next;
free(urlnode);
urlnode = nextnode;
continue;
}
outs.stream = stdout;
outs.config = config;
outs.bytes = 0;
if(urlnode->outfile) {
outfiles = strdup(urlnode->outfile);
if(!outfiles) {
clean_getout(config);
break;
}
}
infiles = urlnode->infile;
if(!config->globoff && infiles) {
res = glob_url(&inglob, infiles, &infilenum,
config->showerror?config->errors:NULL);
if(res != CURLE_OK) {
clean_getout(config);
if(outfiles)
free(outfiles);
break;
}
}
for(up = 0;
(!up && !infiles) ||
((uploadfile = inglob?
glob_next_url(inglob):
(!up?strdup(infiles):NULL)) != NULL);
up++) {
int separator = 0;
long retry_numretries;
uploadfilesize=-1;
if(!config->globoff) {
res = glob_url(&urls, dourl, &urlnum,
config->showerror?config->errors:NULL);
if(res != CURLE_OK) {
break;
}
}
else
urlnum = 1;
separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
for(i = 0;
((url = urls?glob_next_url(urls):(i?NULL:strdup(url))) != NULL);
i++) {
int infd = STDIN_FILENO;
bool infdopen;
char *outfile;
struct timeval retrystart;
outfile = outfiles?strdup(outfiles):NULL;
if((urlnode->flags&GETOUT_USEREMOTE) ||
(outfile && !curlx_strequal("-", outfile)) ) {
if(!outfile) {
outfile = get_url_file_name(url);
if((!outfile || !*outfile) && !config->content_disposition) {
helpf(config->errors, "Remote file name has no length!\n");
res = CURLE_WRITE_ERROR;
free(url);
break;
}
#if defined(MSDOS) || defined(WIN32)
outfile = sanitize_dos_name(outfile);
if(!outfile) {
res = CURLE_OUT_OF_MEMORY;
break;
}
#endif
}
else if(urls) {
char *storefile = outfile;
outfile = glob_match_url(storefile, urls);
free(storefile);
if(!outfile) {
warnf(config, "bad output glob!\n");
free(url);
res = CURLE_FAILED_INIT;
break;
}
}
if(config->create_dirs &&
(-1 == create_dir_hierarchy(outfile, config->errors))) {
free(url);
res = CURLE_WRITE_ERROR;
break;
}
if(config->resume_from_current) {
struct_stat fileinfo;
if(0 == stat(outfile, &fileinfo))
config->resume_from = fileinfo.st_size;
else
config->resume_from = 0;
}
outs.filename = outfile;
if(config->resume_from) {
outs.init = config->resume_from;
outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
if(!outs.stream) {
helpf(config->errors, "Can't open '%s'!\n", outfile);
free(url);
res = CURLE_WRITE_ERROR;
break;
}
}
else {
outs.stream = NULL;
outs.bytes = 0;
}
}
infdopen=FALSE;
if(uploadfile && !stdin_upload(uploadfile)) {
struct_stat fileinfo;
url = add_file_name_to_url(curl, url, uploadfile);
if(!url) {
helpf(config->errors, "out of memory\n");
res = CURLE_OUT_OF_MEMORY;
break;
}
infd= open(uploadfile, O_RDONLY | O_BINARY);
if((infd == -1) || fstat(infd, &fileinfo)) {
helpf(config->errors, "Can't open '%s'!\n", uploadfile);
if(infd != -1)
close(infd);
if(urls) {
glob_cleanup(urls);
urls = NULL;
}
if(inglob) {
glob_cleanup(inglob);
inglob = NULL;
}
res = CURLE_READ_ERROR;
goto quit_urls;
}
infdopen=TRUE;
if(S_ISREG(fileinfo.st_mode))
uploadfilesize=fileinfo.st_size;
}
else if(uploadfile && stdin_upload(uploadfile)) {
int authbits = 0;
int bitcheck = 0;
while(bitcheck < 32) {
if(config->authtype & (1 << bitcheck++)) {
authbits++;
if(authbits > 1) {
break;
}
}
}
if(config->proxyanyauth || (authbits>1)) {
warnf(config,
"Using --anyauth or --proxy-anyauth with upload from stdin"
" involves a big risk of it not working. Use a temporary"
" file or a fixed auth type instead!\n");
}
SET_BINMODE(stdin);
infd = STDIN_FILENO;
if(curlx_strequal(uploadfile, ".")) {
if(curlx_nonblock((curl_socket_t)infd, TRUE) < 0)
warnf(config,
"fcntl failed on fd=%d: %s\n", infd, strerror(errno));
}
}
if(uploadfile && config->resume_from_current)
config->resume_from = -1;
if(output_expected(url, uploadfile)
&& outs.stream && isatty(fileno(outs.stream)))
config->noprogress = config->isatty = TRUE;
if(urlnum > 1 && !(config->mute)) {
fprintf(config->errors, "\n[%d/%d]: %s --> %s\n",
i+1, urlnum, url, outfile ? outfile : "<stdout>");
if(separator)
printf("%s%s\n", CURLseparator, url);
}
if(httpgetfields) {
char *urlbuffer;
const char *pc =strstr(url, "://");
char sep='?';
if(pc)
pc+=3;
else
pc=url;
pc = strrchr(pc, '/');
if(pc) {
if(strchr(pc, '?'))
sep='&';
}
urlbuffer = malloc(strlen(url) + strlen(httpgetfields) + 3);
if(!urlbuffer) {
helpf(config->errors, "out of memory\n");
if(urls) {
glob_cleanup(urls);
urls = NULL;
}
if(inglob) {
glob_cleanup(inglob);
inglob = NULL;
}
res = CURLE_OUT_OF_MEMORY;
goto quit_urls;
}
if(pc)
sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
else
sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
free(url);
url = urlbuffer;
}
if(!config->errors)
config->errors = stderr;
if((!outfile || !strcmp(outfile, "-")) && !config->use_ascii) {
SET_BINMODE(stdout);
}
if(1 == config->tcp_nodelay)
my_setopt(curl, CURLOPT_TCP_NODELAY, 1);
my_setopt(curl, CURLOPT_WRITEDATA, &outs);
my_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
input.fd = infd;
input.config = config;
my_setopt(curl, CURLOPT_READDATA, &input);
if((outfile && !curlx_strequal("-", outfile)) ||
!checkprefix("telnet:", url))
my_setopt(curl, CURLOPT_READFUNCTION, my_fread);
my_setopt(curl, CURLOPT_SEEKDATA, &input);
my_setopt(curl, CURLOPT_SEEKFUNCTION, my_seek);
if(config->recvpersecond)
my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
if(uploadfilesize != -1)
my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
my_setopt_str(curl, CURLOPT_URL, url);
my_setopt_str(curl, CURLOPT_PROXY, config->proxy);
if(config->proxy)
my_setopt(curl, CURLOPT_PROXYTYPE, config->proxyver);
my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress);
if(config->no_body) {
my_setopt(curl, CURLOPT_NOBODY, 1);
my_setopt(curl, CURLOPT_HEADER, 1);
}
else
my_setopt(curl, CURLOPT_HEADER, config->include_headers);
my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror);
my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly);
my_setopt(curl, CURLOPT_APPEND, config->ftp_append);
if(config->netrc_opt)
my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
else if(config->netrc)
my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
else
my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
my_setopt(curl, CURLOPT_FOLLOWLOCATION, config->followlocation);
my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, config->unrestricted_auth);
my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii);
my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
my_setopt(curl, CURLOPT_NOPROXY, config->noproxy);
my_setopt_str(curl, CURLOPT_RANGE, config->range);
my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
switch(config->httpreq) {
case HTTPREQ_SIMPLEPOST:
my_setopt_str(curl, CURLOPT_POSTFIELDS, config->postfields);
my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, config->postfieldsize);
break;
case HTTPREQ_POST:
my_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
break;
default:
break;
}
my_setopt_str(curl, CURLOPT_REFERER, config->referer);
my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer);
my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
config->low_speed_limit);
my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
config->sendpersecond);
my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
config->recvpersecond);
my_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
config->use_resume?config->resume_from:0);
my_setopt_str(curl, CURLOPT_COOKIE, config->cookie);
my_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
my_setopt(curl, CURLOPT_SSLCERT, config->cert);
my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
my_setopt(curl, CURLOPT_SSLKEY, config->key);
my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd);
my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
config->hostpubmd5);
if(config->cacert || config->capath) {
if(config->cacert)
my_setopt_str(curl, CURLOPT_CAINFO, config->cacert);
if(config->capath)
my_setopt_str(curl, CURLOPT_CAPATH, config->capath);
my_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
}
if(config->crlfile)
my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
if(config->insecure_ok) {
my_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
}
else {
char *home = homedir();
char *file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR);
if(home)
free(home);
if(file) {
my_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file);
curl_free(file);
}
else {
if(urls) {
glob_cleanup(urls);
urls = NULL;
}
if(inglob) {
glob_cleanup(inglob);
inglob = NULL;
}
res = CURLE_OUT_OF_MEMORY;
goto quit_urls;
}
}
if(config->no_body || config->remote_time) {
my_setopt(curl, CURLOPT_FILETIME, TRUE);
}
my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
my_setopt(curl, CURLOPT_CRLF, config->crlf);
my_setopt(curl, CURLOPT_QUOTE, config->quote);
my_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
my_setopt(curl, CURLOPT_PREQUOTE, config->prequote);
my_setopt(curl, CURLOPT_HEADERDATA,
config->headerfile?&heads:NULL);
my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile);
if(config->cookiejar)
my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar);
my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
my_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
my_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
my_setopt(curl, CURLOPT_STDERR, config->errors);
my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
progressbarinit(&progressbar, config);
if((config->progressmode == CURL_PROGRESS_BAR) &&
!config->noprogress && !config->mute) {
my_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
}
my_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
my_setopt_str(curl, CURLOPT_RANDOM_FILE, config->random_file);
my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
if(config->cipher_list)
my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
if(config->httpversion)
my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
if(config->disable_epsv)
my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
if(config->disable_eprt)
my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
if(config->authtype)
my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
if(config->tracetype != TRACE_NONE) {
my_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
my_setopt(curl, CURLOPT_DEBUGDATA, config);
my_setopt(curl, CURLOPT_VERBOSE, TRUE);
}
res = CURLE_OK;
if(config->engine) {
res = my_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
}
if(res != CURLE_OK)
goto show_error;
my_setopt_str(curl, CURLOPT_ENCODING,
(config->encoding) ? "" : NULL);
my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
config->ftp_create_dirs);
if(config->proxyanyauth)
my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
else if(config->proxynegotiate)
my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_GSSNEGOTIATE);
else if(config->proxyntlm)
my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
else if(config->proxydigest)
my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
else if(config->proxybasic)
my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
if(config->max_filesize)
my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
config->max_filesize);
if(4 == config->ip_version)
my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
else if(6 == config->ip_version)
my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
else
my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
if(config->ftp_ssl_reqd)
my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
else if(config->ftp_ssl)
my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
else if(config->ftp_ssl_control)
my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL);
if(config->ftp_ssl_ccc)
my_setopt(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode);
if(config->socksproxy) {
my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy);
my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(config->socks5_gssapi_service)
my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE,
config->socks5_gssapi_service);
if(config->socks5_gssapi_nec)
my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC,
config->socks5_gssapi_nec);
#endif
my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl);
my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip);
my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod);
if(config->localport) {
my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
my_setopt_str(curl, CURLOPT_LOCALPORTRANGE,
config->localportrange);
}
my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
config->ftp_alternative_to_user);
if(config->disable_sessionid)
my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE,
!config->disable_sessionid);
if(config->raw) {
my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE);
my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE);
}
if(!config->nokeepalive) {
my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockoptcallback);
my_setopt(curl, CURLOPT_SOCKOPTDATA, config);
}
my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
(config->post302 ? CURL_REDIR_POST_302 : FALSE));
if(config->tftp_blksize)
my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
if(config->mail_from)
my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
if(config->mail_rcpt)
my_setopt(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
if(config->ftp_pret)
my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE);
if(config->proto_present)
my_setopt(curl, CURLOPT_PROTOCOLS, config->proto);
if(config->proto_redir_present)
my_setopt(curl, CURLOPT_REDIR_PROTOCOLS, config->proto_redir);
if((urlnode->flags & GETOUT_USEREMOTE)
&& config->content_disposition) {
my_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
my_setopt(curl, CURLOPT_HEADERDATA, &outs);
}
if(config->resolve)
my_setopt(curl, CURLOPT_RESOLVE, config->resolve);
curl_easy_setopt(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username);
curl_easy_setopt(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password);
retry_numretries = config->req_retry;
retrystart = cutil_tvnow();
for(;;) {
res = curl_easy_perform(curl);
if(!curl_slist_append(easycode, "ret = curl_easy_perform(hnd);")) {
res = CURLE_OUT_OF_MEMORY;
break;
}
if(config->content_disposition && outs.stream && !config->mute &&
outs.filename)
printf("curl: Saved to filename '%s'\n", outs.filename);
if(retry_numretries &&
(!config->retry_maxtime ||
(cutil_tvdiff(cutil_tvnow(), retrystart)<
config->retry_maxtime*1000)) ) {
enum {
RETRY_NO,
RETRY_TIMEOUT,
RETRY_HTTP,
RETRY_FTP,
RETRY_LAST
} retry = RETRY_NO;
long response;
if(CURLE_OPERATION_TIMEDOUT == res)
retry = RETRY_TIMEOUT;
else if((CURLE_OK == res) ||
(config->failonerror &&
(CURLE_HTTP_RETURNED_ERROR == res))) {
char *this_url=NULL;
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url);
if(this_url &&
checkprefix("http", this_url)) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
switch(response) {
case 500:
case 502:
case 503:
case 504:
retry = RETRY_HTTP;
break;
}
}
}
else if(res) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
if(response/100 == 4)
retry = RETRY_FTP;
}
if(retry) {
static const char * const m[]={
NULL, "timeout", "HTTP error", "FTP error"
};
warnf(config, "Transient problem: %s "
"Will retry in %ld seconds. "
"%ld retries left.\n",
m[retry], retry_sleep/1000, retry_numretries);
go_sleep(retry_sleep);
retry_numretries--;
if(!config->retry_delay) {
retry_sleep *= 2;
if(retry_sleep > RETRY_SLEEP_MAX)
retry_sleep = RETRY_SLEEP_MAX;
}
if(outs.bytes && outs.filename) {
if(!config->mute)
fprintf(config->errors, "Throwing away %"
CURL_FORMAT_CURL_OFF_T " bytes\n",
outs.bytes);
fflush(outs.stream);
#ifdef HAVE_FTRUNCATE
if(ftruncate( fileno(outs.stream), outs.init)) {
if(!config->mute)
fprintf(config->errors,
"failed to truncate, exiting\n");
break;
}
fseek(outs.stream, 0, SEEK_END);
#else
fseek(outs.stream, (long)outs.init, SEEK_SET);
#endif
outs.bytes = 0;
}
continue;
}
}
retry_sleep = retry_sleep_default;
break;
}
if((config->progressmode == CURL_PROGRESS_BAR) &&
progressbar.calls)
fputs("\n", progressbar.out);
if(config->writeout)
ourWriteOut(curl, config->writeout);
#ifdef USE_ENVIRONMENT
if(config->writeenv)
ourWriteEnv(curl);
#endif
show_error:
#ifdef __VMS
if(is_vms_shell()) {
if(!config->showerror) {
vms_show = VMSSTS_HIDE;
}
}
else
#endif
{
if((res!=CURLE_OK) && config->showerror) {
fprintf(config->errors, "curl: (%d) %s\n", res,
errorbuffer[0]? errorbuffer:
curl_easy_strerror((CURLcode)res));
if(CURLE_SSL_CACERT == res) {
#define CURL_CA_CERT_ERRORMSG1 \
"More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
"curl performs SSL certificate verification by default, using a \"bundle\"\n" \
" of Certificate Authority (CA) public keys (CA certs). If the default\n" \
" bundle file isn't adequate, you can specify an alternate file\n" \
" using the --cacert option.\n"
#define CURL_CA_CERT_ERRORMSG2 \
"If this HTTPS server uses a certificate signed by a CA represented in\n" \
" the bundle, the certificate verification probably failed due to a\n" \
" problem with the certificate (it might be expired, or the name might\n" \
" not match the domain name in the URL).\n" \
"If you'd like to turn off curl's verification of the certificate, use\n" \
" the -k (or --insecure) option.\n"
fprintf(config->errors, "%s%s",
CURL_CA_CERT_ERRORMSG1,
CURL_CA_CERT_ERRORMSG2 );
}
}
}
if(outfile && !curlx_strequal(outfile, "-") && outs.stream) {
int rc;
if(config->xattr) {
rc = fwrite_xattr(curl, fileno(outs.stream) );
if(rc)
warnf(config, "Error setting extended attributes: %s\n",
strerror(errno) );
}
rc = fclose(outs.stream);
if(!res && rc) {
res = CURLE_WRITE_ERROR;
fprintf(config->errors, "(%d) Failed writing body\n", res);
}
}
#ifdef HAVE_UTIME
if(config->remote_time && outs.filename) {
long filetime;
curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
if(filetime >= 0) {
struct utimbuf times;
times.actime = (time_t)filetime;
times.modtime = (time_t)filetime;
utime(outs.filename, ×);
}
}
#endif
#ifdef __AMIGA__
if( strlen(url) > 78 )
url[79] = '\0';
SetComment( outs.filename, url);
#endif
quit_urls:
if(url)
free(url);
if(outfile)
free(outfile);
if(infdopen)
close(infd);
}
if(urls) {
glob_cleanup(urls);
urls = NULL;
}
if(uploadfile)
free(uploadfile);
}
if(inglob) {
glob_cleanup(inglob);
inglob = NULL;
}
if(outfiles)
free(outfiles);
if(urlnode->url)
free(urlnode->url);
if(urlnode->outfile)
free(urlnode->outfile);
if(urlnode->infile)
free(urlnode->infile);
nextnode=urlnode->next;
free(urlnode);
urlnode = nextnode;
}
quit_curl:
if(httpgetfields)
free(httpgetfields);
if(config->engine)
free(config->engine);
curl_easy_cleanup(curl);
config->easy = NULL;
if(easycode)
curl_slist_append(easycode, "curl_easy_cleanup(hnd);");
if(heads.stream && (heads.stream != stdout))
fclose(heads.stream);
if(allocuseragent)
free(config->useragent);
if(config->trace_fopened && config->trace_stream)
fclose(config->trace_stream);
dumpeasycode(config);
if(config->errors_fopened)
fclose(config->errors);
main_free();
return res;
}
static void checkfds(void)
{
#ifdef HAVE_PIPE
int fd[2] = { STDIN_FILENO, STDIN_FILENO };
while( fd[0] == STDIN_FILENO ||
fd[0] == STDOUT_FILENO ||
fd[0] == STDERR_FILENO ||
fd[1] == STDIN_FILENO ||
fd[1] == STDOUT_FILENO ||
fd[1] == STDERR_FILENO )
if(pipe(fd) < 0)
return;
close(fd[0]);
close(fd[1]);
#endif
}
int main(int argc, char *argv[])
{
int res;
struct Configurable config;
memset(&config, 0, sizeof(struct Configurable));
config.errors = stderr;
checkfds();
res = operate(&config, argc, argv);
#ifdef __SYMBIAN32__
if(config.showerror)
pressanykey();
#endif
free_config_fields(&config);
#ifdef __NOVELL_LIBC__
if(getenv("_IN_NETWARE_BASH_") == NULL)
pressanykey();
#endif
#ifdef __VMS
vms_special_exit(res, vms_show);
#else
return res;
#endif
}
static char *my_get_line(FILE *fp)
{
char buf[4096];
char *nl = NULL;
char *retval = NULL;
do {
if(NULL == fgets(buf, sizeof(buf), fp))
break;
if(NULL == retval) {
retval = strdup(buf);
if(!retval)
return NULL;
}
else {
char *ptr;
ptr = realloc(retval, strlen(retval) + strlen(buf) + 1);
if(NULL == ptr) {
free(retval);
return NULL;
}
retval = ptr;
strcat(retval, buf);
}
}
while(NULL == (nl = strchr(retval, '\n')));
if(NULL != nl)
*nl = '\0';
return retval;
}
static void show_dir_errno(FILE *errors, const char *name)
{
switch (ERRNO) {
#ifdef EACCES
case EACCES:
fprintf(errors,"You don't have permission to create %s.\n", name);
break;
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
fprintf(errors,"The directory name %s is too long.\n", name);
break;
#endif
#ifdef EROFS
case EROFS:
fprintf(errors,"%s resides on a read-only file system.\n", name);
break;
#endif
#ifdef ENOSPC
case ENOSPC:
fprintf(errors,"No space left on the file system that will "
"contain the directory %s.\n", name);
break;
#endif
#ifdef EDQUOT
case EDQUOT:
fprintf(errors,"Cannot create directory %s because you "
"exceeded your quota.\n", name);
break;
#endif
default :
fprintf(errors,"Error creating directory %s.\n", name);
break;
}
}
static int create_dir_hierarchy(const char *outfile, FILE *errors)
{
char *tempdir;
char *tempdir2;
char *outdup;
char *dirbuildup;
int result=0;
outdup = strdup(outfile);
if(!outdup)
return -1;
dirbuildup = malloc(sizeof(char) * strlen(outfile));
if(!dirbuildup) {
free(outdup);
return -1;
}
dirbuildup[0] = '\0';
tempdir = strtok(outdup, DIR_CHAR);
while(tempdir != NULL) {
tempdir2 = strtok(NULL, DIR_CHAR);
if(tempdir2 != NULL) {
size_t dlen = strlen(dirbuildup);
if(dlen)
sprintf(&dirbuildup[dlen], "%s%s", DIR_CHAR, tempdir);
else {
if(0 != strncmp(outdup, DIR_CHAR, 1))
strcpy(dirbuildup, tempdir);
else
sprintf(dirbuildup, "%s%s", DIR_CHAR, tempdir);
}
if(access(dirbuildup, F_OK) == -1) {
result = mkdir(dirbuildup,(mode_t)0000750);
if(-1 == result) {
show_dir_errno(errors, dirbuildup);
break;
}
}
}
tempdir = tempdir2;
}
free(dirbuildup);
free(outdup);
return result;
}
#if defined(MSDOS) || defined(WIN32)
#ifndef HAVE_BASENAME
static char *Curl_basename(char *path)
{
char *s1;
char *s2;
s1=strrchr(path, '/');
s2=strrchr(path, '\\');
if(s1 && s2) {
path = (s1 > s2? s1 : s2)+1;
}
else if(s1)
path = s1 + 1;
else if(s2)
path = s2 + 1;
return path;
}
#define basename(x) Curl_basename((x))
#endif
static const char *
msdosify (const char *file_name)
{
static char dos_name[PATH_MAX];
static const char illegal_chars_dos[] = ".+, ;=[]"
"|<>\\\":?*";
static const char *illegal_chars_w95 = &illegal_chars_dos[8];
int idx, dot_idx;
const char *s = file_name;
char *d = dos_name;
const char * const dlimit = dos_name + sizeof(dos_name) - 1;
const char *illegal_aliens = illegal_chars_dos;
size_t len = sizeof (illegal_chars_dos) - 1;
if(_use_lfn (file_name)) {
illegal_aliens = illegal_chars_w95;
len -= (illegal_chars_w95 - illegal_chars_dos);
}
if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
*d++ = *s++;
*d++ = *s++;
}
for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
if(memchr (illegal_aliens, *s, len)) {
if(*s == '.') {
if(idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
*d++ = *s++;
if(*s == '.')
*d++ = *s++;
*d = *s;
}
else if(idx == 0)
*d = '_';
else if(dot_idx >= 0) {
if(dot_idx < 5) {
d[dot_idx - idx] = '_';
*d = '.';
}
else
*d = '-';
}
else
*d = '.';
if(*s == '.')
dot_idx = idx;
}
else if(*s == '+' && s[1] == '+') {
if(idx - 2 == dot_idx) {
*d++ = 'x';
*d = 'x';
}
else {
memcpy (d, "plus", 4);
d += 3;
}
s++;
idx++;
}
else
*d = '_';
}
else
*d = *s;
if(*s == '/') {
idx = 0;
dot_idx = -1;
}
else
idx++;
}
*d = '\0';
return dos_name;
}
static char *
rename_if_dos_device_name (char *file_name)
{
char *base;
struct_stat st_buf;
char fname[PATH_MAX];
strncpy(fname, file_name, PATH_MAX-1);
fname[PATH_MAX-1] = 0;
base = basename(fname);
if(((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
size_t blen = strlen (base);
if(strlen(fname) >= PATH_MAX-1) {
blen--;
base[blen] = 0;
}
memmove (base + 1, base, blen + 1);
base[0] = '_';
strcpy (file_name, fname);
}
return file_name;
}
static char *sanitize_dos_name(char *fn)
{
char tmpfn[PATH_MAX];
if(strlen(fn) >= PATH_MAX)
fn[PATH_MAX-1]=0;
strcpy(tmpfn, msdosify(fn));
free(fn);
return strdup(rename_if_dos_device_name(tmpfn));
}
#endif