diff options
author | Yang Tse <yangsita@gmail.com> | 2011-10-05 00:03:20 +0200 |
---|---|---|
committer | Yang Tse <yangsita@gmail.com> | 2011-10-05 00:03:58 +0200 |
commit | 49b79b76316248d5233d08006234933913faaa3b (patch) | |
tree | 3122e6c5390cb4118296a915308baea7216c4e09 /src/main.c | |
parent | ca2c32636191ba33dbad91e88d8ceecc74bacbd1 (diff) | |
download | curl-49b79b76316248d5233d08006234933913faaa3b.tar.gz |
curl tool: code moved to tool_*.[ch] files
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 4309 |
1 files changed, 0 insertions, 4309 deletions
diff --git a/src/main.c b/src/main.c deleted file mode 100644 index f331e4d9a..000000000 --- a/src/main.c +++ /dev/null @@ -1,4309 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ -#include "setup.h" - -#include <curl/curl.h> - -/* -** system headers -*/ - -#include <sys/types.h> -#include <sys/stat.h> - -#ifdef NETWARE -# ifdef __NOVELL_LIBC__ -# include <screen.h> -# else -# include <nwconio.h> -# endif -#endif - -#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> -#elif defined(HAVE_SYS_UTIME_H) -# include <sys/utime.h> -#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 - -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif - -#ifdef HAVE_NETINET_TCP_H -# include <netinet/tcp.h> -#endif - -#ifdef MSDOS -# include <dos.h> -#endif - -/* -** src subdirectory headers -*/ - -#include "urlglob.h" -#include "writeout.h" -#include "getpass.h" -#include "homedir.h" -#include "curlutil.h" -#include "version.h" -#include "xattr.h" -#include "tool_cfgable.h" -#include "tool_convert.h" -#include "tool_dirhie.h" -#include "tool_doswin.h" -#include "tool_easysrc.h" -#include "tool_libinfo.h" -#include "tool_mfiles.h" -#include "tool_msgs.h" -#include "tool_myfunc.h" -#include "tool_cb_prg.h" -#include "tool_setopt.h" -#include "tool_vms.h" - -#include "tool_cb_rea.h" -#include "tool_cb_wrt.h" -#include "tool_cb_see.h" -#include "tool_cb_skt.h" -#include "tool_cb_hdr.h" -#include "tool_cb_dbg.h" - -#include "tool_binmode.h" -#include "tool_formparse.h" - -#ifdef USE_MANUAL -# include "hugehelp.h" -#endif -#ifdef USE_ENVIRONMENT -# include "writeenv.h" -#endif - -/* -** libcurl subdirectory headers -*/ - -#include "rawstr.h" - -#define ENABLE_CURLX_PRINTF -/* make the curlx header define all printf() functions to use the curlx_* - versions instead */ -#include "curlx.h" - -/* This is low-level hard-hacking memory leak tracking and similar. Using - the library level code from this client-side is ugly, but we do this - anyway for convenience. */ -#include "memdebug.h" /* keep this as LAST include */ - -#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 - -#ifndef O_BINARY -/* since O_BINARY as used in bitmasks, setting it to zero makes it usable in - source code but yet it doesn't ruin anything */ -#define O_BINARY 0 -#endif - -#ifdef MSDOS -#define USE_WATT32 -#ifdef DJGPP -/* we want to glob our own argv[] */ -char **__crt0_glob_function (char *arg) -{ - (void)arg; - return (char**)0; -} -#endif /* __DJGPP__ */ -#endif /* MSDOS */ - -#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 CURLseparator "--_curl_--" - -#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" - -/* - * This is the main global constructor for the app. Call this before - * _any_ libcurl usage. If this fails, *NO* libcurl functions may be - * used, or havoc may be the result. - */ -static CURLcode main_init(void) -{ -#ifdef DJGPP - /* stop stat() wasting time */ - _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; -#endif - - return curl_global_init(CURL_GLOBAL_DEFAULT); -} - -/* - * This is the main global destructor for the app. Call this after - * _all_ libcurl usage is done. - */ -static void main_free(void) -{ - curl_global_cleanup(); - convert_cleanup(); -} - -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 help(void) -{ - int i; - /* A few of these source lines are >80 columns wide, but that's only because - breaking the strings narrower makes this chunk look even worse! - - Starting with 7.18.0, this list of command line options is sorted based - on the long option name. It is not done automatically, although a command - line like the following can help out: - - curl --help | cut -c5- | grep "^-" | sort - */ - 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 DIR 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 STRING/FILE 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 DATA HTTP POST data url encoded (H)", - " --delegation STRING GSS-API delegation permission", - " --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 ENGINGE Crypto engine (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 CONTENT Specify HTTP multipart POST data (H)", - " --form-string STRING Specify HTTP multipart POST data (H)", - " --ftp-account DATA Account data string (F)", - " --ftp-alternative-to-user COMMAND " - "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 ADR Use PORT with given 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", - " -l, --list-only List only names of an FTP directory (F)", - " --local-port RANGE Force use of these local port numbers", - " -L, --location Follow redirects (H)", - " --location-trusted like --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", - " --netrc-file FILE Set up the netrc filename to use", - " -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 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 [PROTOCOL://]HOST[:PORT] Use 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] 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 transfer (F/SFTP)", - " --random-file FILE File for reading random data from (SSL)", - " -r, --range RANGE Retrieve only the bytes within a range", - " --raw Do HTTP \"raw\", without any transfer decoding (H)", - " -e, --referer Referer URL (H)", - " -J, --remote-header-name Use the header-provided filename (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 RATE " - "Stop transfers below speed-limit for 'speed-time' secs", - " -y, --speed-time SECONDS " - "Time for 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", - " --tr-encoding Request compressed transfer encoding (H)", - " -T, --upload-file FILE Transfer FILE to destination", - " --url URL URL to work with", - " -B, --use-ascii Use ASCII/text transfer", - " -u, --user USER[:PASSWORD] Server user and password", - " --tlsuser USER TLS username", - " --tlspassword STRING TLS password", - " --tlsauthtype STRING 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 int parseconfig(const char *filename, - struct Configurable *config); -static char *my_get_line(FILE *fp); - -#define GetStr(str,val) \ -do { \ - if(*(str)) { \ - free(*(str)); \ - *(str) = NULL; \ - } \ - if((val)) \ - *(str) = strdup((val)); \ - if(!*(str)) \ - return PARAM_NO_MEM; \ -} WHILE_FALSE - -static void clean_getout(struct Configurable *config) -{ - struct getout *next; - struct getout *node = config->url_list; - - while(node) { - next = node->next; - Curl_safefree(node->url); - Curl_safefree(node->outfile); - Curl_safefree(node->infile); - Curl_safefree(node); - node = next; - } - config->url_list = NULL; -} - -static struct getout *new_getout(struct Configurable *config) -{ - struct getout *node =malloc(sizeof(struct getout)); - struct getout *last= config->url_last; - if(node) { - /* clear the struct */ - memset(node, 0, sizeof(struct getout)); - - /* append this new node last in the list */ - if(last) - last->next = node; - else - config->url_list = node; /* first node */ - - /* move the last pointer */ - config->url_last = node; - - node->flags = config->default_node_flags; - } - return node; -} - -/* Print list of OpenSSL engines supported. - */ -static void list_engines(const struct curl_slist *engines) -{ - puts("Build-time engines:"); - if(!engines) { - puts(" <none>"); - return; - } - for(; engines; engines = engines->next) - printf(" %s\n", engines->data); -} - -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) { - Curl_safefree(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)) { - /* size_t overflow detection for huge files */ - if(alloc+1 > ((size_t)-1)/2) { - Curl_safefree(buffer); - return PARAM_NO_MEM; - } - alloc *= 2; - /* allocate an extra char, reserved space, for null termination */ - if((newbuf = realloc(buffer, alloc+1)) == NULL) { - Curl_safefree(buffer); - return PARAM_NO_MEM; - } - buffer = newbuf; - } - nread = fread(buffer+nused, 1, alloc-nused, file); - nused += nread; - } while(nread); - /* null terminate the buffer in case it's used as a string later */ - buffer[nused] = '\0'; - /* free trailing slack space, if possible */ - if(alloc != nused) { - if((newbuf = realloc(buffer, nused+1)) != NULL) - buffer = newbuf; - } - /* discard buffer if nothing was read */ - if(!nused) { - Curl_safefree(buffer); /* no string */ - } - } - *size = nused; - *bufp = buffer; - return PARAM_OK; -} - -static void cleanarg(char *str) -{ -#ifdef HAVE_WRITABLE_ARGV - /* now that GetStr has copied the contents of nextarg, wipe the next - * argument out so that the username:password isn't displayed in the - * system process list */ - if(str) { - size_t len = strlen(str); - memset(str, ' ', len); - } -#else - (void)str; -#endif -} - -/* - * Parse the string and write the integer in the given address. Return - * non-zero on failure, zero on success. - * - * The string must start with a digit to be valid. - * - * Since this function gets called with the 'nextarg' pointer from within the - * getparameter a lot, we must check it for NULL before accessing the str - * data. - */ - -static int str2num(long *val, 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; /* Ok */ - } - } - return 1; /* badness */ -} - -/* - * Parse the string and modify the long in the given address. Return - * non-zero on failure, zero on success. - * - * The string is a list of protocols - * - * Since this function gets called with the 'nextarg' pointer from within the - * getparameter a lot, we must check it for NULL before accessing the str - * data. - */ - -static 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); /* because strtok corrupts it */ - - for(token = strtok(buffer, sep); - token; - token = strtok(NULL, sep)) { - enum e_action { allow, deny, set } action = allow; - - struct sprotos const *pp; - - /* Process token modifiers */ - while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */ - switch (*token++) { - case '=': - action = set; - break; - case '-': - action = deny; - break; - case '+': - action = allow; - break; - default: /* Includes case of terminating NULL */ - Curl_safefree(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)) { /* unknown protocol */ - /* If they have specified only this protocol, we say treat it as - if no protocols are allowed */ - if(action == set) - *val = 0; - warnf(config, "unrecognized protocol '%s'\n", token); - } - } - Curl_safefree(buffer); - return 0; -} - -/** - * Parses the given string looking for an offset (which may be - * a larger-than-integer value). - * - * @param val the offset to populate - * @param str the buffer containing the offset - * @return zero if successful, non-zero if failure. - */ -static int str2offset(curl_off_t *val, 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, /* for what purpose */ - char **userpwd) /* pointer to allocated string */ -{ - char *ptr; - if(!*userpwd) - return; - - ptr = strchr(*userpwd, ':'); - if(!ptr) { - /* no password present, prompt for one */ - char passwd[256]=""; - char prompt[256]; - size_t passwdlen; - size_t userlen = strlen(*userpwd); - char *passptr; - - /* build a nice-looking prompt */ - curlx_msnprintf(prompt, sizeof(prompt), - "Enter %s password for user '%s':", - kind, *userpwd); - - /* get password */ - getpass_r(prompt, passwd, sizeof(passwd)); - passwdlen = strlen(passwd); - - /* extend the allocated memory area to fit the password too */ - passptr = realloc(*userpwd, - passwdlen + 1 + /* an extra for the colon */ - userlen + 1); /* an extra for the zero */ - - if(passptr) { - /* append the password separated with a colon */ - passptr[userlen]=':'; - memcpy(&passptr[userlen+1], passwd, passwdlen+1); - *userpwd = passptr; - } - } -} - -static ParameterError add2list(struct curl_slist **list, - 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 long delegation(struct Configurable *config, - char *str) -{ - if(curlx_raw_equal("none", str)) - return CURLGSSAPI_DELEGATION_NONE; - if(curlx_raw_equal("policy", str)) - return CURLGSSAPI_DELEGATION_POLICY_FLAG; - if(curlx_raw_equal("always", str)) - return CURLGSSAPI_DELEGATION_FLAG; - warnf(config, "unrecognized delegation method '%s', using none\n", str); - return CURLGSSAPI_DELEGATION_NONE; -} - -static ParameterError getparameter(char *flag, /* f or -long-flag */ - char *nextarg, /* NULL if unset */ - bool *usedarg, /* set to TRUE if the arg - has been used */ - struct Configurable *config) -{ - char letter; - char subletter=0; /* subletters can only occur on long options */ - int rc; /* generic return code variable */ - const char *parse=NULL; - unsigned int j; - time_t now; - int hit=-1; - bool longopt=FALSE; - bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */ - ParameterError err; - bool toggle=TRUE; /* how to switch boolean options, on or off. Controlled - by using --OPTION or --no-OPTION */ - - /* single-letter, - long-name, - boolean whether it takes an additional argument - */ - static const struct LongShort aliases[]= { - /* all these ones, starting with "*" or "$" as a short-option have *no* - short option to mention. */ - {"*", "url", TRUE}, - {"*a", "random-file", TRUE}, - {"*b", "egd-file", TRUE}, - {"*c", "connect-timeout", TRUE}, - {"*d", "ciphers", TRUE}, - {"*e", "disable-epsv", FALSE}, - {"*E", "epsv", FALSE}, /* made like this to make --no-epsv and --epsv to - work although --disable-epsv is the documented - option */ -#ifdef USE_ENVIRONMENT - {"*f", "environment", FALSE}, -#endif - {"*g", "trace", TRUE}, - {"*h", "trace-ascii", TRUE}, - {"*i", "limit-rate", TRUE}, - {"*j", "compressed", FALSE}, - {"*J", "tr-encoding", FALSE}, - {"*k", "digest", FALSE}, - {"*l", "negotiate", FALSE}, - {"*m", "ntlm", FALSE}, - {"*M", "ntlm-wb", 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}, /* this is the previous name */ - {"*y", "max-filesize", TRUE}, - {"*z", "disable-eprt", FALSE}, - {"*Z", "eprt", FALSE}, /* made like this to make --no-eprt and --eprt to - work although --disable-eprt is the documented - option */ - {"$a", "ftp-ssl", FALSE}, /* deprecated name since 7.20.0 */ - {"$a", "ssl", FALSE}, /* new option name in 7.20.0, previously this - was ftp-ssl */ - {"$b", "ftp-pasv", FALSE}, - {"$c", "socks5", TRUE}, - {"$c", "socks", TRUE}, /* this is how the option once was documented - but we prefer the --socks5 version for - explicit version */ - {"$d", "tcp-nodelay",FALSE}, - {"$e", "proxy-digest", FALSE}, - {"$f", "proxy-basic", FALSE}, - {"$g", "retry", TRUE}, - {"$h", "retry-delay", TRUE}, - {"$i", "retry-max-time", TRUE}, - {"$k", "proxy-negotiate", FALSE}, - {"$m", "ftp-account", TRUE}, - {"$n", "proxy-anyauth", FALSE}, - {"$o", "trace-time", FALSE}, - {"$p", "ignore-content-length", FALSE}, - {"$q", "ftp-skip-pasv-ip", FALSE}, - {"$r", "ftp-method", TRUE}, - {"$s", "local-port", TRUE}, - {"$t", "socks4", TRUE}, - {"$T", "socks4a", TRUE}, - {"$u", "ftp-alternative-to-user", TRUE}, - {"$v", "ftp-ssl-reqd", FALSE}, /* deprecated name since 7.20.0 */ - {"$v", "ssl-reqd", FALSE}, /* new option name in 7.20.0, previously this - was ftp-ssl-reqd */ - {"$w", "sessionid", FALSE}, /* listed as --no-sessionid in the help */ - {"$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}, /* listed as --no-keepalive in the help */ - {"$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}, - {"$G", "delegation", 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}, - {"ne", "netrc-file", TRUE}, - {"N", "buffer", FALSE}, /* listed as --no-buffer in the help */ - {"o", "output", TRUE}, - {"O", "remote-name", FALSE}, - {"Oa", "remote-name-all", FALSE}, - {"p", "proxytunnel", FALSE}, - {"P", "ftpport", TRUE}, /* older version */ - {"P", "ftp-port", TRUE}, - {"q", "disable", FALSE}, - {"Q", "quote", TRUE}, - {"r", "range", TRUE}, - {"R", "remote-time", FALSE}, - {"s", "silent", FALSE}, - {"S", "show-error", FALSE}, - {"t", "telnet-options", TRUE}, /* this is documented as telnet-option */ - {"T", "upload-file", TRUE}, - {"u", "user", TRUE}, - {"U", "proxy-user", TRUE}, - {"v", "verbose", FALSE}, - {"V", "version", FALSE}, - {"w", "write-out", TRUE}, - {"x", "proxy", TRUE}, - {"X", "request", TRUE}, - {"X", "http-request", TRUE}, /* OBSOLETE VERSION */ - {"Y", "speed-limit", TRUE}, - {"y", "speed-time", TRUE}, - {"z", "time-cond", TRUE}, - {"#", "progress-bar",FALSE}, - {"~", "xattr",FALSE}, - }; - - if(('-' != flag[0]) || - (('-' == flag[0]) && ('-' == flag[1]))) { - /* this should be a long name */ - char *word=('-' == flag[0])?flag+2:flag; - size_t fnam=strlen(word); - int numhits=0; - - if(!strncmp(word, "no-", 3)) { - /* disable this option but ignore the "no-" part when looking for it */ - 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; /* a single unique hit */ - break; - } - parse = aliases[j].letter; - hit = j; - } - } - if(numhits>1) { - /* this is at least the second match! */ - return PARAM_OPTION_AMBIGUOUS; - } - if(hit < 0) { - return PARAM_OPTION_UNKNOWN; - } - } - else { - flag++; /* prefixed with one dash, pass it */ - hit=-1; - parse = flag; - } - - do { - /* we can loop here if we have multiple single-letters */ - - if(!longopt) { - if(NULL != parse) { - letter = (char)*parse; - } - else { - letter = '\0'; - } - subletter='\0'; - } - else { - letter = parse[0]; - subletter = parse[1]; - } - *usedarg = FALSE; /* default is that we don't use the arg */ - - if(hit < 0) { - for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) { - if(letter == aliases[j].letter[0]) { - hit = j; - break; - } - } - if(hit < 0) { - return PARAM_OPTION_UNKNOWN; - } - } - - if(aliases[hit].extraparam) { - /* this option requires an extra parameter */ - if(!longopt && parse[1]) { - nextarg=(char *)&parse[1]; /* this is the actual extra parameter */ - singleopt=TRUE; /* don't loop anymore after this */ - } - else if(!nextarg) - return PARAM_REQUIRES_PARAMETER; - else - *usedarg = TRUE; /* mark it as used */ - } - - switch(letter) { - case '*': /* options without a short option */ - switch(subletter) { - case 'a': /* random-file */ - GetStr(&config->random_file, nextarg); - break; - case 'b': /* egd-file */ - GetStr(&config->egd_file, nextarg); - break; - case 'c': /* connect-timeout */ - if(str2num(&config->connecttimeout, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case 'd': /* ciphers */ - GetStr(&config->cipher_list, nextarg); - break; - case 'e': /* --disable-epsv */ - config->disable_epsv = toggle; - break; - case 'E': /* --epsv */ - config->disable_epsv = (!toggle)?TRUE:FALSE; - break; -#ifdef USE_ENVIRONMENT - case 'f': - config->writeenv = toggle; - break; -#endif - case 'g': /* --trace */ - GetStr(&config->trace_dump, nextarg); - if(config->tracetype && (config->tracetype != TRACE_BIN)) - warnf(config, "--trace overrides an earlier trace/verbose option\n"); - config->tracetype = TRACE_BIN; - break; - case 'h': /* --trace-ascii */ - GetStr(&config->trace_dump, nextarg); - if(config->tracetype && (config->tracetype != TRACE_ASCII)) - warnf(config, - "--trace-ascii overrides an earlier trace/verbose option\n"); - config->tracetype = TRACE_ASCII; - break; - case 'i': /* --limit-rate */ - { - /* We support G, M, K too */ - char *unit; - curl_off_t value = curlx_strtoofft(nextarg, &unit, 0); - - if(!*unit) - unit=(char *)"b"; - else if(strlen(unit) > 1) - unit=(char *)"w"; /* unsupported */ - - switch(*unit) { - case 'G': - case 'g': - value *= 1024*1024*1024; - break; - case 'M': - case 'm': - value *= 1024*1024; - break; - case 'K': - case 'k': - value *= 1024; - break; - case 'b': - case 'B': - /* for plain bytes, leave as-is */ - break; - default: - warnf(config, "unsupported rate unit. Use G, M, K or B!\n"); - return PARAM_BAD_USE; - } - config->recvpersecond = value; - config->sendpersecond = value; - } - break; - - case 'j': /* --compressed */ - if(toggle && !(curlinfo->features & CURL_VERSION_LIBZ)) - return PARAM_LIBCURL_DOESNT_SUPPORT; - config->encoding = toggle; - break; - - case 'J': /* --tr-encoding */ - config->tr_encoding = toggle; - break; - - case 'k': /* --digest */ - if(toggle) - config->authtype |= CURLAUTH_DIGEST; - else - config->authtype &= ~CURLAUTH_DIGEST; - break; - - case 'l': /* --negotiate */ - 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': /* --ntlm */ - 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 'M': /* --ntlm-wb */ - if(toggle) { - if(curlinfo->features & CURL_VERSION_NTLM_WB) - config->authtype |= CURLAUTH_NTLM_WB; - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - } - else - config->authtype &= ~CURLAUTH_NTLM_WB; - break; - - case 'n': /* --basic for completeness */ - if(toggle) - config->authtype |= CURLAUTH_BASIC; - else - config->authtype &= ~CURLAUTH_BASIC; - break; - - case 'o': /* --anyauth, let libcurl pick it */ - if(toggle) - config->authtype = CURLAUTH_ANY; - /* --no-anyauth simply doesn't touch it */ - break; - -#ifdef USE_WATT32 - case 'p': /* --wdebug */ - dbug_init(); - break; -#endif - case 'q': /* --ftp-create-dirs */ - config->ftp_create_dirs = toggle; - break; - - case 'r': /* --create-dirs */ - config->create_dirs = TRUE; - break; - - case 's': /* --max-redirs */ - /* specified max no of redirects (http(s)) */ - if(str2num(&config->maxredirs, nextarg)) - return PARAM_BAD_NUMERIC; - break; - - case 't': /* --proxy-ntlm */ - if(curlinfo->features & CURL_VERSION_NTLM) - config->proxyntlm = toggle; - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - - case 'u': /* --crlf */ - /* LF -> CRLF conversion? */ - config->crlf = TRUE; - break; - - case 'v': /* --stderr */ - if(strcmp(nextarg, "-")) { - FILE *newfile = fopen(nextarg, "wt"); - if(!newfile) - warnf(config, "Failed to open %s!\n", nextarg); - else { - if(config->errors_fopened) - fclose(config->errors); - config->errors = newfile; - config->errors_fopened = TRUE; - } - } - else - config->errors = stdout; - break; - case 'w': /* --interface */ - /* interface */ - GetStr(&config->iface, nextarg); - break; - case 'x': /* --krb */ - /* kerberos level string */ - if(curlinfo->features & (CURL_VERSION_KERBEROS4 | - CURL_VERSION_GSSNEGOTIATE)) - GetStr(&config->krblevel, nextarg); - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case 'y': /* --max-filesize */ - if(str2offset(&config->max_filesize, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case 'z': /* --disable-eprt */ - config->disable_eprt = toggle; - break; - case 'Z': /* --eprt */ - config->disable_eprt = (!toggle)?TRUE:FALSE; - break; - - default: /* the URL! */ - { - struct getout *url; - if(config->url_get || ((config->url_get = config->url_list) != NULL)) { - /* there's a node here, if it already is filled-in continue to find - an "empty" node */ - while(config->url_get && (config->url_get->flags&GETOUT_URL)) - config->url_get = config->url_get->next; - } - - /* now there might or might not be an available node to fill in! */ - - if(config->url_get) - /* existing node */ - url = config->url_get; - else - /* there was no free node, create one! */ - url=new_getout(config); - - if(url) { - /* fill in the URL */ - GetStr(&url->url, nextarg); - url->flags |= GETOUT_URL; - } - } - } - break; - case '$': /* more options without a short option */ - switch(subletter) { - case 'a': /* --ftp-ssl */ - if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) - return PARAM_LIBCURL_DOESNT_SUPPORT; - config->ftp_ssl = toggle; - break; - case 'b': /* --ftp-pasv */ - Curl_safefree(config->ftpport); - break; - case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves - the name locally and passes on the resolved address */ - GetStr(&config->socksproxy, nextarg); - config->socksver = CURLPROXY_SOCKS5; - break; - case 't': /* --socks4 specifies a socks4 proxy to use */ - GetStr(&config->socksproxy, nextarg); - config->socksver = CURLPROXY_SOCKS4; - break; - case 'T': /* --socks4a specifies a socks4a proxy to use */ - GetStr(&config->socksproxy, nextarg); - config->socksver = CURLPROXY_SOCKS4A; - break; - case '2': /* --socks5-hostname specifies a socks5 proxy and enables name - resolving with the proxy */ - GetStr(&config->socksproxy, nextarg); - config->socksver = CURLPROXY_SOCKS5_HOSTNAME; - break; - case 'd': /* --tcp-nodelay option */ - config->tcp_nodelay = toggle; - break; - case 'e': /* --proxy-digest */ - config->proxydigest = toggle; - break; - case 'f': /* --proxy-basic */ - config->proxybasic = toggle; - break; - case 'g': /* --retry */ - if(str2num(&config->req_retry, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case 'h': /* --retry-delay */ - if(str2num(&config->retry_delay, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case 'i': /* --retry-max-time */ - if(str2num(&config->retry_maxtime, nextarg)) - return PARAM_BAD_NUMERIC; - break; - - case 'k': /* --proxy-negotiate */ - if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE) - config->proxynegotiate = toggle; - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case 'm': /* --ftp-account */ - GetStr(&config->ftp_account, nextarg); - break; - case 'n': /* --proxy-anyauth */ - config->proxyanyauth = toggle; - break; - case 'o': /* --trace-time */ - config->tracetime = toggle; - break; - case 'p': /* --ignore-content-length */ - config->ignorecl = toggle; - break; - case 'q': /* --ftp-skip-pasv-ip */ - config->ftp_skip_ip = toggle; - break; - case 'r': /* --ftp-method (undocumented at this point) */ - config->ftp_filemethod = ftpfilemethod(config, nextarg); - break; - case 's': /* --local-port */ - rc = sscanf(nextarg, "%d - %d", - &config->localport, - &config->localportrange); - if(!rc) - return PARAM_BAD_USE; - else if(rc == 1) - config->localportrange = 1; /* default number of ports to try */ - else { - config->localportrange -= config->localport; - if(config->localportrange < 1) { - warnf(config, "bad range input\n"); - return PARAM_BAD_USE; - } - } - break; - case 'u': /* --ftp-alternative-to-user */ - GetStr(&config->ftp_alternative_to_user, nextarg); - break; - case 'v': /* --ftp-ssl-reqd */ - if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) - return PARAM_LIBCURL_DOESNT_SUPPORT; - config->ftp_ssl_reqd = toggle; - break; - case 'w': /* --no-sessionid */ - config->disable_sessionid = (!toggle)?TRUE:FALSE; - break; - case 'x': /* --ftp-ssl-control */ - if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) - return PARAM_LIBCURL_DOESNT_SUPPORT; - config->ftp_ssl_control = toggle; - break; - case 'y': /* --ftp-ssl-ccc */ - config->ftp_ssl_ccc = toggle; - if(!config->ftp_ssl_ccc_mode) - config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE; - break; - case 'j': /* --ftp-ssl-ccc-mode */ - config->ftp_ssl_ccc = TRUE; - config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg); - break; - case 'z': /* --libcurl */ - GetStr(&config->libcurl, nextarg); - break; - case '#': /* --raw */ - config->raw = toggle; - break; - case '0': /* --post301 */ - config->post301 = toggle; - break; - case '1': /* --no-keepalive */ - config->nokeepalive = (!toggle)?TRUE:FALSE; - break; - case '3': /* --keepalive-time */ - if(str2num(&config->alivetime, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case '4': /* --post302 */ - config->post302 = toggle; - break; - case '5': /* --noproxy */ - /* This specifies the noproxy list */ - GetStr(&config->noproxy, nextarg); - break; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case '6': /* --socks5-gssapi-service */ - GetStr(&config->socks5_gssapi_service, nextarg); - break; - case '7': /* --socks5-gssapi-nec*/ - config->socks5_gssapi_nec = TRUE; - break; -#endif - case '8': /* --proxy1.0 */ - /* http 1.0 proxy */ - GetStr(&config->proxy, nextarg); - config->proxyver = CURLPROXY_HTTP_1_0; - break; - case '9': /* --tftp-blksize */ - str2num(&config->tftp_blksize, nextarg); - break; - case 'A': /* --mail-from */ - GetStr(&config->mail_from, nextarg); - break; - case 'B': /* --mail-rcpt */ - /* append receiver to a list */ - err = add2list(&config->mail_rcpt, nextarg); - if(err) - return err; - break; - case 'C': /* --ftp-pret */ - config->ftp_pret = toggle; - break; - case 'D': /* --proto */ - config->proto_present = TRUE; - if(proto2num(config, &config->proto, nextarg)) - return PARAM_BAD_USE; - break; - case 'E': /* --proto-redir */ - config->proto_redir_present = TRUE; - if(proto2num(config, &config->proto_redir, nextarg)) - return PARAM_BAD_USE; - break; - case 'F': /* --resolve */ - err = add2list(&config->resolve, nextarg); - if(err) - return err; - break; - case 'G': /* --delegation LEVEL */ - config->gssapi_delegation = delegation(config, nextarg); - break; - } - break; - case '#': /* --progress-bar */ - if(toggle) - config->progressmode = CURL_PROGRESS_BAR; - else - config->progressmode = CURL_PROGRESS_STATS; - break; - case '~': /* --xattr */ - config->xattr = toggle; - break; - case '0': - /* HTTP version 1.0 */ - config->httpversion = CURL_HTTP_VERSION_1_0; - break; - case '1': - /* TLS version 1 */ - config->ssl_version = CURL_SSLVERSION_TLSv1; - break; - case '2': - /* SSL version 2 */ - config->ssl_version = CURL_SSLVERSION_SSLv2; - break; - case '3': - /* SSL version 3 */ - config->ssl_version = CURL_SSLVERSION_SSLv3; - break; - case '4': - /* IPv4 */ - config->ip_version = 4; - break; - case '6': - /* IPv6 */ - config->ip_version = 6; - break; - case 'a': - /* This makes the FTP sessions use APPE instead of STOR */ - config->ftp_append = toggle; - break; - case 'A': - /* This specifies the User-Agent name */ - GetStr(&config->useragent, nextarg); - break; - case 'b': /* cookie string coming up: */ - if(nextarg[0] == '@') { - nextarg++; - } - else if(strchr(nextarg, '=')) { - /* A cookie string must have a =-letter */ - GetStr(&config->cookie, nextarg); - break; - } - /* We have a cookie file to read from! */ - GetStr(&config->cookiefile, nextarg); - break; - case 'B': - /* use ASCII/text when transferring */ - config->use_ascii = toggle; - break; - case 'c': - /* get the file name to dump all cookies in */ - GetStr(&config->cookiejar, nextarg); - break; - case 'C': - /* This makes us continue an ftp transfer at given position */ - if(!curlx_strequal(nextarg, "-")) { - if(str2offset(&config->resume_from, nextarg)) - return PARAM_BAD_NUMERIC; - config->resume_from_current = FALSE; - } - else { - config->resume_from_current = TRUE; - config->resume_from = 0; - } - config->use_resume=TRUE; - break; - case 'd': - /* postfield data */ - { - char *postdata=NULL; - FILE *file; - size_t size = 0; - - if(subletter == 'e') { /* --data-urlencode*/ - /* [name]=[content], we encode the content part only - * [name]@[file name] - * - * Case 2: we first load the file using that name and then encode - * the content. - */ - const char *p = strchr(nextarg, '='); - size_t nlen; - char is_file; - if(!p) - /* there was no '=' letter, check for a '@' instead */ - p = strchr(nextarg, '@'); - if(p) { - nlen = p - nextarg; /* length of the name part */ - is_file = *p++; /* pass the separator */ - } - else { - /* neither @ nor =, so no name and it isn't a file */ - nlen = is_file = 0; - p = nextarg; - } - if('@' == is_file) { - /* a '@' letter, it means that a file name or - (stdin) follows */ - - 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) { - /* no data from the file, point to a zero byte string to make this - get sent as a POST anyway */ - postdata=strdup(""); - size = 0; - } - else { - char *enc = curl_easy_escape(config->easy, postdata, (int)size); - Curl_safefree(postdata); /* no matter if it worked or not */ - if(enc) { - /* now make a string with the name from above and append the - encoded string */ - size_t outlen = nlen + strlen(enc) + 2; - char *n = malloc(outlen); - if(!n) { - curl_free(enc); - return PARAM_NO_MEM; - } - if(nlen > 0) { /* only append '=' if we have a name */ - snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc); - size = outlen-1; - } - else { - strcpy(n, enc); - size = outlen-2; /* since no '=' was inserted */ - } - curl_free(enc); - postdata = n; - } - else - return PARAM_NO_MEM; - } - } - else if('@' == *nextarg) { - /* the data begins with a '@' letter, it means that a file name - or - (stdin) follows */ - nextarg++; /* pass the @ */ - - if(curlx_strequal("-", nextarg)) { - file = stdin; - if(subletter == 'b') /* forced data-binary */ - set_binmode(stdin); - } - else { - file = fopen(nextarg, "rb"); - if(!file) - warnf(config, "Couldn't read data from file \"%s\", this makes " - "an empty POST.\n", nextarg); - } - - if(subletter == 'b') - /* forced binary */ - err = file2memory(&postdata, &size, file); - else { - err = file2string(&postdata, file); - if(postdata) - size = strlen(postdata); - } - - if(file && (file != stdin)) - fclose(file); - if(err) - return err; - - if(!postdata) { - /* no data from the file, point to a zero byte string to make this - get sent as a POST anyway */ - postdata=strdup(""); - } - } - else { - GetStr(&postdata, nextarg); - size=strlen(postdata); - } - -#ifdef CURL_DOES_CONVERSIONS - if(subletter != 'b') { /* NOT forced binary, convert to ASCII */ - convert_to_network(postdata, strlen(postdata)); - } -#endif - - if(config->postfields) { - /* we already have a string, we append this one with a separating - &-letter */ - char *oldpost=config->postfields; - curl_off_t oldlen = config->postfieldsize; - curl_off_t newlen = oldlen + size + 2; - config->postfields=malloc((size_t)newlen); - if(!config->postfields) { - Curl_safefree(postdata); - return PARAM_NO_MEM; - } - memcpy(config->postfields, oldpost, (size_t)oldlen); - /* use byte value 0x26 for '&' to accommodate non-ASCII platforms */ - config->postfields[oldlen]='\x26'; - memcpy(&config->postfields[oldlen+1], postdata, size); - config->postfields[oldlen+1+size]=0; - Curl_safefree(oldpost); - Curl_safefree(postdata); - config->postfieldsize += size+1; - } - else { - config->postfields=postdata; - config->postfieldsize = size; - } - } - /* - We can't set the request type here, as this data might be used in - a simple GET if -G is used. Already or soon. - - if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) - return PARAM_BAD_USE; - */ - break; - case 'D': - /* dump-header to given file name */ - GetStr(&config->headerfile, nextarg); - break; - case 'e': - { - char *ptr = strstr(nextarg, ";auto"); - if(ptr) { - /* Automatic referer requested, this may be combined with a - set initial one */ - config->autoreferer = TRUE; - *ptr = 0; /* zero terminate here */ - } - else - config->autoreferer = FALSE; - GetStr(&config->referer, nextarg); - } - break; - case 'E': - switch(subletter) { - case 'a': /* CA info PEM file */ - /* CA info PEM file */ - GetStr(&config->cacert, nextarg); - break; - case 'b': /* cert file type */ - GetStr(&config->cert_type, nextarg); - break; - case 'c': /* private key file */ - GetStr(&config->key, nextarg); - break; - case 'd': /* private key file type */ - GetStr(&config->key_type, nextarg); - break; - case 'e': /* private key passphrase */ - GetStr(&config->key_passwd, nextarg); - cleanarg(nextarg); - break; - case 'f': /* crypto engine */ - GetStr(&config->engine, nextarg); - if(config->engine && curlx_raw_equal(config->engine,"list")) - config->list_engines = TRUE; - break; - case 'g': /* CA info PEM file */ - /* CA cert directory */ - GetStr(&config->capath, nextarg); - break; - case 'h': /* --pubkey public key file */ - GetStr(&config->pubkey, nextarg); - break; - case 'i': /* --hostpubmd5 md5 of the host public key */ - GetStr(&config->hostpubmd5, nextarg); - if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32) - return PARAM_BAD_USE; - break; - case 'j': /* CRL info PEM file */ - /* CRL file */ - GetStr(&config->crlfile, nextarg); - break; - case 'k': /* TLS username */ - if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) - GetStr(&config->tls_username, nextarg); - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case 'l': /* TLS password */ - if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) - GetStr(&config->tls_password, nextarg); - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case 'm': /* TLS authentication type */ - if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { - GetStr(&config->tls_authtype, nextarg); - if(!strequal(config->tls_authtype, "SRP")) - return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ - } - else - return PARAM_LIBCURL_DOESNT_SUPPORT; - break; - default: /* certificate file */ - { - char *ptr = strchr(nextarg, ':'); - /* Since we live in a world of weirdness and confusion, the win32 - dudes can use : when using drive letters and thus - c:\file:password needs to work. In order not to break - compatibility, we still use : as separator, but we try to detect - when it is used for a file name! On windows. */ -#ifdef WIN32 - if(ptr && - (ptr == &nextarg[1]) && - (nextarg[2] == '\\' || nextarg[2] == '/') && - (ISALPHA(nextarg[0])) ) - /* colon in the second column, followed by a backslash, and the - first character is an alphabetic letter: - - this is a drive letter colon */ - ptr = strchr(&nextarg[3], ':'); /* find the next one instead */ -#endif - if(ptr) { - /* we have a password too */ - *ptr=0; - ptr++; - GetStr(&config->key_passwd, ptr); - } - GetStr(&config->cert, nextarg); - cleanarg(nextarg); - } - } - break; - case 'f': - /* fail hard on errors */ - config->failonerror = toggle; - break; - case 'F': - /* "form data" simulation, this is a little advanced so lets do our best - to sort this out slowly and carefully */ - if(formparse(config, - nextarg, - &config->httppost, - &config->last_post, - (subletter=='s')?TRUE:FALSE)) /* 's' means literal string */ - return PARAM_BAD_USE; - if(SetHTTPrequest(config, HTTPREQ_POST, &config->httpreq)) - return PARAM_BAD_USE; - break; - - case 'g': /* g disables URLglobbing */ - config->globoff = toggle; - break; - - case 'G': /* HTTP GET */ - config->use_httpget = TRUE; - break; - - case 'h': /* h for help */ - if(toggle) { - help(); - return PARAM_HELP_REQUESTED; - } - /* we now actually support --no-help too! */ - break; - case 'H': - /* A custom header to append to a list */ - err = add2list(&config->headers, nextarg); - if(err) - return err; - break; - case 'i': - config->include_headers = toggle; /* include the headers as well in the - general output stream */ - break; - case 'j': - config->cookiesession = toggle; - break; - case 'I': - /* - * no_body will imply include_headers later on - */ - config->no_body = toggle; - if(SetHTTPrequest(config, - (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET, - &config->httpreq)) - return PARAM_BAD_USE; - break; - case 'J': /* --remote-header-name */ - 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': /* allow insecure SSL connects */ - config->insecure_ok = toggle; - break; - case 'K': /* parse config file */ - if(parseconfig(nextarg, config)) - warnf(config, "error trying read config from the '%s' file\n", - nextarg); - break; - case 'l': - config->dirlistonly = toggle; /* only list the names of the FTP dir */ - break; - case 'L': - config->followlocation = toggle; /* Follow Location: HTTP headers */ - switch (subletter) { - case 't': - /* Continue to send authentication (user+password) when following - * locations, even when hostname changed */ - config->unrestricted_auth = toggle; - break; - } - break; - case 'm': - /* specified max time */ - if(str2num(&config->timeout, nextarg)) - return PARAM_BAD_NUMERIC; - break; - case 'M': /* M for manual, huge help */ - if(toggle) { /* --no-manual shows no manual... */ -#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': /* CA info PEM file */ - /* use .netrc or URL */ - config->netrc_opt = toggle; - break; - case 'e': /* netrc-file */ - GetStr(&config->netrc_file, nextarg); - break; - default: - /* pick info from .netrc, if this is used for http, curl will - automatically enfore user+password with the request */ - config->netrc = toggle; - break; - } - break; - case 'N': - /* disable the output I/O buffering. note that the option is called - --buffer but is mostly used in the negative form: --no-buffer */ - if(longopt) - config->nobuffer = (!toggle)?TRUE:FALSE; - else - config->nobuffer = toggle; - break; - case 'O': /* --remote-name */ - if(subletter == 'a') { /* --remote-name-all */ - config->default_node_flags = toggle?GETOUT_USEREMOTE:0; - break; - } - /* fall-through! */ - case 'o': /* --output */ - /* output file */ - { - struct getout *url; - if(config->url_out || ((config->url_out = config->url_list) != NULL)) { - /* there's a node here, if it already is filled-in continue to find - an "empty" node */ - while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE)) - config->url_out = config->url_out->next; - } - - /* now there might or might not be an available node to fill in! */ - - if(config->url_out) - /* existing node */ - url = config->url_out; - else - /* there was no free node, create one! */ - url=new_getout(config); - - if(url) { - /* fill in the outfile */ - if('o' == letter) { - GetStr(&url->outfile, nextarg); - url->flags &= ~GETOUT_USEREMOTE; /* switch off */ - } - else { - url->outfile=NULL; /* leave it */ - if(toggle) - url->flags |= GETOUT_USEREMOTE; /* switch on */ - else - url->flags &= ~GETOUT_USEREMOTE; /* switch off */ - } - url->flags |= GETOUT_OUTFILE; - } - } - break; - case 'P': - /* This makes the FTP sessions use PORT instead of PASV */ - /* use <eth0> or <192.168.10.10> style addresses. Anything except - this will make us try to get the "default" address. - NOTE: this is a changed behaviour since the released 4.1! - */ - GetStr(&config->ftpport, nextarg); - break; - case 'p': - /* proxy tunnel for non-http protocols */ - config->proxytunnel = toggle; - break; - - case 'q': /* if used first, already taken care of, we do it like - this so we don't cause an error! */ - break; - case 'Q': - /* QUOTE command to send to FTP server */ - switch(nextarg[0]) { - case '-': - /* prefixed with a dash makes it a POST TRANSFER one */ - nextarg++; - err = add2list(&config->postquote, nextarg); - break; - case '+': - /* prefixed with a plus makes it a just-before-transfer one */ - nextarg++; - err = add2list(&config->prequote, nextarg); - break; - default: - err = add2list(&config->quote, nextarg); - break; - } - if(err) - return err; - break; - case 'r': - /* Specifying a range WITHOUT A DASH will create an illegal HTTP range - (and won't actually be range by definition). The man page previously - claimed that to be a good way, why this code is added to work-around - it. */ - if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) { - char buffer[32]; - curl_off_t off; - warnf(config, - "A 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); - Curl_safefree(config->range); - config->range = strdup(buffer); - } - { - /* byte range requested */ - char* tmp_range; - tmp_range=nextarg; - while(*tmp_range != '\0') { - if(!ISDIGIT(*tmp_range)&&*tmp_range!='-'&&*tmp_range!=',') { - warnf(config,"Invalid character is found in given range. " - "A specified range MUST have only digits in " - "\'start\'-\'stop\'. The server's response to this " - "request is uncertain.\n"); - break; - } - tmp_range++; - } - /* byte range requested */ - GetStr(&config->range, nextarg); - } - break; - case 'R': - /* use remote file's time */ - config->remote_time = toggle; - break; - case 's': - /* don't show progress meter, don't show errors : */ - if(toggle) - config->mute = config->noprogress = TRUE; - else - config->mute = config->noprogress = FALSE; - config->showerror = (!toggle)?TRUE:FALSE; /* toggle off */ - break; - case 'S': - /* show errors */ - config->showerror = toggle; /* toggle on if used with -s */ - break; - case 't': - /* Telnet options */ - err = add2list(&config->telnet_options, nextarg); - if(err) - return err; - break; - case 'T': - /* we are uploading */ - { - struct getout *url; - if(config->url_out || ((config->url_out = config->url_list) != NULL)) { - /* there's a node here, if it already is filled-in continue to find - an "empty" node */ - while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD)) - config->url_out = config->url_out->next; - } - - /* now there might or might not be an available node to fill in! */ - - if(config->url_out) - /* existing node */ - url = config->url_out; - else - /* there was no free node, create one! */ - url=new_getout(config); - - if(url) { - url->flags |= GETOUT_UPLOAD; /* mark -T used */ - if(!*nextarg) - url->flags |= GETOUT_NOUPLOAD; - else { - /* "-" equals stdin, but keep the string around for now */ - GetStr(&url->infile, nextarg); - } - } - } - break; - case 'u': - /* user:password */ - GetStr(&config->userpwd, nextarg); - cleanarg(nextarg); - checkpasswd("host", &config->userpwd); - break; - case 'U': - /* Proxy user:password */ - GetStr(&config->proxyuserpwd, nextarg); - cleanarg(nextarg); - checkpasswd("proxy", &config->proxyuserpwd); - break; - case 'v': - if(toggle) { - /* the '%' thing here will cause the trace get sent to stderr */ - Curl_safefree(config->trace_dump); - config->trace_dump = strdup("%"); - if(config->tracetype && (config->tracetype != TRACE_PLAIN)) - warnf(config, - "-v, --verbose overrides an earlier trace/verbose option\n"); - config->tracetype = TRACE_PLAIN; - } - else - /* verbose is disabled here */ - config->tracetype = TRACE_NONE; - break; - case 'V': - { - const char * const *proto; - - if(!toggle) - /* --no-version yields no output! */ - break; - - printf(CURL_ID "%s\n", curl_version()); - if(curlinfo->protocols) { - printf("Protocols: "); - for(proto=curlinfo->protocols; *proto; ++proto) { - printf("%s ", *proto); - } - puts(""); /* newline */ - } - if(curlinfo->features) { - unsigned int i; - struct feat { - const char *name; - int bitmask; - }; - static const struct feat feats[] = { - {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, - {"Debug", CURL_VERSION_DEBUG}, - {"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}, - {"NTLM_WB", CURL_VERSION_NTLM_WB}, - {"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(""); /* newline */ - } - } - return PARAM_HELP_REQUESTED; - case 'w': - /* get the output string */ - if('@' == *nextarg) { - /* the data begins with a '@' letter, it means that a file name - or - (stdin) follows */ - FILE *file; - const char *fname; - nextarg++; /* pass the @ */ - 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': - /* proxy */ - GetStr(&config->proxy, nextarg); - config->proxyver = CURLPROXY_HTTP; - break; - case 'X': - /* set custom request */ - GetStr(&config->customrequest, nextarg); - break; - case 'y': - /* low speed time */ - if(str2num(&config->low_speed_time, nextarg)) - return PARAM_BAD_NUMERIC; - if(!config->low_speed_limit) - config->low_speed_limit = 1; - break; - case 'Y': - /* low speed limit */ - if(str2num(&config->low_speed_limit, nextarg)) - return PARAM_BAD_NUMERIC; - if(!config->low_speed_time) - config->low_speed_time=30; - break; - case 'z': /* time condition coming up */ - switch(*nextarg) { - case '+': - nextarg++; - default: - /* If-Modified-Since: (section 14.28 in RFC2068) */ - config->timecond = CURL_TIMECOND_IFMODSINCE; - break; - case '-': - /* If-Unmodified-Since: (section 14.24 in RFC2068) */ - config->timecond = CURL_TIMECOND_IFUNMODSINCE; - nextarg++; - break; - case '=': - /* Last-Modified: (section 14.29 in RFC2068) */ - config->timecond = CURL_TIMECOND_LASTMOD; - nextarg++; - break; - } - now=time(NULL); - config->condtime=curl_getdate(nextarg, &now); - if(-1 == (int)config->condtime) { - /* now let's see if it is a file name to get the time from instead! */ - struct_stat statbuf; - if(-1 == stat(nextarg, &statbuf)) { - /* failed, remove time condition */ - config->timecond = CURL_TIMECOND_NONE; - warnf(config, - "Illegal date format for -z, --timecond (and not " - "a file name). Disabling time condition. " - "See curl_getdate(3) for valid date syntax.\n"); - } - else { - /* pull the time out from the file */ - config->condtime = statbuf.st_mtime; - } - } - break; - default: /* unknown flag */ - return PARAM_OPTION_UNKNOWN; - } - hit = -1; - - } while(!longopt && !singleopt && *++parse && !*usedarg); - - return PARAM_OK; -} - -/* - * Copies the string from line to the buffer at param, unquoting - * backslash-quoted characters and NUL-terminating the output string. - * Stops at the first non-backslash-quoted double quote character or the - * end of the input string. param must be at least as long as the input - * string. Returns the pointer after the last handled input character. - */ -static const char *unslashquote(const char *line, char *param) -{ - while(*line && (*line != '\"')) { - if(*line == '\\') { - char out; - line++; - - /* default is to output the letter after the backslash */ - switch(out = *line) { - case '\0': - continue; /* this'll break out of the loop */ - 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; /* always zero terminate */ - return line; -} - -/* return 0 on everything-is-fine, and non-zero otherwise */ -static int parseconfig(const char *filename, - struct Configurable *config) -{ - int res; - FILE *file; - char filebuffer[512]; - bool usedarg; - char *home; - int rc = 0; - - if(!filename || !*filename) { - /* NULL or no file name attempts to load .curlrc from the homedir! */ - -#define CURLRC DOT_CHAR "curlrc" - -#ifndef __AMIGA__ - filename = CURLRC; /* sensible default */ - home = homedir(); /* portable homedir finder */ - if(home) { - if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) { - snprintf(filebuffer, sizeof(filebuffer), - "%s%s%s", home, DIR_CHAR, CURLRC); - -#ifdef WIN32 - /* Check if the file exists - if not, try CURLRC in the same - * directory as our executable - */ - file = fopen(filebuffer, "r"); - if(file != NULL) { - fclose(file); - filename = filebuffer; - } - else { - /* Get the filename of our executable. GetModuleFileName is - * already declared via inclusions done in setup header file. - * We assume that we are using the ASCII version here. - */ - int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer)); - if(n > 0 && n < (int)sizeof(filebuffer)) { - /* We got a valid filename - get the directory part */ - char *lastdirchar = strrchr(filebuffer, '\\'); - if(lastdirchar) { - size_t remaining; - *lastdirchar = 0; - /* If we have enough space, build the RC filename */ - remaining = sizeof(filebuffer) - strlen(filebuffer); - if(strlen(CURLRC) < remaining - 1) { - snprintf(lastdirchar, remaining, - "%s%s", DIR_CHAR, CURLRC); - /* Don't bother checking if it exists - we do - * that later - */ - filename = filebuffer; - } - } - } - } -#else /* WIN32 */ - filename = filebuffer; -#endif /* WIN32 */ - } - Curl_safefree(home); /* we've used it, now free it */ - } - -# else /* __AMIGA__ */ - /* On AmigaOS all the config files are into env: - */ - 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; - - /* line with # in the first non-blank column is a comment! */ - while(*line && ISSPACE(*line)) - line++; - - switch(*line) { - case '#': - case '/': - case '\r': - case '\n': - case '*': - case '\0': - Curl_safefree(aline); - continue; - } - - /* the option keywords starts here */ - option = line; - while(*line && !ISSPACE(*line) && !ISSEP(*line)) - line++; - /* ... and has ended here */ - - if(*line) - *line++=0; /* zero terminate, we have a local copy of the data */ - -#ifdef DEBUG_CONFIG - fprintf(stderr, "GOT: %s\n", option); -#endif - - /* pass spaces and separator(s) */ - while(*line && (ISSPACE(*line) || ISSEP(*line))) - line++; - - /* the parameter starts here (unless quoted) */ - if(*line == '\"') { - /* quoted parameter, do the quote dance */ - line++; - param=malloc(strlen(line)+1); /* parameter */ - if(!param) { - /* out of memory */ - Curl_safefree(aline); - rc = 1; - break; - } - alloced_param=TRUE; - (void)unslashquote(line, param); - } - else { - param=line; /* parameter starts here */ - while(*line && !ISSPACE(*line)) - line++; - *line=0; /* zero terminate */ - } - - if(param && !*param) { - /* do this so getparameter can check for required parameters. - Otherwise it always thinks there's a parameter. */ - if(alloced_param) - Curl_safefree(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) - /* we passed in a parameter that wasn't used! */ - res = PARAM_GOT_EXTRA_PARAMETER; - - if(res != PARAM_OK) { - /* the help request isn't really an error */ - 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) - Curl_safefree(param); - - Curl_safefree(aline); - } - if(file != stdin) - fclose(file); - } - else - rc = 1; /* couldn't open the file */ - return rc; -} - -static void go_sleep(long ms) -{ -#ifdef HAVE_POLL_FINE - /* portable subsecond "sleep" */ - poll((void *)0, 0, (int)ms); -#else - /* systems without poll() need other solutions */ - -#ifdef WIN32 - /* Windows offers a millisecond sleep */ - Sleep(ms); -#elif defined(MSDOS) - delay(ms); -#else - /* Other systems must use select() for this */ - struct timeval timeout; - - timeout.tv_sec = ms/1000; - ms = ms%1000; - timeout.tv_usec = ms * 1000; - - select(0, NULL, NULL, NULL, &timeout); -#endif - -#endif -} - -#define RETRY_SLEEP_DEFAULT 1000L /* ms */ -#define RETRY_SLEEP_MAX 600000L /* ms == 10 minutes */ - -static bool -output_expected(const char* url, const char* uploadfile) -{ - if(!uploadfile) - return TRUE; /* download */ - if(checkprefix("http://", url) || checkprefix("https://", url)) - return TRUE; /* HTTP(S) upload */ - - return FALSE; /* non-HTTP upload, probably no output should be expected */ -} - -static bool stdin_upload(const char *uploadfile) -{ - return (curlx_strequal(uploadfile, "-") || - curlx_strequal(uploadfile, ".")) ? TRUE : FALSE; -} - -/* Adds the file name to the URL if it doesn't already have one. - * url will be freed before return if the returned pointer is different - */ -static char *add_file_name_to_url(CURL *curl, char *url, const char *filename) -{ - /* If no file name part is given in the URL, we add this file name */ - char *ptr=strstr(url, "://"); - if(ptr) - ptr+=3; - else - ptr=url; - ptr = strrchr(ptr, '/'); - if(!ptr || !strlen(++ptr)) { - /* The URL has no file name part, add the local file name. In order - to be able to do so, we have to create a new URL in another - buffer.*/ - - /* We only want the part of the local path that is on the right - side of the rightmost slash and backslash. */ - 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; - - /* URL encode the file name */ - encfile = curl_easy_escape(curl, filep, 0 /* use strlen */); - if(encfile) { - char *urlbuffer = malloc(strlen(url) + strlen(encfile) + 3); - if(!urlbuffer) { - Curl_safefree(url); - return NULL; - } - if(ptr) - /* there is a trailing slash on the URL */ - sprintf(urlbuffer, "%s%s", url, encfile); - else - /* there is no trailing slash on the URL */ - sprintf(urlbuffer, "%s/%s", url, encfile); - - curl_free(encfile); - - Curl_safefree(url); - url = urlbuffer; /* use our new URL instead! */ - } - } - return url; -} - -/* Extracts the name portion of the URL. - * Returns a heap-allocated string, or NULL if no name part - */ -static char *get_url_file_name(const char *url) -{ - char *fn = NULL; - - /* Find and get the remote file name */ - const char * pc =strstr(url, "://"); - if(pc) - pc+=3; - else - pc=url; - pc = strrchr(pc, '/'); - - if(pc) { - /* duplicate the string beyond the slash */ - pc++; - fn = *pc ? strdup(pc): NULL; - } - return fn; -} - -#ifdef CURLDEBUG -static void memory_tracking_init(void) -{ - char *env; - /* if CURL_MEMDEBUG is set, this starts memory tracking message logging */ - env = curlx_getenv("CURL_MEMDEBUG"); - if(env) { - /* use the value as file name */ - char fname[CURL_MT_LOGFNAME_BUFSIZE]; - if(strlen(env) >= CURL_MT_LOGFNAME_BUFSIZE) - env[CURL_MT_LOGFNAME_BUFSIZE-1] = '\0'; - strcpy(fname, env); - curl_free(env); - curl_memdebug(fname); - /* this weird stuff here is to make curl_free() get called - before curl_memdebug() as otherwise memory tracking will - log a free() without an alloc! */ - } - /* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */ - 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); - } -} -#else -# define memory_tracking_init() Curl_nop_stmt -#endif - -static int -operate(struct Configurable *config, int argc, argv_item_t argv[]) -{ - char errorbuffer[CURL_ERROR_SIZE]; - struct ProgressData progressbar; - struct getout *urlnode; - - struct OutStruct heads; - - CURL *curl = NULL; - char *httpgetfields = NULL; - - bool stillflags; - int res = 0; - int i; - - errorbuffer[0] = '\0'; - /* default headers output stream is stdout */ - memset(&heads, 0, sizeof(struct OutStruct)); - heads.stream = stdout; - heads.config = config; - - memory_tracking_init(); - - /* - ** Initialize curl library - do not call any libcurl functions before - ** this point. Note that the memory_tracking_init() magic above is an - ** exception, but then that's not part of the official public API. - */ - if(main_init() != CURLE_OK) { - helpf(config->errors, "error initializing curl library\n"); - return CURLE_FAILED_INIT; - } - - /* Get libcurl info right away */ - if(get_libcurl_info() != CURLE_OK) { - helpf(config->errors, "error retrieving curl library information\n"); - main_free(); - return CURLE_FAILED_INIT; - } - - /* Get a curl handle to use for all forthcoming curl transfers */ - curl = curl_easy_init(); - if(!curl) { - helpf(config->errors, "error initializing curl easy handle\n"); - main_free(); - return CURLE_FAILED_INIT; - } - config->easy = curl; - - /* - ** Beyond this point no return'ing from this function allowed. - ** Jump to label 'quit_curl' in order to abandon this function - ** from outside of nested loops further down below. - */ - - /* setup proper locale from environment */ -#ifdef HAVE_SETLOCALE - setlocale(LC_ALL, ""); -#endif - - /* inits */ - config->postfieldsize = -1; - config->showerror = TRUE; - config->use_httpget = FALSE; - config->create_dirs = FALSE; - config->maxredirs = DEFAULT_MAXREDIRS; - config->proto = CURLPROTO_ALL; /* FIXME: better to read from library */ - config->proto_present = FALSE; - config->proto_redir = - CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */ - config->proto_redir_present = FALSE; - - if((argc > 1) && - (!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) && - strchr(argv[1], 'q')) { - /* - * The first flag, that is not a verbose name, but a shortname - * and it includes the 'q' flag! - */ - ; - } - else { - parseconfig(NULL, config); /* ignore possible failure */ - } - - if((argc < 2) && !config->url_list) { - helpf(config->errors, NULL); - res = CURLE_FAILED_INIT; - goto quit_curl; - } - - /* Parse options */ - for(i = 1, stillflags = TRUE; 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])) - /* this indicates the end of the flags and thus enables the - following (URL) argument to start with -. */ - stillflags = FALSE; - else { - nextarg = (i < (argc-1)) ? argv[i+1] : NULL; - - res = getparameter(flag, nextarg, &passarg, config); - if(res) { - 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; - } - res = retval; - goto quit_curl; - } - - if(passarg) /* we're supposed to skip this */ - i++; - } - } - else { - bool used; - /* just add the URL please */ - res = getparameter((char *)"--url", argv[i], &used, config); - if(res) - goto quit_curl; - } - } - - if((!config->url_list || !config->url_list->url) && !config->list_engines) { - helpf(config->errors, "no URL specified!\n"); - res = CURLE_FAILED_INIT; - goto quit_curl; - } - - if(!config->useragent) - config->useragent = my_useragent(); - if(!config->useragent) { - helpf(config->errors, "out of memory\n"); - res = CURLE_OUT_OF_MEMORY; - goto quit_curl; - } - - /* On WIN32 we can't set the path to curl-ca-bundle.crt - * at compile time. So we look here for the file in two ways: - * 1: look at the environment variable CURL_CA_BUNDLE for a path - * 2: if #1 isn't found, use the windows API function SearchPath() - * to find it along the app's path (includes app's dir and CWD) - * - * We support the environment variable thing for non-Windows platforms - * too. Just for the sake of it. - */ - if(!config->cacert && - !config->capath && - !config->insecure_ok) { - char *env; - env = curlx_getenv("CURL_CA_BUNDLE"); - if(env) { - config->cacert = strdup(env); - if(!config->cacert) { - curl_free(env); - helpf(config->errors, "out of memory\n"); - res = CURLE_OUT_OF_MEMORY; - goto quit_curl; - } - } - else { - env = curlx_getenv("SSL_CERT_DIR"); - if(env) { - config->capath = strdup(env); - if(!config->capath) { - curl_free(env); - helpf(config->errors, "out of memory\n"); - res = CURLE_OUT_OF_MEMORY; - goto quit_curl; - } - } - else { - env = curlx_getenv("SSL_CERT_FILE"); - if(env) { - config->cacert = strdup(env); - if(!config->cacert) { - curl_free(env); - helpf(config->errors, "out of memory\n"); - res = CURLE_OUT_OF_MEMORY; - goto quit_curl; - } - } - } - } - - if(env) - curl_free(env); -#ifdef WIN32 - else { - res = FindWin32CACert(config, "curl-ca-bundle.crt"); - if(res) - goto quit_curl; - } -#endif - } - - if(config->postfields) { - if(config->use_httpget) { - /* Use the postfields data for a http get */ - httpgetfields = strdup(config->postfields); - Curl_safefree(config->postfields); - if(SetHTTPrequest(config, - (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET), - &config->httpreq)) { - res = PARAM_BAD_USE; - goto quit_curl; - } - } - else { - if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq)) { - res = PARAM_BAD_USE; - goto quit_curl; - } - } - } - - /* This is the first entry added to easysrc and it initializes the slist */ - easysrc = curl_slist_append(easysrc, "CURL *hnd = curl_easy_init();"); - if(!easysrc) { - helpf(config->errors, "out of memory\n"); - 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; - } - - /* Single header file for all URLs */ - if(config->headerfile) { - /* open file for output: */ - if(!curlx_strequal(config->headerfile, "-")) { - FILE *newfile = fopen(config->headerfile, "wb"); - if(!newfile) { - warnf(config, "Failed to open %s\n", config->headerfile); - res = CURLE_WRITE_ERROR; - goto quit_curl; - } - else { - heads.filename = config->headerfile; - heads.s_isreg = TRUE; - heads.fopened = TRUE; - heads.stream = newfile; - } - } - } - - /* - ** Nested loops start here. - */ - - /* loop through the list of given URLs */ - - for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) { - - int up; /* upload file counter within a single upload glob */ - char *infiles; /* might be a glob pattern */ - char *outfiles; - int infilenum; - URLGlob *inglob; - - outfiles = NULL; - infilenum = 0; - inglob = NULL; - - /* urlnode->url is the full URL (it might be NULL) */ - - if(!urlnode->url) { - /* This node has no URL. Free node data without destroying the - node itself nor modifying next pointer and continue to next */ - Curl_safefree(urlnode->outfile); - Curl_safefree(urlnode->infile); - urlnode->flags = 0; - continue; /* next URL please */ - } - - /* save outfile pattern before expansion */ - if(urlnode->outfile) { - outfiles = strdup(urlnode->outfile); - if(!outfiles) { - helpf(config->errors, "out of memory\n"); - res = CURLE_OUT_OF_MEMORY; - break; - } - } - - infiles = urlnode->infile; - - if(!config->globoff && infiles) { - /* Unless explicitly shut off */ - res = glob_url(&inglob, infiles, &infilenum, - config->showerror?config->errors:NULL); - if(res) { - Curl_safefree(outfiles); - break; - } - } - - /* Here's the loop for uploading multiple files within the same - single globbed string. If no upload, we enter the loop once anyway. */ - for(up = 0 ;; up++) { - - char *uploadfile; /* a single file, never a glob */ - int separator; - URLGlob *urls; - int urlnum; - - uploadfile = NULL; - separator = 0; - urls = NULL; - urlnum = 0; - - if(!up && !infiles) - Curl_nop_stmt; - else { - if(inglob) - uploadfile = glob_next_url(inglob); - else if(!up) - uploadfile = strdup(infiles); - else - uploadfile = NULL; - if(!uploadfile) - break; - } - - if(!config->globoff) { - /* Unless explicitly shut off, we expand '{...}' and '[...]' - expressions and return total number of URLs in pattern set */ - res = glob_url(&urls, urlnode->url, &urlnum, - config->showerror?config->errors:NULL); - if(res) { - Curl_safefree(uploadfile); - break; - } - } - else - urlnum = 1; /* without globbing, this is a single URL */ - - /* if multiple files extracted to stdout, insert separators! */ - separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1); - - /* Here's looping around each globbed URL */ - for(i = 0 ;; i++) { - - int infd; - bool infdopen; - char *outfile; - struct OutStruct outs; - struct InStruct input; - struct timeval retrystart; - curl_off_t uploadfilesize; - long retry_numretries; - long retry_sleep_default; - long retry_sleep; - char *this_url; - - outfile = NULL; - infdopen = FALSE; - infd = STDIN_FILENO; - uploadfilesize = -1; /* -1 means unknown */ - - /* default output stream is stdout */ - memset(&outs, 0, sizeof(struct OutStruct)); - outs.stream = stdout; - outs.config = config; - - if(urls) - this_url = glob_next_url(urls); - else if(!i) { - this_url = strdup(urlnode->url); - } - else - this_url = NULL; - if(!this_url) - break; - - if(outfiles) { - outfile = strdup(outfiles); - if(!outfile) { - res = CURLE_OUT_OF_MEMORY; - goto show_error; - } - } - - if((urlnode->flags&GETOUT_USEREMOTE) || - (outfile && !curlx_strequal("-", outfile)) ) { - - /* - * We have specified a file name to store the result in, or we have - * decided we want to use the remote file name. - */ - - if(!outfile) { - /* extract the file name from the URL */ - outfile = get_url_file_name(this_url); - if((!outfile || !*outfile) && !config->content_disposition) { - helpf(config->errors, "Remote file name has no length!\n"); - res = CURLE_WRITE_ERROR; - goto quit_urls; - } -#if defined(MSDOS) || defined(WIN32) - /* For DOS and WIN32, we do some major replacing of - bad characters in the file name before using it */ - outfile = sanitize_dos_name(outfile); - if(!outfile) { - res = CURLE_OUT_OF_MEMORY; - goto show_error; - } -#endif /* MSDOS || WIN32 */ - } - else if(urls) { - /* fill '#1' ... '#9' terms from URL pattern */ - char *storefile = outfile; - outfile = glob_match_url(storefile, urls); - Curl_safefree(storefile); - if(!outfile) { - /* bad globbing */ - warnf(config, "bad output glob!\n"); - res = CURLE_FAILED_INIT; - goto quit_urls; - } - } - - /* Create the directory hierarchy, if not pre-existent to a multiple - file output call */ - - if(config->create_dirs) { - res = create_dir_hierarchy(outfile, config->errors); - /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */ - if(res == CURLE_WRITE_ERROR) - goto quit_urls; - if(res) { - goto show_error; - } - } - - if((urlnode->flags & GETOUT_USEREMOTE) - && config->content_disposition) { - /* Our header callback sets the filename */ - DEBUGASSERT(!outs.filename); - } - else { - if(config->resume_from_current) { - /* We're told to continue from where we are now. Get the size - of the file as it is now and open it for append instead */ - struct_stat fileinfo; - /* VMS -- Danger, the filesize is only valid for stream files */ - if(0 == stat(outfile, &fileinfo)) - /* set offset to current file size: */ - config->resume_from = fileinfo.st_size; - else - /* let offset be 0 */ - config->resume_from = 0; - } - - if(config->resume_from) { - /* open file for output: */ - FILE *file = fopen(outfile, config->resume_from?"ab":"wb"); - if(!file) { - helpf(config->errors, "Can't open '%s'!\n", outfile); - res = CURLE_WRITE_ERROR; - goto quit_urls; - } - outs.fopened = TRUE; - outs.stream = file; - outs.init = config->resume_from; - } - else { - outs.stream = NULL; /* open when needed */ - } - outs.filename = outfile; - outs.s_isreg = TRUE; - } - } - - if(uploadfile && !stdin_upload(uploadfile)) { - /* - * We have specified a file to upload and it isn't "-". - */ - struct_stat fileinfo; - - this_url = add_file_name_to_url(curl, this_url, uploadfile); - if(!this_url) { - res = CURLE_OUT_OF_MEMORY; - goto show_error; - } - /* VMS Note: - * - * Reading binary from files can be a problem... Only FIXED, VAR - * etc WITHOUT implied CC will work Others need a \n appended to a - * line - * - * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a - * fixed file with implied CC needs to have a byte added for every - * record processed, this can by derived from Filesize & recordsize - * for VARiable record files the records need to be counted! for - * every record add 1 for linefeed and subtract 2 for the record - * header for VARIABLE header files only the bare record data needs - * to be considered with one appended if implied CC - */ - - 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); - infd = STDIN_FILENO; - } - res = CURLE_READ_ERROR; - goto quit_urls; - } - infdopen = TRUE; - - /* we ignore file size for char/block devices, sockets, etc. */ - if(S_ISREG(fileinfo.st_mode)) - uploadfilesize = fileinfo.st_size; - - } - else if(uploadfile && stdin_upload(uploadfile)) { - /* count to see if there are more than one auth bit set - in the authtype field */ - int authbits = 0; - int bitcheck = 0; - while(bitcheck < 32) { - if(config->authtype & (1 << bitcheck++)) { - authbits++; - if(authbits > 1) { - /* more than one, we're done! */ - break; - } - } - } - - /* - * If the user has also selected --anyauth or --proxy-anyauth - * we should warn him/her. - */ - 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"); - } - - DEBUGASSERT(infdopen == FALSE); - DEBUGASSERT(infd == STDIN_FILENO); - - set_binmode(stdin); - 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; /* -1 will then force get-it-yourself */ - - if(output_expected(this_url, uploadfile) - && outs.stream && isatty(fileno(outs.stream))) - /* we send the output to a tty, therefore we switch off the progress - meter */ - config->noprogress = config->isatty = TRUE; - - if(urlnum > 1 && !(config->mute)) { - fprintf(config->errors, "\n[%d/%d]: %s --> %s\n", - i+1, urlnum, this_url, outfile ? outfile : "<stdout>"); - if(separator) - printf("%s%s\n", CURLseparator, this_url); - } - if(httpgetfields) { - char *urlbuffer; - /* Find out whether the url contains a file name */ - const char *pc = strstr(this_url, "://"); - char sep = '?'; - if(pc) - pc += 3; - else - pc = this_url; - - pc = strrchr(pc, '/'); /* check for a slash */ - - if(pc) { - /* there is a slash present in the URL */ - - if(strchr(pc, '?')) - /* Ouch, there's already a question mark in the URL string, we - then append the data with an ampersand separator instead! */ - sep='&'; - } - /* - * Then append ? followed by the get fields to the url. - */ - urlbuffer = malloc(strlen(this_url) + strlen(httpgetfields) + 3); - if(!urlbuffer) { - res = CURLE_OUT_OF_MEMORY; - goto show_error; - } - if(pc) - sprintf(urlbuffer, "%s%c%s", this_url, sep, httpgetfields); - else - /* Append / before the ? to create a well-formed url - if the url contains a hostname only - */ - sprintf(urlbuffer, "%s/?%s", this_url, httpgetfields); - - Curl_safefree(this_url); /* free previous URL */ - this_url = urlbuffer; /* use our new URL instead! */ - } - - if(!config->errors) - config->errors = stderr; - - if((!outfile || !strcmp(outfile, "-")) && !config->use_ascii) { - /* We get the output to stdout and we have not got the ASCII/text - flag, then set stdout to be binary */ - set_binmode(stdout); - } - - if(config->tcp_nodelay) - my_setopt(curl, CURLOPT_TCP_NODELAY, 1); - - /* where to store */ - my_setopt(curl, CURLOPT_WRITEDATA, &outs); - /* what call to write */ - my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); - - /* for uploads */ - input.fd = infd; - input.config = config; - my_setopt(curl, CURLOPT_READDATA, &input); - /* what call to read */ - if((outfile && !curlx_strequal("-", outfile)) || - !checkprefix("telnet:", this_url)) - my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb); - - /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what - CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */ - my_setopt(curl, CURLOPT_SEEKDATA, &input); - my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); - - if(config->recvpersecond) - /* tell libcurl to use a smaller sized buffer as it allows us to - make better sleeps! 7.9.9 stuff! */ - my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond); - - /* size of uploaded file: */ - if(uploadfilesize != -1) - my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); - my_setopt_str(curl, CURLOPT_URL, this_url); /* what to fetch */ - 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); - -#if !defined(CURL_DISABLE_PROXY) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - my_setopt_str(curl, CURLOPT_PROXY, config->proxy); - my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); - - /* new in libcurl 7.3 */ - my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); - - /* new in libcurl 7.5 */ - if(config->proxy) - my_setopt(curl, CURLOPT_PROXYTYPE, config->proxyver); - - /* new in libcurl 7.10 */ - if(config->socksproxy) { - my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy); - my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver); - } - - /* new in libcurl 7.10.6 */ - 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); - - /* new in libcurl 7.19.4 */ - my_setopt(curl, CURLOPT_NOPROXY, config->noproxy); - } -#endif - - 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 || config->netrc_file) - my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); - else - my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); - - if(config->netrc_file) - my_setopt(curl, CURLOPT_NETRC_FILE, config->netrc_file); - - my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); - my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd); - my_setopt_str(curl, CURLOPT_RANGE, config->range); - my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); - my_setopt(curl, CURLOPT_TIMEOUT, config->timeout); - - if(built_in_protos & CURLPROTO_HTTP) { - - my_setopt(curl, CURLOPT_FOLLOWLOCATION, - config->followlocation); - my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, - config->unrestricted_auth); - - 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(curl, CURLOPT_HTTPHEADER, config->headers); - - /* new in libcurl 7.5 */ - my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); - - /* new in libcurl 7.9.1 */ - if(config->httpversion) - my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion); - - /* new in libcurl 7.10.6 (default is Basic) */ - if(config->authtype) - my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype); - - /* curl 7.19.1 (the 301 version existed in 7.18.2) */ - my_setopt(curl, CURLOPT_POSTREDIR, config->post301 | - (config->post302 ? CURL_REDIR_POST_302 : FALSE)); - - /* new in libcurl 7.21.6 */ - if(config->encoding) - my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, ""); - - /* new in libcurl 7.21.6 */ - if(config->tr_encoding) - my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1); - - } /* (built_in_protos & CURLPROTO_HTTP) */ - - 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(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); - - if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { - - /* SSH and SSL private key uses same command-line option */ - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); - - /* new in libcurl 7.17.1: SSH host key md5 checking allows us - to fail if we are not talking to who we think we should */ - my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, - config->hostpubmd5); - } - - if(config->cacert) - my_setopt_str(curl, CURLOPT_CAINFO, config->cacert); - if(config->capath) - my_setopt_str(curl, CURLOPT_CAPATH, config->capath); - if(config->crlfile) - my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile); - - if(curlinfo->features & CURL_VERSION_SSL) { - if(config->insecure_ok) { - my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); - } - else { - my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - /* libcurl default is strict verifyhost -> 2L */ - /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */ - } - } - - if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) { - if(!config->insecure_ok) { - char *home; - char *file; - res = CURLE_OUT_OF_MEMORY; - home = homedir(); - if(home) { - file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR); - if(file) { - /* new in curl 7.19.6 */ - res = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file); - curl_free(file); - if(res == CURLE_UNKNOWN_OPTION) - /* libssh2 version older than 1.1.1 */ - res = CURLE_OK; - } - free(home); - } - if(res) - goto show_error; - } - } - - if(config->no_body || config->remote_time) { - /* no body or use remote time */ - my_setopt(curl, CURLOPT_FILETIME, TRUE); - } - - 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); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - if(config->cookie) - my_setopt_str(curl, CURLOPT_COOKIE, config->cookie); - - if(config->cookiefile) - my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile); - - /* new in libcurl 7.9 */ - if(config->cookiejar) - my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar); - - /* new in libcurl 7.9.7 */ - my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession); - } -#endif - - 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); - - /* three new ones in libcurl 7.3: */ - 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) { - /* we want the alternative style, then we have to implement it - ourselves! */ - my_setopt(curl, CURLOPT_PROGRESSFUNCTION, tool_progress_cb); - my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar); - } - - /* new in libcurl 7.6.2: */ - my_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); - - /* new in libcurl 7.7: */ - 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); - - /* new in libcurl 7.9.2: */ - if(config->disable_epsv) - /* disable it */ - my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE); - - /* new in libcurl 7.10.5 */ - if(config->disable_eprt) - /* disable it */ - my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE); - - if(config->tracetype != TRACE_NONE) { - my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb); - my_setopt(curl, CURLOPT_DEBUGDATA, config); - my_setopt(curl, CURLOPT_VERBOSE, TRUE); - } - - /* new in curl 7.9.3 */ - if(config->engine) { - res = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); - if(res) - goto show_error; - my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1); - } - - /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */ - my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, - config->ftp_create_dirs); - - /* new in curl 7.10.8 */ - 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); - - /* new in curl 7.15.5 */ - if(config->ftp_ssl_reqd) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); - - /* new in curl 7.11.0 */ - else if(config->ftp_ssl) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); - - /* new in curl 7.16.0 */ - else if(config->ftp_ssl_control) - my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL); - - /* new in curl 7.16.1 */ - if(config->ftp_ssl_ccc) - my_setopt(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode); - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - { - /* TODO: Make this a run-time check instead of compile-time one. */ - - /* new in curl 7.19.4 */ - if(config->socks5_gssapi_service) - my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE, - config->socks5_gssapi_service); - - /* new in curl 7.19.4 */ - if(config->socks5_gssapi_nec) - my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, - config->socks5_gssapi_nec); - } -#endif - /* curl 7.13.0 */ - my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); - - my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); - - /* curl 7.14.2 */ - my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip); - - /* curl 7.15.1 */ - my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); - - /* curl 7.15.2 */ - if(config->localport) { - my_setopt(curl, CURLOPT_LOCALPORT, config->localport); - my_setopt_str(curl, CURLOPT_LOCALPORTRANGE, - config->localportrange); - } - - /* curl 7.15.5 */ - my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, - config->ftp_alternative_to_user); - - /* curl 7.16.0 */ - if(config->disable_sessionid) - my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, - !config->disable_sessionid); - - /* curl 7.16.2 */ - if(config->raw) { - my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE); - my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE); - } - - /* curl 7.17.1 */ - if(!config->nokeepalive) { - my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, tool_sockopt_cb); - my_setopt(curl, CURLOPT_SOCKOPTDATA, config); - } - - /* curl 7.20.0 */ - 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); - - /* curl 7.20.x */ - 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, tool_header_cb); - my_setopt(curl, CURLOPT_HEADERDATA, &outs); - } - else { - /* if HEADERFUNCTION was set to something in the previous loop, it - is important that we set it (back) to NULL now */ - my_setopt(curl, CURLOPT_HEADERFUNCTION, NULL); - my_setopt(curl, CURLOPT_HEADERDATA, config->headerfile?&heads:NULL); - } - - if(config->resolve) - /* new in 7.21.3 */ - my_setopt(curl, CURLOPT_RESOLVE, config->resolve); - - /* new in 7.21.4 */ - if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { - if(config->tls_username) - my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, - config->tls_username); - if(config->tls_password) - my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD, - config->tls_password); - if(config->tls_authtype) - my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE, - config->tls_authtype); - } - - /* new in 7.22.0 */ - if(config->gssapi_delegation) - my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION, - config->gssapi_delegation); - - /* initialize retry vars for loop below */ - retry_sleep_default = (config->retry_delay) ? - config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ - - retry_numretries = config->req_retry; - retry_sleep = retry_sleep_default; /* ms */ - retrystart = cutil_tvnow(); - - for(;;) { - res = curl_easy_perform(curl); - if(!curl_slist_append(easysrc, "ret = curl_easy_perform(hnd);")) { - res = CURLE_OUT_OF_MEMORY; - goto show_error; - } - - if(config->content_disposition && outs.stream && !config->mute && - outs.filename) - printf("curl: Saved to filename '%s'\n", outs.filename); - - /* if retry-max-time is non-zero, make sure we haven't exceeded the - time */ - if(retry_numretries && - (!config->retry_maxtime || - (cutil_tvdiff(cutil_tvnow(), retrystart)< - config->retry_maxtime*1000L)) ) { - enum { - RETRY_NO, - RETRY_TIMEOUT, - RETRY_HTTP, - RETRY_FTP, - RETRY_LAST /* not used */ - } retry = RETRY_NO; - long response; - if(CURLE_OPERATION_TIMEDOUT == res) - /* retry timeout always */ - retry = RETRY_TIMEOUT; - else if((CURLE_OK == res) || - (config->failonerror && - (CURLE_HTTP_RETURNED_ERROR == res))) { - /* If it returned OK. _or_ failonerror was enabled and it - returned due to such an error, check for HTTP transient - errors to retry on. */ - char *effective_url = NULL; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url); - if(effective_url && - checkprefix("http", effective_url)) { - /* This was HTTP(S) */ - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - switch(response) { - case 500: /* Internal Server Error */ - case 502: /* Bad Gateway */ - case 503: /* Service Unavailable */ - case 504: /* Gateway Timeout */ - retry = RETRY_HTTP; - /* - * At this point, we have already written data to the output - * file (or terminal). If we write to a file, we must rewind - * or close/re-open the file so that the next attempt starts - * over from the beginning. - * - * TODO: similar action for the upload case. We might need - * to start over reading from a previous point if we have - * uploaded something when this was returned. - */ - break; - } - } - } /* if CURLE_OK */ - else if(res) { - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); - - if(response/100 == 4) - /* - * This is typically when the FTP server only allows a certain - * amount of users and we are not one of them. All 4xx codes - * are transient. - */ - 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/1000L, 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) { - /* We have written data to a output file, we truncate file - */ - if(!config->mute) - fprintf(config->errors, "Throwing away %" - CURL_FORMAT_CURL_OFF_T " bytes\n", - outs.bytes); - fflush(outs.stream); - /* truncate file at the position where we started appending */ -#ifdef HAVE_FTRUNCATE - if(ftruncate( fileno(outs.stream), outs.init)) { - /* when truncate fails, we can't just append as then we'll - create something strange, bail out */ - if(!config->mute) - fprintf(config->errors, - "failed to truncate, exiting\n"); - res = CURLE_WRITE_ERROR; - goto quit_urls; - } - /* now seek to the end of the file, the position where we - just truncated the file in a large file-safe way */ - fseek(outs.stream, 0, SEEK_END); -#else - /* ftruncate is not available, so just reposition the file - to the location we would have truncated it. This won't - work properly with large files on 32-bit systems, but - most of those will have ftruncate. */ - fseek(outs.stream, (long)outs.init, SEEK_SET); -#endif - outs.bytes = 0; /* clear for next round */ - } - continue; /* curl_easy_perform loop */ - } - } /* if retry_numretries */ - - /* In all ordinary cases, just break out of loop here */ - break; /* curl_easy_perform loop */ - - } - - if((config->progressmode == CURL_PROGRESS_BAR) && - progressbar.calls) - /* if the custom progress bar has been displayed, we output a - newline here */ - fputs("\n", progressbar.out); - - if(config->writeout) - ourWriteOut(curl, config->writeout); -#ifdef USE_ENVIRONMENT - if(config->writeenv) - ourWriteEnv(curl); -#endif - - /* - ** Code within this loop may jump directly here to label 'show_error' - ** in order to display an error message for CURLcode stored in 'res' - ** variable and exit loop once that necessary writing and cleanup - ** in label 'quit_urls' has been done. - */ - - show_error: - -#ifdef __VMS - if(is_vms_shell()) { - /* VMS DCL shell behavior */ - if(!config->showerror) - vms_show = VMSSTS_HIDE; - } - else -#endif - if(res && config->showerror) { - fprintf(config->errors, "curl: (%d) %s\n", res, (errorbuffer[0]) ? - errorbuffer : curl_easy_strerror((CURLcode)res)); - if(res == CURLE_SSL_CACERT) - fprintf(config->errors, "%s%s", - CURL_CA_CERT_ERRORMSG1, CURL_CA_CERT_ERRORMSG2); - } - - /* Fall through comment to 'quit_urls' label */ - - /* - ** Upon error condition and always that a message has already been - ** displayed, code within this loop may jump directly here to label - ** 'quit_urls' otherwise it should jump to 'show_error' label above. - ** - ** When 'res' variable is _not_ CURLE_OK loop will exit once that - ** all code following 'quit_urls' has been executed. Otherwise it - ** will loop to the beginning from where it may exit if there are - ** no more urls left. - */ - - quit_urls: - - /* Set file extended attributes */ - if(!res && config->xattr && outs.fopened && outs.stream) { - int rc = fwrite_xattr(curl, fileno(outs.stream)); - if(rc) - warnf(config, "Error setting extended attributes: %s\n", - strerror(errno)); - } - - /* Close the file */ - if(outs.fopened && outs.stream) { - int rc = fclose(outs.stream); - if(!res && rc) { - /* something went wrong in the writing process */ - res = CURLE_WRITE_ERROR; - fprintf(config->errors, "(%d) Failed writing body\n", res); - } - } - else if(!outs.s_isreg && outs.stream) { - /* Dump standard stream buffered data */ - int rc = fflush(outs.stream); - if(!res && rc) { - /* something went wrong in the writing process */ - res = CURLE_WRITE_ERROR; - fprintf(config->errors, "(%d) Failed writing body\n", res); - } - } - -#ifdef __AMIGA__ - if(!res && outs.s_isreg && outs.filename) { - /* Set the url (up to 80 chars) as comment for the file */ - if(strlen(url) > 78) - url[79] = '\0'; - SetComment(outs.filename, url); - } -#endif - -#ifdef HAVE_UTIME - /* File time can only be set _after_ the file has been closed */ - if(!res && config->remote_time && outs.s_isreg && outs.filename) { - /* Ask libcurl if we got a remote file time */ - long filetime = -1; - 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, ×); /* set the time we got */ - } - } -#endif - /* No more business with this output struct */ - if(outs.alloc_filename) - Curl_safefree(outs.filename); - memset(&outs, 0, sizeof(struct OutStruct)); - - /* Free loop-local allocated memory and close loop-local opened fd */ - - Curl_safefree(outfile); - Curl_safefree(this_url); - - if(infdopen) { - close(infd); - infdopen = FALSE; - infd = STDIN_FILENO; - } - - /* upon error exit loop */ - if(res) - break; - - } /* loop to the next URL */ - - /* Free loop-local allocated memory */ - - Curl_safefree(uploadfile); - - if(urls) { - /* Free list of remaining URLs */ - glob_cleanup(urls); - urls = NULL; - } - - /* upon error exit loop */ - if(res) - break; - - } /* loop to the next globbed upload file */ - - /* Free loop-local allocated memory */ - - Curl_safefree(outfiles); - - if(inglob) { - /* Free list of globbed upload files */ - glob_cleanup(inglob); - inglob = NULL; - } - - /* Free this URL node data without destroying the - the node itself nor modifying next pointer. */ - Curl_safefree(urlnode->url); - Curl_safefree(urlnode->outfile); - Curl_safefree(urlnode->infile); - urlnode->flags = 0; - - /* TODO: Should CURLE_SSL_CACERT be included as critical error ? */ - - /* - ** Bail out upon critical errors - */ - switch(res) { - case CURLE_FAILED_INIT: - case CURLE_OUT_OF_MEMORY: - case CURLE_FUNCTION_NOT_FOUND: - case CURLE_BAD_FUNCTION_ARGUMENT: - goto quit_curl; - default: - /* Merrily loop to next URL */ - break; - } - - } /* for-loop through all URLs */ - - /* - ** Nested loops end here. - */ - - quit_curl: - - /* Free function-local referenced allocated memory */ - Curl_safefree(httpgetfields); - - /* Free list of given URLs */ - clean_getout(config); - - /* Cleanup the curl handle now that our - progressbar struct is still in scope */ - if(curl) { - curl_easy_cleanup(curl); - config->easy = curl = NULL; - } - if(easysrc) - curl_slist_append(easysrc, "curl_easy_cleanup(hnd);"); - - /* Close function-local opened file descriptors */ - - if(heads.fopened && heads.stream) - fclose(heads.stream); - if(heads.alloc_filename) - Curl_safefree(heads.filename); - - if(config->trace_fopened && config->trace_stream) - fclose(config->trace_stream); - - /* Dump the libcurl code if previously enabled. - NOTE: that this function relies on config->errors amongst other things - so not everything can be closed and cleaned before this is called */ - dumpeasysrc(config); - - if(config->errors_fopened && config->errors) - fclose(config->errors); - - main_free(); /* cleanup */ - - return res; -} - -/* Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are - open before starting to run. Otherwise, the first three network - sockets opened by curl could be used for input sources, downloaded data - or error logs as they will effectively be stdin, stdout and/or stderr. -*/ -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; /* Out of handles. This isn't really a big problem now, but - will be when we try to create a socket later. */ - 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; /* default errors to 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 -} - -/* - * Reads a line from the given file, ensuring is NUL terminated. - * The pointer must be freed by the caller. - * NULL is returned on an out of memory condition. - */ -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) { - Curl_safefree(retval); - return NULL; - } - retval = ptr; - strcat(retval, buf); - } - } - while(NULL == (nl = strchr(retval, '\n'))); - - if(NULL != nl) - *nl = '\0'; - - return retval; -} - |