| /*************************************************************************** |
| * _ _ ____ _ |
| * 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> |
| |
| #if defined(MSDOS) || defined(WIN32) |
| # if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) |
| # include <libgen.h> |
| # endif |
| #endif |
| |
| #ifdef NETWARE |
| # ifdef __NOVELL_LIBC__ |
| # include <screen.h> |
| # else |
| # include <nwconio.h> |
| # define mkdir mkdir_510 |
| # 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 |
| |
| #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) |
| # include <iconv.h> |
| /* set default codesets for iconv */ |
| # ifndef CURL_ICONV_CODESET_OF_NETWORK |
| # define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1" |
| # endif |
| #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ |
| |
| #ifdef MSDOS |
| # include <dos.h> |
| #endif |
| |
| #if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) |
| # include <io.h> |
| # include <sys/types.h> |
| # include <sys/stat.h> |
| #endif |
| |
| #ifdef WIN32 |
| # include <direct.h> |
| #endif |
| |
| /* |
| ** src subdirectory headers |
| */ |
| |
| #include "urlglob.h" |
| #include "writeout.h" |
| #include "getpass.h" |
| #include "homedir.h" |
| #include "curlutil.h" |
| #include "os-specific.h" |
| #include "version.h" |
| #include "xattr.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" |
| |
| /* The last #include file should be: */ |
| #ifdef CURLDEBUG |
| #ifndef CURLTOOLDEBUG |
| #define MEMDEBUG_NODEFINES |
| #endif |
| /* This is low-level hard-hacking memory leak tracking and similar. Using |
| the library level code from this client-side is ugly, but we do this |
| anyway for convenience. */ |
| #include "memdebug.h" |
| #endif |
| |
| #ifdef __VMS |
| static int vms_show = 0; |
| #endif |
| |
| #if defined(NETWARE) |
| #define PRINT_LINES_PAUSE 23 |
| #endif |
| |
| #if defined(__SYMBIAN32__) |
| #define PRINT_LINES_PAUSE 16 |
| #define pressanykey() getchar() |
| #endif |
| |
| #define DEFAULT_MAXREDIRS 50L |
| |
| #if defined(O_BINARY) && defined(HAVE_SETMODE) |
| #ifdef __HIGHC__ |
| #define SET_BINMODE(file) _setmode(file,O_BINARY) |
| #else |
| #define SET_BINMODE(file) setmode(fileno(file),O_BINARY) |
| #endif |
| #else |
| #define SET_BINMODE(file) ((void)0) |
| #endif |
| |
| #ifndef O_BINARY |
| /* 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 |
| |
| #if defined(MSDOS) || defined(WIN32) |
| |
| static const char *msdosify(const char *); |
| static char *rename_if_dos_device_name(char *); |
| static char *sanitize_dos_name(char *); |
| |
| #ifndef S_ISCHR |
| # ifdef S_IFCHR |
| # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) |
| # else |
| # define S_ISCHR(m) (0) /* cannot tell if file is a device */ |
| # endif |
| #endif |
| |
| #ifdef WIN32 |
| # define _use_lfn(f) (1) /* long file names always available */ |
| #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ |
| # define _use_lfn(f) (0) /* long file names never available */ |
| #endif |
| |
| #endif /* MSDOS || WIN32 */ |
| |
| #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_PROGRESS_STATS 0 /* default progress display */ |
| #define CURL_PROGRESS_BAR 1 |
| |
| typedef enum { |
| HTTPREQ_UNSPEC, |
| HTTPREQ_GET, |
| HTTPREQ_HEAD, |
| HTTPREQ_POST, |
| HTTPREQ_SIMPLEPOST, |
| HTTPREQ_CUSTOM, |
| HTTPREQ_LAST |
| } HttpReq; |
| |
| /* |
| * Large file support (>2Gb) using WIN32 functions. |
| */ |
| |
| #ifdef USE_WIN32_LARGE_FILES |
| # define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence) |
| # define fstat(fdes,stp) _fstati64(fdes, stp) |
| # define stat(fname,stp) _stati64(fname, stp) |
| # define struct_stat struct _stati64 |
| # define LSEEK_ERROR (__int64)-1 |
| #endif |
| |
| /* |
| * Small file support (<2Gb) using WIN32 functions. |
| */ |
| |
| #ifdef USE_WIN32_SMALL_FILES |
| # define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence) |
| # define fstat(fdes,stp) _fstat(fdes, stp) |
| # define stat(fname,stp) _stat(fname, stp) |
| # define struct_stat struct _stat |
| # define LSEEK_ERROR (long)-1 |
| #endif |
| |
| #ifndef struct_stat |
| # define struct_stat struct stat |
| #endif |
| |
| #ifndef LSEEK_ERROR |
| # define LSEEK_ERROR (off_t)-1 |
| #endif |
| |
| #ifdef WIN32 |
| # define mkdir(x,y) (mkdir)(x) |
| # undef PATH_MAX |
| # define PATH_MAX MAX_PATH |
| # ifndef __POCC__ |
| # define F_OK 0 |
| # endif |
| #endif |
| |
| /* |
| * Default sizeof(off_t) in case it hasn't been defined in config file. |
| */ |
| |
| #ifndef SIZEOF_OFF_T |
| # if defined(__VMS) && !defined(__VAX) |
| # if defined(_LARGEFILE) |
| # define SIZEOF_OFF_T 8 |
| # endif |
| # elif defined(__OS400__) && defined(__ILEC400__) |
| # if defined(_LARGE_FILES) |
| # define SIZEOF_OFF_T 8 |
| # endif |
| # elif defined(__MVS__) && defined(__IBMC__) |
| # if defined(_LP64) || defined(_LARGE_FILES) |
| # define SIZEOF_OFF_T 8 |
| # endif |
| # elif defined(__370__) && defined(__IBMC__) |
| # if defined(_LP64) || defined(_LARGE_FILES) |
| # define SIZEOF_OFF_T 8 |
| # endif |
| # elif defined(TPF) |
| # define SIZEOF_OFF_T 8 |
| # endif |
| # ifndef SIZEOF_OFF_T |
| # define SIZEOF_OFF_T 4 |
| # endif |
| #endif |
| |
| #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" |
| |
| #ifdef CURL_DOES_CONVERSIONS |
| #ifdef HAVE_ICONV |
| iconv_t inbound_cd = (iconv_t)-1; |
| iconv_t outbound_cd = (iconv_t)-1; |
| |
| /* |
| * convert_to_network() is an internal function to convert |
| * from the host encoding to ASCII on non-ASCII platforms. |
| */ |
| static CURLcode |
| convert_to_network(char *buffer, size_t length) |
| { |
| CURLcode rc; |
| |
| /* translate from the host encoding to the network encoding */ |
| char *input_ptr, *output_ptr; |
| size_t in_bytes, out_bytes; |
| |
| /* open an iconv conversion descriptor if necessary */ |
| if(outbound_cd == (iconv_t)-1) { |
| outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, |
| CURL_ICONV_CODESET_OF_HOST); |
| if(outbound_cd == (iconv_t)-1) { |
| return CURLE_CONV_FAILED; |
| } |
| } |
| /* call iconv */ |
| input_ptr = output_ptr = buffer; |
| in_bytes = out_bytes = length; |
| rc = iconv(outbound_cd, &input_ptr, &in_bytes, |
| &output_ptr, &out_bytes); |
| if((rc == -1) || (in_bytes != 0)) { |
| return CURLE_CONV_FAILED; |
| } |
| |
| return CURLE_OK; |
| } |
| |
| /* |
| * convert_from_network() is an internal function |
| * for performing ASCII conversions on non-ASCII platforms. |
| */ |
| static CURLcode |
| convert_from_network(char *buffer, size_t length) |
| { |
| CURLcode rc; |
| |
| /* translate from the network encoding to the host encoding */ |
| char *input_ptr, *output_ptr; |
| size_t in_bytes, out_bytes; |
| |
| /* open an iconv conversion descriptor if necessary */ |
| if(inbound_cd == (iconv_t)-1) { |
| inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, |
| CURL_ICONV_CODESET_OF_NETWORK); |
| if(inbound_cd == (iconv_t)-1) { |
| return CURLE_CONV_FAILED; |
| } |
| } |
| /* call iconv */ |
| input_ptr = output_ptr = buffer; |
| in_bytes = out_bytes = length; |
| rc = iconv(inbound_cd, &input_ptr, &in_bytes, |
| &output_ptr, &out_bytes); |
| if((rc == -1) || (in_bytes != 0)) { |
| return CURLE_CONV_FAILED; |
| } |
| |
| return CURLE_OK; |
| } |
| #endif /* HAVE_ICONV */ |
| |
| static |
| char convert_char(curl_infotype infotype, char this_char) |
| { |
| /* determine how this specific character should be displayed */ |
| switch(infotype) { |
| case CURLINFO_DATA_IN: |
| case CURLINFO_DATA_OUT: |
| case CURLINFO_SSL_DATA_IN: |
| case CURLINFO_SSL_DATA_OUT: |
| /* data, treat as ASCII */ |
| if((this_char >= 0x20) && (this_char < 0x7f)) { |
| /* printable ASCII hex value: convert to host encoding */ |
| convert_from_network(&this_char, 1); |
| } |
| else { |
| /* non-printable ASCII, use a replacement character */ |
| return UNPRINTABLE_CHAR; |
| } |
| /* fall through to default */ |
| default: |
| /* treat as host encoding */ |
| if(ISPRINT(this_char) |
| && (this_char != '\t') |
| && (this_char != '\r') |
| && (this_char != '\n')) { |
| /* printable characters excluding tabs and line end characters */ |
| return this_char; |
| } |
| break; |
| } |
| /* non-printable, use a replacement character */ |
| return UNPRINTABLE_CHAR; |
| } |
| #endif /* CURL_DOES_CONVERSIONS */ |
| |
| #if defined(WIN32) && !defined(__MINGW64__) |
| |
| #ifdef __BORLANDC__ |
| /* 64-bit lseek-like function unavailable */ |
| # define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence) |
| #endif |
| |
| #ifdef __POCC__ |
| # if(__POCC__ < 450) |
| /* 64-bit lseek-like function unavailable */ |
| # define _lseeki64(hnd,ofs,whence) _lseek(hnd,ofs,whence) |
| # else |
| # define _lseeki64(hnd,ofs,whence) _lseek64(hnd,ofs,whence) |
| # endif |
| #endif |
| |
| #ifndef HAVE_FTRUNCATE |
| #define HAVE_FTRUNCATE 1 |
| #endif |
| |
| /* |
| * Truncate a file handle at a 64-bit position 'where'. |
| */ |
| |
| static int ftruncate64(int fd, curl_off_t where) |
| { |
| if(_lseeki64(fd, where, SEEK_SET) < 0) |
| return -1; |
| |
| if(!SetEndOfFile((HANDLE)_get_osfhandle(fd))) |
| return -1; |
| |
| return 0; |
| } |
| #define ftruncate(fd,where) ftruncate64(fd,where) |
| |
| #endif /* WIN32 */ |
| |
| typedef enum { |
| TRACE_NONE, /* no trace/verbose output at all! */ |
| TRACE_BIN, /* tcpdump inspired look */ |
| TRACE_ASCII, /* like *BIN but without the hex output */ |
| TRACE_PLAIN /* -v/--verbose type */ |
| } trace; |
| |
| struct OutStruct { |
| char *filename; |
| bool alloc_filename; |
| FILE *stream; |
| struct Configurable *config; |
| curl_off_t bytes; /* amount written so far */ |
| curl_off_t init; /* original size (non-zero when appending) */ |
| }; |
| |
| struct Configurable { |
| CURL *easy; /* once we have one, we keep it here */ |
| bool remote_time; |
| char *random_file; |
| char *egd_file; |
| char *useragent; |
| char *cookie; /* single line with specified cookies */ |
| char *cookiejar; /* write to this file */ |
| char *cookiefile; /* read from this file */ |
| bool cookiesession; /* new session? */ |
| bool encoding; /* Accept-Encoding please */ |
| bool tr_encoding; /* Transfer-Encoding please */ |
| long authtype; /* auth bitmask */ |
| bool use_resume; |
| bool resume_from_current; |
| bool disable_epsv; |
| bool disable_eprt; |
| bool ftp_pret; |
| long proto; |
| bool proto_present; |
| long proto_redir; |
| bool proto_redir_present; |
| curl_off_t resume_from; |
| char *postfields; |
| curl_off_t postfieldsize; |
| char *referer; |
| long timeout; |
| long connecttimeout; |
| long maxredirs; |
| curl_off_t max_filesize; |
| char *headerfile; |
| char *ftpport; |
| char *iface; |
| int localport; |
| int localportrange; |
| unsigned short porttouse; |
| char *range; |
| long low_speed_limit; |
| long low_speed_time; |
| bool showerror; |
| char *userpwd; |
| char *tls_username; |
| char *tls_password; |
| char *tls_authtype; |
| char *proxyuserpwd; |
| char *proxy; |
| int proxyver; /* set to CURLPROXY_HTTP* define */ |
| char *noproxy; |
| char *mail_from; |
| struct curl_slist *mail_rcpt; |
| bool proxytunnel; |
| bool ftp_append; /* APPE on ftp */ |
| bool mute; /* shutup */ |
| bool use_ascii; /* select ascii or text transfer */ |
| bool autoreferer; /* automatically set referer */ |
| bool failonerror; /* fail on (HTTP) errors */ |
| bool include_headers; /* send headers to data output */ |
| bool no_body; /* don't get the body */ |
| bool dirlistonly; /* only get the FTP dir list */ |
| bool followlocation; /* follow http redirects */ |
| bool unrestricted_auth; /* Continue to send authentication (user+password) |
| when following ocations, even when hostname |
| changed */ |
| bool netrc_opt; |
| bool netrc; |
| char *netrc_file; |
| bool noprogress; |
| bool isatty; /* updated internally only if the output is a tty */ |
| struct getout *url_list; /* point to the first node */ |
| struct getout *url_last; /* point to the last/current node */ |
| struct getout *url_get; /* point to the node to fill in URL */ |
| struct getout *url_out; /* point to the node to fill in outfile */ |
| char *cipher_list; |
| char *cert; |
| char *cert_type; |
| char *cacert; |
| char *capath; |
| char *crlfile; |
| char *key; |
| char *key_type; |
| char *key_passwd; |
| char *pubkey; |
| char *hostpubmd5; |
| char *engine; |
| bool list_engines; |
| bool crlf; |
| char *customrequest; |
| char *krblevel; |
| char *trace_dump; /* file to dump the network trace to, or NULL */ |
| FILE *trace_stream; |
| bool trace_fopened; |
| trace tracetype; |
| bool tracetime; /* include timestamp? */ |
| long httpversion; |
| int progressmode; |
| bool nobuffer; |
| bool readbusy; /* set when reading input returns EAGAIN */ |
| bool globoff; |
| bool use_httpget; |
| bool insecure_ok; /* set TRUE to allow insecure SSL connects */ |
| bool create_dirs; |
| bool ftp_create_dirs; |
| bool ftp_skip_ip; |
| bool proxynegotiate; |
| bool proxyntlm; |
| bool proxydigest; |
| bool proxybasic; |
| bool proxyanyauth; |
| char *writeout; /* %-styled format string to output */ |
| bool writeenv; /* write results to environment, if available */ |
| FILE *errors; /* if stderr redirect is requested */ |
| bool errors_fopened; |
| struct curl_slist *quote; |
| struct curl_slist *postquote; |
| struct curl_slist *prequote; |
| long ssl_version; |
| long ip_version; |
| curl_TimeCond timecond; |
| time_t condtime; |
| struct curl_slist *headers; |
| struct curl_httppost *httppost; |
| struct curl_httppost *last_post; |
| struct curl_slist *telnet_options; |
| struct curl_slist *resolve; |
| HttpReq httpreq; |
| |
| /* for bandwidth limiting features: */ |
| curl_off_t sendpersecond; /* send to peer */ |
| curl_off_t recvpersecond; /* receive from peer */ |
| |
| bool ftp_ssl; |
| bool ftp_ssl_reqd; |
| bool ftp_ssl_control; |
| bool ftp_ssl_ccc; |
| int ftp_ssl_ccc_mode; |
| |
| char *socksproxy; /* set to server string */ |
| int socksver; /* set to CURLPROXY_SOCKS* define */ |
| char *socks5_gssapi_service; /* set service name for gssapi principal |
| * default rcmd */ |
| int socks5_gssapi_nec ; /* The NEC reference server does not protect |
| * the encryption type exchange */ |
| |
| bool tcp_nodelay; |
| long req_retry; /* number of retries */ |
| long retry_delay; /* delay between retries (in seconds) */ |
| long retry_maxtime; /* maximum time to keep retrying */ |
| |
| char *ftp_account; /* for ACCT */ |
| char *ftp_alternative_to_user; /* send command if USER/PASS fails */ |
| int ftp_filemethod; |
| long tftp_blksize; /* TFTP BLKSIZE option */ |
| bool ignorecl; /* --ignore-content-length */ |
| bool disable_sessionid; |
| |
| char *libcurl; /* output libcurl code to this file name */ |
| bool raw; |
| bool post301; |
| bool post302; |
| bool nokeepalive; /* for keepalive needs */ |
| long alivetime; |
| bool content_disposition; /* use Content-disposition filename */ |
| |
| int default_node_flags; /* default flags to search for each 'node', which is |
| basically each given URL to transfer */ |
| struct OutStruct *outs; |
| bool xattr; /* store metadata in extended attributes */ |
| long gssapi_delegation; |
| }; |
| |
| #define WARN_PREFIX "Warning: " |
| #define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX)) |
| /* produce this text message to the user unless mute was selected */ |
| static void warnf(struct Configurable *config, const char *fmt, ...) |
| { |
| if(!config->mute) { |
| va_list ap; |
| int len; |
| char *ptr; |
| char print_buffer[256]; |
| |
| va_start(ap, fmt); |
| len = vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap); |
| va_end(ap); |
| |
| ptr = print_buffer; |
| while(len > 0) { |
| fputs(WARN_PREFIX, config->errors); |
| |
| if(len > (int)WARN_TEXTWIDTH) { |
| int cut = WARN_TEXTWIDTH-1; |
| |
| while(!ISSPACE(ptr[cut]) && cut) { |
| cut--; |
| } |
| if(0 == cut) |
| /* not a single cutting position was found, just cut it at the |
| max text width then! */ |
| cut = WARN_TEXTWIDTH-1; |
| |
| (void)fwrite(ptr, cut + 1, 1, config->errors); |
| fputs("\n", config->errors); |
| ptr += cut+1; /* skip the space too */ |
| len -= cut; |
| } |
| else { |
| fputs(ptr, config->errors); |
| len = 0; |
| } |
| } |
| } |
| } |
| |
| /* |
| * This is the main global constructor for the app. Call this before |
| * _any_ libcurl usage. If this fails, *NO* libcurl functions may be |
| * used, or havoc may be the result. |
| */ |
| static CURLcode main_init(void) |
| { |
| #ifdef DJGPP |
| /* stop stat() wasting time */ |
| _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; |
| #endif |
| |
| return curl_global_init(CURL_GLOBAL_DEFAULT); |
| } |
| |
| /* |
| * This is the main global destructor for the app. Call this after |
| * _all_ libcurl usage is done. |
| */ |
| static void main_free(void) |
| { |
| curl_global_cleanup(); |
| #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) |
| /* close iconv conversion descriptor */ |
| if(inbound_cd != (iconv_t)-1) |
| iconv_close(inbound_cd); |
| if(outbound_cd != (iconv_t)-1) |
| iconv_close(outbound_cd); |
| #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ |
| } |
| |
| static int SetHTTPrequest(struct Configurable *config, |
| HttpReq req, HttpReq *store) |
| { |
| if((*store == HTTPREQ_UNSPEC) || |
| (*store == req)) { |
| *store = req; |
| return 0; |
| } |
| warnf(config, "You can only select one HTTP request!\n"); |
| return 1; |
| } |
| |
| static void helpf(FILE *errors, const char *fmt, ...) |
| { |
| va_list ap; |
| if(fmt) { |
| va_start(ap, fmt); |
| fputs("curl: ", errors); /* prefix it */ |
| vfprintf(errors, fmt, ap); |
| va_end(ap); |
| } |
| fprintf(errors, "curl: try 'curl --help' " |
| #ifdef USE_MANUAL |
| "or 'curl --manual' " |
| #endif |
| "for more information\n"); |
| } |
| |
| /* |
| * A chain of these nodes contain URL to get and where to put the URL's |
| * contents. |
| */ |
| struct getout { |
| struct getout *next; /* next one */ |
| char *url; /* the URL we deal with */ |
| char *outfile; /* where to store the output */ |
| char *infile; /* file to upload, if GETOUT_UPLOAD is set */ |
| int flags; /* options */ |
| }; |
| #define GETOUT_OUTFILE (1<<0) /* set when outfile is deemed done */ |
| #define GETOUT_URL (1<<1) /* set when URL is deemed done */ |
| #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */ |
| #define GETOUT_UPLOAD (1<<3) /* if set, -T has been used */ |
| #define GETOUT_NOUPLOAD (1<<4) /* if set, -T "" has been used */ |
| |
| static void help(void) |
| { |
| int i; |
| /* 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; |
| }; |
| |
| /* global variable to hold info about libcurl */ |
| static curl_version_info_data *curlinfo; |
| |
| static int parseconfig(const char *filename, |
| struct Configurable *config); |
| static char *my_get_line(FILE *fp); |
| static int create_dir_hierarchy(const char *outfile, FILE *errors); |
| |
| static void GetStr(char **string, |
| const char *value) |
| { |
| if(*string) |
| free(*string); |
| if(value) |
| *string = strdup(value); |
| else |
| *string = NULL; |
| } |
| |
| static void clean_getout(struct Configurable *config) |
| { |
| struct getout *node=config->url_list; |
| struct getout *next; |
| |
| while(node) { |
| next = node->next; |
| if(node->url) |
| free(node->url); |
| if(node->outfile) |
| free(node->outfile); |
| if(node->infile) |
| free(node->infile); |
| free(node); |
| |
| node = next; /* GOTO next */ |
| } |
| } |
| |
| static struct getout *new_getout(struct Configurable *config) |
| { |
| struct getout *node =malloc(sizeof(struct getout)); |
| struct getout *last= config->url_last; |
| if(node) { |
| /* clear the struct */ |
| memset(node, 0, sizeof(struct getout)); |
| |
| /* append this new node last in the list */ |
| if(last) |
| last->next = node; |
| else |
| config->url_list = node; /* first node */ |
| |
| /* move the last pointer */ |
| config->url_last = node; |
| |
| node->flags = config->default_node_flags; |
| } |
| return node; |
| } |
| |
| /* Structure for storing the information needed to build a multiple files |
| * section |
| */ |
| struct multi_files { |
| struct curl_forms form; |
| struct multi_files *next; |
| }; |
| |
| /* Add a new list entry possibly with a type_name |
| */ |
| static struct multi_files * |
| AddMultiFiles(const char *file_name, |
| const char *type_name, |
| const char *show_filename, |
| struct multi_files **multi_start, |
| struct multi_files **multi_current) |
| { |
| struct multi_files *multi; |
| struct multi_files *multi_type = NULL; |
| struct multi_files *multi_name = NULL; |
| multi = malloc(sizeof(struct multi_files)); |
| if(multi) { |
| memset(multi, 0, sizeof(struct multi_files)); |
| multi->form.option = CURLFORM_FILE; |
| multi->form.value = file_name; |
| } |
| else |
| return NULL; |
| |
| if(!*multi_start) |
| *multi_start = multi; |
| |
| if(type_name) { |
| multi_type = malloc(sizeof(struct multi_files)); |
| if(multi_type) { |
| memset(multi_type, 0, sizeof(struct multi_files)); |
| multi_type->form.option = CURLFORM_CONTENTTYPE; |
| multi_type->form.value = type_name; |
| multi->next = multi_type; |
| |
| multi = multi_type; |
| } |
| else { |
| free(multi); |
| return NULL; |
| } |
| } |
| if(show_filename) { |
| multi_name = malloc(sizeof(struct multi_files)); |
| if(multi_name) { |
| memset(multi_name, 0, sizeof(struct multi_files)); |
| multi_name->form.option = CURLFORM_FILENAME; |
| multi_name->form.value = show_filename; |
| multi->next = multi_name; |
| |
| multi = multi_name; |
| } |
| else { |
| free(multi); |
| return NULL; |
| } |
| } |
| |
| if(*multi_current) |
| (*multi_current)->next = multi; |
| |
| *multi_current = multi; |
| |
| return *multi_current; |
| } |
| |
| /* Free the items of the list. |
| */ |
| static void FreeMultiInfo(struct multi_files *multi_start) |
| { |
| struct multi_files *multi; |
| while(multi_start) { |
| multi = multi_start; |
| multi_start = multi_start->next; |
| free(multi); |
| } |
| } |
| |
| /* Print list of OpenSSL engines supported. |
| */ |
| static void list_engines(const struct curl_slist *engines) |
| { |
| puts("Build-time engines:"); |
| if(!engines) { |
| puts(" <none>"); |
| return; |
| } |
| for(; engines; engines = engines->next) |
| printf(" %s\n", engines->data); |
| } |
| |
| /*************************************************************************** |
| * |
| * formparse() |
| * |
| * Reads a 'name=value' parameter and builds the appropriate linked list. |
| * |
| * Specify files to upload with 'name=@filename'. Supports specified |
| * given Content-Type of the files. Such as ';type=<content-type>'. |
| * |
| * If literal_value is set, any initial '@' or '<' in the value string |
| * loses its special meaning, as does any embedded ';type='. |
| * |
| * You may specify more than one file for a single name (field). Specify |
| * multiple files by writing it like: |
| * |
| * 'name=@filename,filename2,filename3' |
| * |
| * If you want content-types specified for each too, write them like: |
| * |
| * 'name=@filename;type=image/gif,filename2,filename3' |
| * |
| * If you want custom headers added for a single part, write them in a separate |
| * file and do like this: |
| * |
| * 'name=foo;headers=@headerfile' or why not |
| * 'name=@filemame;headers=@headerfile' |
| * |
| * To upload a file, but to fake the file name that will be included in the |
| * formpost, do like this: |
| * |
| * 'name=@filename;filename=/dev/null' |
| * |
| * This function uses curl_formadd to fulfill it's job. Is heavily based on |
| * the old curl_formparse code. |
| * |
| ***************************************************************************/ |
| |
| #define FORM_FILE_SEPARATOR ',' |
| #define FORM_TYPE_SEPARATOR ';' |
| |
| static int formparse(struct Configurable *config, |
| const char *input, |
| struct curl_httppost **httppost, |
| struct curl_httppost **last_post, |
| bool literal_value) |
| { |
| /* nextarg MUST be a string in the format 'name=contents' and we'll |
| build a linked list with the info */ |
| char name[256]; |
| char *contents; |
| char major[128]; |
| char minor[128]; |
| char *contp; |
| const char *type = NULL; |
| char *sep; |
| char *sep2; |
| |
| if((1 == sscanf(input, "%255[^=]=", name)) && |
| ((contp = strchr(input, '=')) != NULL)) { |
| /* the input was using the correct format */ |
| |
| /* Allocate the contents */ |
| contents = strdup(contp+1); |
| if(!contents) { |
| fprintf(config->errors, "out of memory\n"); |
| return 1; |
| } |
| contp = contents; |
| |
| if('@' == contp[0] && !literal_value) { |
| struct multi_files *multi_start = NULL, *multi_current = NULL; |
| /* we use the @-letter to indicate file name(s) */ |
| contp++; |
| |
| multi_start = multi_current=NULL; |
| |
| do { |
| /* since this was a file, it may have a content-type specifier |
| at the end too, or a filename. Or both. */ |
| char *ptr; |
| char *filename=NULL; |
| |
| sep=strchr(contp, FORM_TYPE_SEPARATOR); |
| sep2=strchr(contp, FORM_FILE_SEPARATOR); |
| |
| /* pick the closest */ |
| if(sep2 && (sep2 < sep)) { |
| sep = sep2; |
| |
| /* no type was specified! */ |
| } |
| |
| type = NULL; |
| |
| if(sep) { |
| |
| /* if we got here on a comma, don't do much */ |
| if(FORM_FILE_SEPARATOR == *sep) |
| ptr = NULL; |
| else |
| ptr = sep+1; |
| |
| *sep=0; /* terminate file name at separator */ |
| |
| while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) { |
| |
| /* pass all white spaces */ |
| while(ISSPACE(*ptr)) |
| ptr++; |
| |
| if(checkprefix("type=", ptr)) { |
| /* set type pointer */ |
| type = &ptr[5]; |
| |
| /* verify that this is a fine type specifier */ |
| if(2 != sscanf(type, "%127[^/]/%127[^;,\n]", |
| major, minor)) { |
| warnf(config, "Illegally formatted content-type field!\n"); |
| free(contents); |
| FreeMultiInfo(multi_start); |
| return 2; /* illegal content-type syntax! */ |
| } |
| |
| /* now point beyond the content-type specifier */ |
| sep = (char *)type + strlen(major)+strlen(minor)+1; |
| |
| /* there's a semicolon following - we check if it is a filename |
| specified and if not we simply assume that it is text that |
| the user wants included in the type and include that too up |
| to the next zero or semicolon. */ |
| if((*sep==';') && !checkprefix(";filename=", sep)) { |
| sep2 = strchr(sep+1, ';'); |
| if(sep2) |
| sep = sep2; |
| else |
| sep = sep+strlen(sep); /* point to end of string */ |
| } |
| |
| if(*sep) { |
| *sep=0; /* zero terminate type string */ |
| |
| ptr=sep+1; |
| } |
| else |
| ptr = NULL; /* end */ |
| } |
| else if(checkprefix("filename=", ptr)) { |
| filename = &ptr[9]; |
| ptr=strchr(filename, FORM_TYPE_SEPARATOR); |
| if(!ptr) { |
| ptr=strchr(filename, FORM_FILE_SEPARATOR); |
| } |
| if(ptr) { |
| *ptr=0; /* zero terminate */ |
| ptr++; |
| } |
| } |
| else |
| /* confusion, bail out of loop */ |
| break; |
| } |
| /* find the following comma */ |
| if(ptr) |
| sep=strchr(ptr, FORM_FILE_SEPARATOR); |
| else |
| sep=NULL; |
| } |
| else { |
| sep=strchr(contp, FORM_FILE_SEPARATOR); |
| } |
| if(sep) { |
| /* the next file name starts here */ |
| *sep =0; |
| sep++; |
| } |
| /* if type == NULL curl_formadd takes care of the problem */ |
| |
| if(!AddMultiFiles(contp, type, filename, &multi_start, |
| &multi_current)) { |
| warnf(config, "Error building form post!\n"); |
| free(contents); |
| FreeMultiInfo(multi_start); |
| return 3; |
| } |
| contp = sep; /* move the contents pointer to after the separator */ |
| |
| } while(sep && *sep); /* loop if there's another file name */ |
| |
| /* now we add the multiple files section */ |
| if(multi_start) { |
| struct curl_forms *forms = NULL; |
| struct multi_files *ptr = multi_start; |
| unsigned int i, count = 0; |
| while(ptr) { |
| ptr = ptr->next; |
| ++count; |
| } |
| forms = malloc((count+1)*sizeof(struct curl_forms)); |
| if(!forms) { |
| fprintf(config->errors, "Error building form post!\n"); |
| free(contents); |
| FreeMultiInfo(multi_start); |
| return 4; |
| } |
| for(i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next) { |
| forms[i].option = ptr->form.option; |
| forms[i].value = ptr->form.value; |
| } |
| forms[count].option = CURLFORM_END; |
| FreeMultiInfo(multi_start); |
| if(curl_formadd(httppost, last_post, |
| CURLFORM_COPYNAME, name, |
| CURLFORM_ARRAY, forms, CURLFORM_END) != 0) { |
| warnf(config, "curl_formadd failed!\n"); |
| free(forms); |
| free(contents); |
| return 5; |
| } |
| free(forms); |
| } |
| } |
| else { |
| struct curl_forms info[4]; |
| int i = 0; |
| char *ct = literal_value? NULL: strstr(contp, ";type="); |
| |
| info[i].option = CURLFORM_COPYNAME; |
| info[i].value = name; |
| i++; |
| |
| if(ct) { |
| info[i].option = CURLFORM_CONTENTTYPE; |
| info[i].value = &ct[6]; |
| i++; |
| ct[0]=0; /* zero terminate here */ |
| } |
| |
| if(contp[0]=='<' && !literal_value) { |
| info[i].option = CURLFORM_FILECONTENT; |
| info[i].value = contp+1; |
| i++; |
| info[i].option = CURLFORM_END; |
| |
| if(curl_formadd(httppost, last_post, |
| CURLFORM_ARRAY, info, CURLFORM_END ) != 0) { |
| warnf(config, "curl_formadd failed, possibly the file %s is bad!\n", |
| contp+1); |
| free(contents); |
| return 6; |
| } |
| } |
| else { |
| #ifdef CURL_DOES_CONVERSIONS |
| convert_to_network(contp, strlen(contp)); |
| #endif |
| info[i].option = CURLFORM_COPYCONTENTS; |
| info[i].value = contp; |
| i++; |
| info[i].option = CURLFORM_END; |
| if(curl_formadd(httppost, last_post, |
| CURLFORM_ARRAY, info, CURLFORM_END) != 0) { |
| warnf(config, "curl_formadd failed!\n"); |
| free(contents); |
| return 7; |
| } |
| } |
| } |
| |
| } |
| else { |
| warnf(config, "Illegally formatted input field!\n"); |
| return 1; |
| } |
| free(contents); |
| return 0; |
| } |
| |
| |
| typedef enum { |
| PARAM_OK, |
| PARAM_OPTION_AMBIGUOUS, |
| PARAM_OPTION_UNKNOWN, |
| PARAM_REQUIRES_PARAMETER, |
| PARAM_BAD_USE, |
| PARAM_HELP_REQUESTED, |
| PARAM_GOT_EXTRA_PARAMETER, |
| PARAM_BAD_NUMERIC, |
| PARAM_LIBCURL_DOESNT_SUPPORT, |
| PARAM_NO_MEM, |
| PARAM_LAST |
| } ParameterError; |
| |
| static const char *param2text(int res) |
| { |
| ParameterError error = (ParameterError)res; |
| switch(error) { |
| case PARAM_GOT_EXTRA_PARAMETER: |
| return "had unsupported trailing garbage"; |
| case PARAM_OPTION_UNKNOWN: |
| return "is unknown"; |
| case PARAM_OPTION_AMBIGUOUS: |
| return "is ambiguous"; |
| case PARAM_REQUIRES_PARAMETER: |
| return "requires parameter"; |
| case PARAM_BAD_USE: |
| return "is badly used here"; |
| case PARAM_BAD_NUMERIC: |
| return "expected a proper numerical parameter"; |
| case PARAM_LIBCURL_DOESNT_SUPPORT: |
| return "the installed libcurl version doesn't support this"; |
| case PARAM_NO_MEM: |
| return "out of memory"; |
| default: |
| return "unknown error"; |
| } |
| } |
| |
| static ParameterError file2string(char **bufp, FILE *file) |
| { |
| char buffer[256]; |
| char *ptr; |
| char *string = NULL; |
| size_t stringlen = 0; |
| size_t buflen; |
| |
| if(file) { |
| while(fgets(buffer, sizeof(buffer), file)) { |
| if((ptr = strchr(buffer, '\r')) != NULL) |
| *ptr = '\0'; |
| if((ptr = strchr(buffer, '\n')) != NULL) |
| *ptr = '\0'; |
| buflen = strlen(buffer); |
| if((ptr = realloc(string, stringlen+buflen+1)) == NULL) { |
| if(string) |
| free(string); |
| return PARAM_NO_MEM; |
| } |
| string = ptr; |
| strcpy(string+stringlen, buffer); |
| stringlen += buflen; |
| } |
| } |
| *bufp = string; |
| return PARAM_OK; |
| } |
| |
| static ParameterError file2memory(char **bufp, size_t *size, FILE *file) |
| { |
| char *newbuf; |
| char *buffer = NULL; |
| size_t alloc = 512; |
| size_t nused = 0; |
| size_t nread; |
| |
| if(file) { |
| do { |
| if(!buffer || (alloc == nused)) { |
| /* size_t overflow detection for huge files */ |
| if(alloc+1 > ((size_t)-1)/2) { |
| if(buffer) |
| free(buffer); |
| return PARAM_NO_MEM; |
| } |
| alloc *= 2; |
| /* allocate an extra char, reserved space, for null termination */ |
| if((newbuf = realloc(buffer, alloc+1)) == NULL) { |
| if(buffer) |
| free(buffer); |
| return PARAM_NO_MEM; |
| } |
| buffer = newbuf; |
| } |
| nread = fread(buffer+nused, 1, alloc-nused, file); |
| nused += nread; |
| } while(nread); |
| /* 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) { |
| free(buffer); |
| buffer = NULL; /* 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 */ |
| free(buffer); |
| return 1; |
| } |
| } |
| |
| for(pp=protos; pp->name; pp++) { |
| if(curlx_raw_equal(token, pp->name)) { |
| switch (action) { |
| case deny: |
| *val &= ~(pp->bit); |
| break; |
| case allow: |
| *val |= pp->bit; |
| break; |
| case set: |
| *val = pp->bit; |
| break; |
| } |
| break; |
| } |
| } |
| |
| if(!(pp->name)) { /* 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); |
| } |
| } |
| free(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 int sockoptcallback(void *clientp, curl_socket_t curlfd, |
| curlsocktype purpose) |
| { |
| struct Configurable *config = (struct Configurable *)clientp; |
| int onoff = 1; /* this callback is only used if we ask for keepalives on the |
| connection */ |
| #if defined(TCP_KEEPIDLE) || defined(TCP_KEEPINTVL) |
| int keepidle = (int)config->alivetime; |
| #endif |
| |
| switch(purpose) { |
| case CURLSOCKTYPE_IPCXN: |
| if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&onoff, |
| sizeof(onoff)) < 0) { |
| /* don't abort operation, just issue a warning */ |
| SET_SOCKERRNO(0); |
| warnf(clientp, "Could not set SO_KEEPALIVE!\n"); |
| return 0; |
| } |
| else { |
| if(config->alivetime) { |
| #ifdef TCP_KEEPIDLE |
| if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle, |
| sizeof(keepidle)) < 0) { |
| /* don't abort operation, just issue a warning */ |
| SET_SOCKERRNO(0); |
| warnf(clientp, "Could not set TCP_KEEPIDLE!\n"); |
| return 0; |
| } |
| #endif |
| #ifdef TCP_KEEPINTVL |
| if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepidle, |
| sizeof(keepidle)) < 0) { |
| /* don't abort operation, just issue a warning */ |
| SET_SOCKERRNO(0); |
| warnf(clientp, "Could not set TCP_KEEPINTVL!\n"); |
| return 0; |
| } |
| #endif |
| #if !defined(TCP_KEEPIDLE) || !defined(TCP_KEEPINTVL) |
| warnf(clientp, "Keep-alive functionality somewhat crippled due to " |
| "missing support in your operating system!\n"); |
| #endif |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static 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 */ |
| if(config->ftpport) |
| free(config->ftpport); |
| config->ftpport = NULL; |
| 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; |
| |
| 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 size = 0; |
| 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(""); |
| } |
| else { |
| char *enc = curl_easy_escape(config->easy, postdata, (int)size); |
| free(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); |
| else |
| strcpy(n, enc); |
| curl_free(enc); |
| postdata = n; |
| } |
| else |
| return PARAM_NO_MEM; |
| } |
| } |
| else if('@' == *nextarg) { |
| size_t size = 0; |
| /* 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); |
| config->postfieldsize = (curl_off_t)size; |
| } |
| else |
| err = file2string(&postdata, file); |
| |
| 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); |
| } |
| |
| #ifdef CURL_DOES_CONVERSIONS |
| if(subletter != 'b') { /* NOT forced binary, convert to ASCII */ |
| convert_to_network(postdata, strlen(postdata)); |
| } |
| #endif |
| |
| if(config->postfields) { |
| /* we already have a string, we append this one |
| with a separating &-letter */ |
| char *oldpost=config->postfields; |
| size_t newlen = strlen(oldpost) + strlen(postdata) + 2; |
| config->postfields=malloc(newlen); |
| if(!config->postfields) { |
| free(postdata); |
| return PARAM_NO_MEM; |
| } |
| /* use ASCII value 0x26 for '&' to accommodate non-ASCII platforms */ |
| snprintf(config->postfields, newlen, "%s\x26%s", oldpost, postdata); |
| free(oldpost); |
| free(postdata); |
| } |
| else |
| config->postfields=postdata; |
| } |
| /* |
| We can't set the request type here, as this data might be used in |
| a simple GET if -G is used. Already or soon. |
| |
| if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) |
| return PARAM_BAD_USE; |
| */ |
| break; |
| case 'D': |
| /* dump-header to given file name */ |
| GetStr(&config->headerfile, nextarg); |
| break; |
| case 'e': |
| { |
| char *ptr = strstr(nextarg, ";auto"); |
| if(ptr) { |
| /* Automatic referer requested, this may be combined with a |
| set initial one */ |
| config->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); |
| GetStr(&config->range, buffer); |
| } |
| { |
| /* byte range requested */ |
| char* tmp_range; |
| tmp_range=nextarg; |
| while(*tmp_range != '\0') { |
| if(!ISDIGIT(*tmp_range)&&*tmp_range!='-'&&*tmp_range!=',') { |
| warnf(config,"Invalid character is found in given range. " |
| "A specified range MUST have only digits in " |
| "\'start\'-\'stop\'. The server's response to this " |
| "request is uncertain.\n"); |
| break; |
| } |
| tmp_range++; |
| } |
| /* byte range requested */ |
| GetStr(&config->range, nextarg); |
| } |
| break; |
| case 'R': |
| /* use remote file's time */ |
| config->remote_time = 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 */ |
| GetStr(&config->trace_dump, (char *)"%"); |
| if(config->tracetype && (config->tracetype != TRACE_PLAIN)) |
| warnf(config, |
| "-v, --verbose overrides an earlier trace/verbose option\n"); |
| config->tracetype = TRACE_PLAIN; |
| } |
| else |
| /* 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 */ |
| } |
| free(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': |
| free(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 */ |
| free(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) |
| free(param); |
| param = NULL; |
| } |
| |
| #ifdef DEBUG_CONFIG |
| fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)")); |
| #endif |
| res = getparameter(option, param, &usedarg, config); |
| |
| if(param && *param && !usedarg) |
| /* 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) { |
| free(param); |
| param = NULL; |
| } |
| |
| free(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 |
| } |
| |
| static size_t my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream) |
| { |
| size_t rc; |
| struct OutStruct *out=(struct OutStruct *)stream; |
| struct Configurable *config = out->config; |
| |
| /* |
| * Once that libcurl has called back my_fwrite() the returned value |
| * is checked against the amount that was intended to be written, if |
| * it does not match then it fails with CURLE_WRITE_ERROR. So at this |
| * point returning a value different from sz*nmemb indicates failure. |
| */ |
| const size_t err_rc = (sz * nmemb) ? 0 : 1; |
| |
| if(!out->stream) { |
| out->bytes = 0; /* nothing written yet */ |
| if(!out->filename) { |
| warnf(config, "Remote filename has no length!\n"); |
| return err_rc; /* Failure */ |
| } |
| |
| if(config->content_disposition) { |
| /* don't overwrite existing files */ |
| FILE* f = fopen(out->filename, "r"); |
| if(f) { |
| fclose(f); |
| warnf(config, "Refusing to overwrite %s: %s\n", out->filename, |
| strerror(EEXIST)); |
| return err_rc; /* Failure */ |
| } |
| } |
| |
| /* open file for writing */ |
| out->stream=fopen(out->filename, "wb"); |
| if(!out->stream) { |
| warnf(config, "Failed to create the file %s: %s\n", out->filename, |
| strerror(errno)); |
| return err_rc; /* failure */ |
| } |
| } |
| |
| rc = fwrite(buffer, sz, nmemb, out->stream); |
| |
| if((sz * nmemb) == rc) |
| /* we added this amount of data to the output */ |
| out->bytes += (sz * nmemb); |
| |
| if(config->readbusy) { |
| config->readbusy = FALSE; |
| curl_easy_pause(config->easy, CURLPAUSE_CONT); |
| } |
| |
| if(config->nobuffer) { |
| /* disable output buffering */ |
| int res = fflush(out->stream); |
| if(res) { |
| /* return a value that isn't the same as sz * nmemb */ |
| return err_rc; /* failure */ |
| } |
| } |
| |
| return rc; |
| } |
| |
| struct InStruct { |
| int fd; |
| struct Configurable *config; |
| }; |
| |
| #define MAX_SEEK 2147483647 |
| |
| /* |
| * my_seek() is the CURLOPT_SEEKFUNCTION we use |
| */ |
| static int my_seek(void *stream, curl_off_t offset, int whence) |
| { |
| struct InStruct *in=(struct InStruct *)stream; |
| |
| #if(CURL_SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES) |
| /* The offset check following here is only interesting if curl_off_t is |
| larger than off_t and we are not using the WIN32 large file support |
| macros that provide the support to do 64bit seeks correctly */ |
| |
| if(offset > MAX_SEEK) { |
| /* Some precaution code to work around problems with different data sizes |
| to allow seeking >32bit even if off_t is 32bit. Should be very rare and |
| is really valid on weirdo-systems. */ |
| curl_off_t left = offset; |
| |
| if(whence != SEEK_SET) |
| /* this code path doesn't support other types */ |
| return 1; |
| |
| if(LSEEK_ERROR == lseek(in->fd, 0, SEEK_SET)) |
| /* couldn't rewind to beginning */ |
| return 1; |
| |
| while(left) { |
| long step = (left>MAX_SEEK ? MAX_SEEK : (long)left); |
| if(LSEEK_ERROR == lseek(in->fd, step, SEEK_CUR)) |
| /* couldn't seek forwards the desired amount */ |
| return 1; |
| left -= step; |
| } |
| return 0; |
| } |
| #endif |
| if(LSEEK_ERROR == lseek(in->fd, offset, whence)) |
| /* couldn't rewind, the reason is in errno but errno is just not portable |
| enough and we don't actually care that much why we failed. We'll let |
| libcurl know that it may try other means if it wants to. */ |
| return CURL_SEEKFUNC_CANTSEEK; |
| |
| return 0; |
| } |
| |
| static size_t my_fread(void *buffer, size_t sz, size_t nmemb, void *userp) |
| { |
| ssize_t rc; |
| struct InStruct *in=(struct InStruct *)userp; |
| |
| rc = read(in->fd, buffer, sz*nmemb); |
| if(rc < 0) { |
| if(errno == EAGAIN) { |
| errno = 0; |
| in->config->readbusy = TRUE; |
| return CURL_READFUNC_PAUSE; |
| } |
| /* since size_t is unsigned we can't return negative values fine */ |
| rc = 0; |
| } |
| in->config->readbusy = FALSE; |
| return (size_t)rc; |
| } |
| |
| struct ProgressData { |
| int calls; |
| curl_off_t prev; |
| int width; |
| FILE *out; /* where to write everything to */ |
| curl_off_t initial_size; |
| }; |
| |
| static int myprogress (void *clientp, |
| double dltotal, |
| double dlnow, |
| double ultotal, |
| double ulnow) |
| { |
| /* The original progress-bar source code was written for curl by Lars Aas, |
| and this new edition inherits some of his concepts. */ |
| |
| char line[256]; |
| char outline[256]; |
| char format[40]; |
| double frac; |
| double percent; |
| int barwidth; |
| int num; |
| int i; |
| |
| struct ProgressData *bar = (struct ProgressData *)clientp; |
| curl_off_t total = (curl_off_t)dltotal + (curl_off_t)ultotal + |
| bar->initial_size; /* expected transfer size */ |
| curl_off_t point = (curl_off_t)dlnow + (curl_off_t)ulnow + |
| bar->initial_size; /* we've come this far */ |
| |
| if(point > total) |
| /* we have got more than the expected total! */ |
| total = point; |
| |
| bar->calls++; /* simply count invokes */ |
| |
| if(total < 1) { |
| curl_off_t prevblock = bar->prev / 1024; |
| curl_off_t thisblock = point / 1024; |
| while(thisblock > prevblock) { |
| fprintf( bar->out, "#" ); |
| prevblock++; |
| } |
| } |
| else { |
| frac = (double)point / (double)total; |
| percent = frac * 100.0f; |
| barwidth = bar->width - 7; |
| num = (int) (((double)barwidth) * frac); |
| for(i = 0; i < num; i++) |
| line[i] = '#'; |
| line[i] = '\0'; |
| snprintf( format, sizeof(format), "%%-%ds %%5.1f%%%%", barwidth ); |
| snprintf( outline, sizeof(outline), format, line, percent ); |
| fprintf( bar->out, "\r%s", outline ); |
| } |
| fflush(bar->out); |
| bar->prev = point; |
| |
| return 0; |
| } |
| |
| static |
| void progressbarinit(struct ProgressData *bar, |
| struct Configurable *config) |
| { |
| #ifdef __EMX__ |
| /* 20000318 mgs */ |
| int scr_size [2]; |
| #endif |
| char *colp; |
| |
| memset(bar, 0, sizeof(struct ProgressData)); |
| |
| /* pass this through to progress function so |
| * it can display progress towards total file |
| * not just the part that's left. (21-may-03, dbyron) */ |
| if(config->use_resume) |
| bar->initial_size = config->resume_from; |
| |
| /* TODO: get terminal width through ansi escapes or something similar. |
| try to update width when xterm is resized... - 19990617 larsa */ |
| #ifndef __EMX__ |
| /* 20000318 mgs |
| * OS/2 users most likely won't have this env var set, and besides that |
| * we're using our own way to determine screen width */ |
| colp = curlx_getenv("COLUMNS"); |
| if(colp != NULL) { |
| char *endptr; |
| long num = strtol(colp, &endptr, 10); |
| if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 0)) |
| bar->width = (int)num; |
| else |
| bar->width = 79; |
| curl_free(colp); |
| } |
| else |
| bar->width = 79; |
| #else |
| /* 20000318 mgs |
| * We use this emx library call to get the screen width, and subtract |
| * one from what we got in order to avoid a problem with the cursor |
| * advancing to the next line if we print a string that is as long as |
| * the screen is wide. */ |
| |
| _scrsize(scr_size); |
| bar->width = scr_size[0] - 1; |
| #endif |
| |
| bar->out = config->errors; |
| } |
| |
| |
| static |
| void dump(const char *timebuf, const char *text, |
| FILE *stream, const unsigned char *ptr, size_t size, |
| trace tracetype, curl_infotype infotype) |
| { |
| size_t i; |
| size_t c; |
| |
| unsigned int width=0x10; |
| |
| if(tracetype == TRACE_ASCII) |
| /* without the hex output, we can fit more on screen */ |
| width = 0x40; |
| |
| fprintf(stream, "%s%s, %zd bytes (0x%zx)\n", timebuf, text, size, size); |
| |
| for(i=0; i<size; i+= width) { |
| |
| fprintf(stream, "%04zx: ", i); |
| |
| if(tracetype == TRACE_BIN) { |
| /* hex not disabled, show it */ |
| for(c = 0; c < width; c++) |
| if(i+c < size) |
| fprintf(stream, "%02x ", ptr[i+c]); |
| else |
| fputs(" ", stream); |
| } |
| |
| for(c = 0; (c < width) && (i+c < size); c++) { |
| /* check for 0D0A; if found, skip past and start a new line of output */ |
| if((tracetype == TRACE_ASCII) && |
| (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) { |
| i+=(c+2-width); |
| break; |
| } |
| #ifdef CURL_DOES_CONVERSIONS |
| /* repeat the 0D0A check above but use the host encoding for CRLF */ |
| if((tracetype == TRACE_ASCII) && |
| (i+c+1 < size) && ptr[i+c]=='\r' && ptr[i+c+1]=='\n') { |
| i+=(c+2-width); |
| break; |
| } |
| /* convert to host encoding and print this character */ |
| fprintf(stream, "%c", convert_char(infotype, ptr[i+c])); |
| #else |
| (void)infotype; |
| fprintf(stream, "%c", |
| (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:UNPRINTABLE_CHAR); |
| #endif /* CURL_DOES_CONVERSIONS */ |
| /* check again for 0D0A, to avoid an extra \n if it's at width */ |
| if((tracetype == TRACE_ASCII) && |
| (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) { |
| i+=(c+3-width); |
| break; |
| } |
| } |
| fputc('\n', stream); /* newline */ |
| } |
| fflush(stream); |
| } |
| |
| static |
| int my_trace(CURL *handle, curl_infotype type, |
| unsigned char *data, size_t size, |
| void *userp) |
| { |
| struct Configurable *config = (struct Configurable *)userp; |
| FILE *output=config->errors; |
| const char *text; |
| struct timeval tv; |
| struct tm *now; |
| char timebuf[20]; |
| time_t secs; |
| static time_t epoch_offset; |
| static int known_offset; |
| |
| (void)handle; /* prevent compiler warning */ |
| |
| if(config->tracetime) { |
| tv = cutil_tvnow(); |
| if(!known_offset) { |
| epoch_offset = time(NULL) - tv.tv_sec; |
| known_offset = 1; |
| } |
| secs = epoch_offset + tv.tv_sec; |
| now = localtime(&secs); /* not thread safe but we don't care */ |
| snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ", |
| now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec); |
| } |
| else |
| timebuf[0]=0; |
| |
| if(!config->trace_stream) { |
| /* open for append */ |
| if(curlx_strequal("-", config->trace_dump)) |
| config->trace_stream = stdout; |
| else if(curlx_strequal("%", config->trace_dump)) |
| /* Ok, this is somewhat hackish but we do it undocumented for now */ |
| config->trace_stream = config->errors; /* aka stderr */ |
| else { |
| config->trace_stream = fopen(config->trace_dump, "w"); |
| config->trace_fopened = TRUE; |
| } |
| } |
| |
| if(config->trace_stream) |
| output = config->trace_stream; |
| |
| if(!output) { |
| warnf(config, "Failed to create/open output"); |
| return 0; |
| } |
| |
| if(config->tracetype == TRACE_PLAIN) { |
| /* |
| * This is the trace look that is similar to what libcurl makes on its |
| * own. |
| */ |
| static const char * const s_infotype[] = { |
| "*", "<", ">", "{", "}", "{", "}" |
| }; |
| size_t i; |
| size_t st=0; |
| static bool newl = FALSE; |
| static bool traced_data = FALSE; |
| |
| switch(type) { |
| case CURLINFO_HEADER_OUT: |
| for(i=0; i<size-1; i++) { |
| if(data[i] == '\n') { /* LF */ |
| if(!newl) { |
| fprintf(output, "%s%s ", timebuf, s_infotype[type]); |
| } |
| (void)fwrite(data+st, i-st+1, 1, output); |
| st = i+1; |
| newl = FALSE; |
| } |
| } |
| if(!newl) |
| fprintf(output, "%s%s ", timebuf, s_infotype[type]); |
| (void)fwrite(data+st, i-st+1, 1, output); |
| newl = (size && (data[size-1] != '\n'))?TRUE:FALSE; |
| traced_data = FALSE; |
| break; |
| case CURLINFO_TEXT: |
| case CURLINFO_HEADER_IN: |
| if(!newl) |
| fprintf(output, "%s%s ", timebuf, s_infotype[type]); |
| (void)fwrite(data, size, 1, output); |
| newl = (size && (data[size-1] != '\n'))?TRUE:FALSE; |
| traced_data = FALSE; |
| break; |
| case CURLINFO_DATA_OUT: |
| case CURLINFO_DATA_IN: |
| case CURLINFO_SSL_DATA_IN: |
| case CURLINFO_SSL_DATA_OUT: |
| if(!traced_data) { |
| /* if the data is output to a tty and we're sending this debug trace |
| to stderr or stdout, we don't display the alert about the data not |
| being shown as the data _is_ shown then just not via this |
| function */ |
| if(!config->isatty || |
| ((output != stderr) && (output != stdout))) { |
| if(!newl) |
| fprintf(output, "%s%s ", timebuf, s_infotype[type]); |
| fprintf(output, "[data not shown]\n"); |
| newl = FALSE; |
| traced_data = TRUE; |
| } |
| } |
| break; |
| default: /* nada */ |
| newl = FALSE; |
| traced_data = FALSE; |
| break; |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CURL_DOES_CONVERSIONS |
| /* Special processing is needed for CURLINFO_HEADER_OUT blocks |
| * if they contain both headers and data (separated by CRLFCRLF). |
| * We dump the header text and then switch type to CURLINFO_DATA_OUT. |
| */ |
| if((type == CURLINFO_HEADER_OUT) && (size > 4)) { |
| size_t i; |
| for(i = 0; i < size - 4; i++) { |
| if(memcmp(&data[i], "\r\n\r\n", 4) == 0) { |
| /* dump everything through the CRLFCRLF as a sent header */ |
| text = "=> Send header"; |
| dump(timebuf, text, output, data, i+4, config->tracetype, type); |
| data += i + 3; |
| size -= i + 4; |
| type = CURLINFO_DATA_OUT; |
| data += 1; |
| break; |
| } |
| } |
| } |
| #endif /* CURL_DOES_CONVERSIONS */ |
| |
| switch (type) { |
| case CURLINFO_TEXT: |
| fprintf(output, "%s== Info: %s", timebuf, data); |
| default: /* in case a new one is introduced to shock us */ |
| return 0; |
| |
| case CURLINFO_HEADER_OUT: |
| text = "=> Send header"; |
| break; |
| case CURLINFO_DATA_OUT: |
| text = "=> Send data"; |
| break; |
| case CURLINFO_HEADER_IN: |
| text = "<= Recv header"; |
| break; |
| case CURLINFO_DATA_IN: |
| text = "<= Recv data"; |
| break; |
| case CURLINFO_SSL_DATA_IN: |
| text = "<= Recv SSL data"; |
| break; |
| case CURLINFO_SSL_DATA_OUT: |
| text = "=> Send SSL data"; |
| break; |
| } |
| |
| dump(timebuf, text, output, data, size, config->tracetype, type); |
| return 0; |
| } |
| |
| static void free_config_fields(struct Configurable *config) |
| { |
| if(config->random_file) |
| free(config->random_file); |
| if(config->egd_file) |
| free(config->egd_file); |
| if(config->trace_dump) |
| free(config->trace_dump); |
| if(config->cipher_list) |
| free(config->cipher_list); |
| if(config->userpwd) |
| free(config->userpwd); |
| if(config->postfields) |
| free(config->postfields); |
| if(config->proxy) |
| free(config->proxy); |
| if(config->proxyuserpwd) |
| free(config->proxyuserpwd); |
| if(config->noproxy) |
| free(config->noproxy); |
| if(config->cookie) |
| free(config->cookie); |
| if(config->cookiefile) |
| free(config->cookiefile); |
| if(config->krblevel) |
| free(config->krblevel); |
| if(config->headerfile) |
| free(config->headerfile); |
| if(config->ftpport) |
| free(config->ftpport); |
| if(config->range) |
| free(config->range); |
| if(config->customrequest) |
| free(config->customrequest); |
| if(config->writeout) |
| free(config->writeout); |
| if(config->httppost) |
| curl_formfree(config->httppost); |
| if(config->netrc_file) |
| free(config->netrc_file); |
| if(config->cert) |
| free(config->cert); |
| if(config->cacert) |
| free(config->cacert); |
| if(config->cert_type) |
| free(config->cert_type); |
| if(config->capath) |
| free(config->capath); |
| if(config->crlfile) |
| free(config->crlfile); |
| if(config->cookiejar) |
| free(config->cookiejar); |
| if(config->ftp_account) |
| free(config->ftp_account); |
| if(config->ftp_alternative_to_user) |
| free(config->ftp_alternative_to_user); |
| if(config->iface) |
| free(config->iface); |
| if(config->socksproxy) |
| free(config->socksproxy); |
| if(config->libcurl) |
| free(config->libcurl); |
| if(config->key_passwd) |
| free(config->key_passwd); |
| if(config->key) |
| free(config->key); |
| if(config->key_type) |
| free(config->key_type); |
| if(config->pubkey) |
| free(config->pubkey); |
| if(config->referer) |
| free(config->referer); |
| if(config->hostpubmd5) |
| free(config->hostpubmd5); |
| if(config->mail_from) |
| free(config->mail_from); |
| #ifdef USE_TLS_SRP |
| if(config->tls_authtype) |
| free(config->tls_authtype); |
| if(config->tls_username) |
| free(config->tls_username); |
| if(config->tls_password) |
| free(config->tls_password); |
| #endif |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) |
| if(config->socks5_gssapi_service) |
| free(config->socks5_gssapi_service); |
| #endif |
| |
| curl_slist_free_all(config->quote); /* checks for config->quote == NULL */ |
| curl_slist_free_all(config->prequote); |
| curl_slist_free_all(config->postquote); |
| curl_slist_free_all(config->headers); |
| curl_slist_free_all(config->telnet_options); |
| curl_slist_free_all(config->mail_rcpt); |
| curl_slist_free_all(config->resolve); |
| |
| if(config->easy) |
| curl_easy_cleanup(config->easy); |
| } |
| |
| #ifdef WIN32 |
| |
| /* Function to find CACert bundle on a Win32 platform using SearchPath. |
| * (SearchPath is already declared via inclusions done in setup header file) |
| * (Use the ASCII version instead of the unicode one!) |
| * The order of the directories it searches is: |
| * 1. application's directory |
| * 2. current working directory |
| * 3. Windows System directory (e.g. C:\windows\system32) |
| * 4. Windows Directory (e.g. C:\windows) |
| * 5. all directories along %PATH% |
| */ |
| static void FindWin32CACert(struct Configurable *config, |
| const char *bundle_file) |
| { |
| /* only check for cert file if "we" support SSL */ |
| if(curlinfo->features & CURL_VERSION_SSL) { |
| DWORD buflen; |
| char *ptr = NULL; |
| char *retval = malloc(sizeof (TCHAR) * (MAX_PATH + 1)); |
| if(!retval) |
| return; |
| retval[0] = '\0'; |
| buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr); |
| if(buflen > 0) { |
| GetStr(&config->cacert, retval); |
| } |
| free(retval); |
| } |
| } |
| |
| #endif |
| |
| #define RETRY_SLEEP_DEFAULT 1000 /* ms */ |
| #define RETRY_SLEEP_MAX 600000 /* 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 */ |
| } |
| |
| #define my_setopt(x,y,z) _my_setopt(x, FALSE, config, #y, y, z) |
| #define my_setopt_str(x,y,z) _my_setopt(x, TRUE, config, #y, y, z) |
| |
| static struct curl_slist *easycode; |
| static struct curl_slist *easycode_remarks; |
| |
| static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config, |
| const char *name, CURLoption tag, ...); |
| |
| static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config, |
| const char *name, CURLoption tag, ...) |
| { |
| va_list arg; |
| CURLcode ret; |
| char *bufp; |
| char value[256]; |
| bool remark=FALSE; |
| bool skip=FALSE; |
| |
| va_start(arg, tag); |
| |
| if(tag < CURLOPTTYPE_OBJECTPOINT) { |
| long lval = va_arg(arg, long); |
| snprintf(value, sizeof(value), "%ldL", lval); |
| ret = curl_easy_setopt(curl, tag, lval); |
| if(!lval) |
| skip = TRUE; |
| } |
| else if(tag < CURLOPTTYPE_OFF_T) { |
| void *pval = va_arg(arg, void *); |
| unsigned char *ptr = (unsigned char *)pval; |
| |
| /* function pointers are never printable */ |
| if(tag >= CURLOPTTYPE_FUNCTIONPOINT) { |
| if(pval) { |
| strcpy(value, "functionpointer"); /* 'value' fits 256 bytes */ |
| remark = TRUE; |
| } |
| else |
| skip = TRUE; |
| } |
| |
| else if(pval && str) |
| snprintf(value, sizeof(value), "\"%s\"", (char *)ptr); |
| else if(pval) { |
| strcpy(value, "objectpointer"); /* 'value' fits 256 bytes */ |
| remark = TRUE; |
| } |
| else |
| skip = TRUE; |
| |
| ret = curl_easy_setopt(curl, tag, pval); |
| |
| } |
| else { |
| curl_off_t oval = va_arg(arg, curl_off_t); |
| snprintf(value, sizeof(value), |
| "(curl_off_t)%" CURL_FORMAT_CURL_OFF_T, oval); |
| ret = curl_easy_setopt(curl, tag, oval); |
| |
| if(!oval) |
| skip = TRUE; |
| } |
| |
| if(config->libcurl && !skip) { |
| /* we only use this for real if --libcurl was used */ |
| |
| if(remark) |
| bufp = curlx_maprintf("%s set to a %s", name, value); |
| else |
| bufp = curlx_maprintf("curl_easy_setopt(hnd, %s, %s);", name, value); |
| |
| if(!bufp) |
| ret = CURLE_OUT_OF_MEMORY; |
| else { |
| struct curl_slist *list = |
| curl_slist_append(remark?easycode_remarks:easycode, bufp); |
| |
| if(remark) |
| easycode_remarks = list; |
| else |
| easycode = list; |
| } |
| if(bufp) |
| curl_free(bufp); |
| } |
| va_end(arg); |
| |
| return ret; |
| } |
| |
| static const char * const srchead[]={ |
| "/********* Sample code generated by the curl command line tool **********", |
| " * All curl_easy_setopt() options are documented at:", |
| " * http://curl.haxx.se/libcurl/c/curl_easy_setopt.html", |
| " ************************************************************************/", |
| "#include <curl/curl.h>", |
| "", |
| "int main(int argc, char *argv[])", |
| "{", |
| " CURLcode ret;", |
| NULL |
| }; |
| |
| static void dumpeasycode(struct Configurable *config) |
| { |
| struct curl_slist *ptr; |
| char *o = config->libcurl; |
| |
| if(o) { |
| FILE *out; |
| bool fopened = FALSE; |
| if(strcmp(o, "-")) { |
| out = fopen(o, "wt"); |
| fopened = TRUE; |
| } |
| else |
| out= stdout; |
| if(!out) |
| warnf(config, "Failed to open %s to write libcurl code!\n", o); |
| else { |
| int i; |
| const char *c; |
| |
| for(i=0; ((c = srchead[i]) != '\0'); i++) |
| fprintf(out, "%s\n", c); |
| |
| ptr = easycode; |
| while(ptr) { |
| fprintf(out, " %s\n", ptr->data); |
| ptr = ptr->next; |
| } |
| |
| ptr = easycode_remarks; |
| if(ptr) { |
| fprintf(out, |
| "\n /* Here is a list of options the curl code" |
| " used that cannot get generated\n" |
| " as source easily. You may select to either" |
| " not use them or implement\n them yourself.\n" |
| "\n"); |
| while(ptr) { |
| fprintf(out, " %s\n", ptr->data); |
| ptr = ptr->next; |
| } |
| fprintf(out, "\n */\n"); |
| } |
| |
| fprintf(out, |
| " return (int)ret;\n" |
| "}\n" |
| "/**** End of sample code ****/\n"); |
| if(fopened) |
| fclose(out); |
| } |
| } |
| curl_slist_free_all(easycode); |
| } |
| |
| static bool stdin_upload(const char *uploadfile) |
| { |
| return (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) { |
| free(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); |
| |
| free(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; |
| } |
| |
| /* |
| * Copies a file name part and returns an ALLOCATED data buffer. |
| */ |
| static char* |
| parse_filename(char *ptr, size_t len) |
| { |
| char* copy; |
| char* p; |
| char* q; |
| char stop = 0; |
| |
| /* simple implementation of strndup() */ |
| copy = malloc(len+1); |
| if(!copy) |
| return NULL; |
| memcpy(copy, ptr, len); |
| copy[len] = 0; |
| |
| p = copy; |
| if(*p == '\'' || *p == '"') { |
| /* store the starting quote */ |
| stop = *p; |
| p++; |
| } |
| else |
| stop = ';'; |
| |
| /* if the filename contains a path, only use filename portion */ |
| q = strrchr(copy, '/'); |
| if(q) { |
| p=q+1; |
| if(!*p) { |
| free(copy); |
| return NULL; |
| } |
| } |
| |
| /* If the filename contains a backslash, only use filename portion. The idea |
| is that even systems that don't handle backslashes as path separators |
| probably want the path removed for convenience. */ |
| q = strrchr(p, '\\'); |
| if(q) { |
| p = q+1; |
| if(!*p) { |
| free(copy); |
| return NULL; |
| } |
| } |
| |
| /* scan for the end letter and stop there */ |
| q = p; |
| while(*q) { |
| if(q[1] && q[0]=='\\') |
| q++; |
| else if(q[0] == stop) |
| break; |
| q++; |
| } |
| *q = 0; |
| |
| /* make sure the file name doesn't end in \r or \n */ |
| q = strchr(p, '\r'); |
| if(q) |
| *q = 0; |
| |
| q = strchr(p, '\n'); |
| if(q) |
| *q = 0; |
| |
| if(copy!=p) |
| memmove(copy, p, strlen(p)+1); |
| |
| /* in case we built curl debug enabled, we allow an evironment variable |
| * named CURL_TESTDIR to prefix the given file name to put it into a |
| * specific directory |
| */ |
| #ifdef CURLDEBUG |
| { |
| char *tdir = curlx_getenv("CURL_TESTDIR"); |
| if(tdir) { |
| char buffer[512]; /* suitably large */ |
| snprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy); |
| free(copy); |
| copy = strdup(buffer); /* clone the buffer, we don't use the libcurl |
| aprintf() or similar since we want to use the |
| same memory code as the "real" parse_filename |
| function */ |
| curl_free(tdir); |
| } |
| } |
| #endif |
| |
| return copy; |
| } |
| |
| static size_t |
| header_callback(void *ptr, size_t size, size_t nmemb, void *stream) |
| { |
| struct OutStruct* outs = (struct OutStruct*)stream; |
| const char* str = (char*)ptr; |
| const size_t cb = size*nmemb; |
| const char* end = (char*)ptr + cb; |
| |
| if(cb > 20 && checkprefix("Content-disposition:", str)) { |
| char *p = (char*)str + 20; |
| |
| /* look for the 'filename=' parameter |
| (encoded filenames (*=) are not supported) */ |
| for(;;) { |
| char *filename; |
| size_t len; |
| |
| while(*p && (p < end) && !ISALPHA(*p)) |
| p++; |
| if(p > end-9) |
| break; |
| |
| if(memcmp(p, "filename=", 9)) { |
| /* no match, find next parameter */ |
| while((p < end) && (*p != ';')) |
| p++; |
| continue; |
| } |
| p+=9; |
| |
| /* this expression below typecasts 'cb' only to avoid |
| warning: signed and unsigned type in conditional expression |
| */ |
| len = (ssize_t)cb - (p - str); |
| filename = parse_filename(p, len); |
| if(filename) { |
| outs->filename = filename; |
| outs->alloc_filename = TRUE; |
| break; |
| } |
| } |
| } |
| |
| return cb; |
| } |
| |
| #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]; |
| char useragent[256]; /* buah, we don't want a larger default user agent */ |
| struct ProgressData progressbar; |
| struct getout *urlnode; |
| struct getout *nextnode; |
| |
| struct OutStruct outs; |
| struct OutStruct heads; |
| struct InStruct input; |
| |
| URLGlob *urls=NULL; |
| URLGlob *inglob=NULL; |
| int urlnum; |
| int infilenum; |
| char *uploadfile=NULL; /* a single file, never a glob */ |
| |
| curl_off_t uploadfilesize; /* -1 means unknown */ |
| bool stillflags=TRUE; |
| |
| bool allocuseragent=FALSE; |
| |
| char *httpgetfields=NULL; |
| |
| CURL *curl; |
| int res = 0; |
| int i; |
| long retry_sleep_default; |
| long retry_sleep; |
| |
| char *env; |
| |
| memset(&heads, 0, sizeof(struct OutStruct)); |
| |
| memory_tracking_init(); |
| |
| /* Initialize curl library - do not call any libcurl functions before. |
| 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 a curl handle to use for all forthcoming curl transfers. Cleanup |
| * when all transfers are done. |
| */ |
| curl = curl_easy_init(); |
| if(!curl) { |
| clean_getout(config); |
| return CURLE_FAILED_INIT; |
| } |
| config->easy = curl; |
| |
| memset(&outs,0,sizeof(outs)); |
| |
| config->outs = &outs; |
| |
| /* we get libcurl info right away */ |
| curlinfo = curl_version_info(CURLVERSION_NOW); |
| |
| errorbuffer[0]=0; /* prevent junk from being output */ |
| |
| /* 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); |
| return CURLE_FAILED_INIT; |
| } |
| |
| /* Parse options */ |
| for(i = 1; i < argc; i++) { |
| if(stillflags && |
| ('-' == argv[i][0])) { |
| char *nextarg; |
| bool passarg; |
| char *origopt=argv[i]; |
| |
| char *flag = argv[i]; |
| |
| if(curlx_strequal("--", argv[i])) |
| /* 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; |
| } |
| clean_getout(config); |
| return retval; |
| } |
| |
| 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) |
| return res; |
| } |
| } |
| |
| retry_sleep_default = config->retry_delay? |
| config->retry_delay*1000:RETRY_SLEEP_DEFAULT; /* ms */ |
| retry_sleep = retry_sleep_default; |
| |
| if((!config->url_list || !config->url_list->url) && !config->list_engines) { |
| clean_getout(config); |
| helpf(config->errors, "no URL specified!\n"); |
| return CURLE_FAILED_INIT; |
| } |
| if(NULL == config->useragent) { |
| /* set non-zero default values: */ |
| snprintf(useragent, sizeof(useragent), |
| CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version()); |
| config->useragent= useragent; |
| } |
| else |
| allocuseragent = TRUE; |
| |
| /* 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) { |
| env = curlx_getenv("CURL_CA_BUNDLE"); |
| if(env) |
| GetStr(&config->cacert, env); |
| else { |
| env = curlx_getenv("SSL_CERT_DIR"); |
| if(env) |
| GetStr(&config->capath, env); |
| else { |
| env = curlx_getenv("SSL_CERT_FILE"); |
| if(env) |
| GetStr(&config->cacert, env); |
| } |
| } |
| |
| if(env) |
| curl_free(env); |
| #ifdef WIN32 |
| else |
| FindWin32CACert(config, "curl-ca-bundle.crt"); |
| #endif |
| } |
| |
| if(config->postfields) { |
| if(config->use_httpget) { |
| /* Use the postfields data for a http get */ |
| httpgetfields = strdup(config->postfields); |
| free(config->postfields); |
| config->postfields = NULL; |
| if(SetHTTPrequest(config, |
| (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET), |
| &config->httpreq)) { |
| free(httpgetfields); |
| return PARAM_BAD_USE; |
| } |
| } |
| else { |
| if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq)) |
| return PARAM_BAD_USE; |
| } |
| } |
| |
| /* This is the first entry added to easycode and it initializes the slist */ |
| easycode = curl_slist_append(easycode, "CURL *hnd = curl_easy_init();"); |
| if(!easycode) { |
| clean_getout(config); |
| res = CURLE_OUT_OF_MEMORY; |
| goto quit_curl; |
| } |
| |
| if(config->list_engines) { |
| struct curl_slist *engines = NULL; |
| |
| curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines); |
| list_engines(engines); |
| curl_slist_free_all(engines); |
| res = CURLE_OK; |
| goto quit_curl; |
| } |
| |
| /* After this point, we should call curl_easy_cleanup() if we decide to bail |
| * out from this function! */ |
| |
| urlnode = config->url_list; |
| |
| if(config->headerfile) { |
| /* open file for output: */ |
| if(strcmp(config->headerfile,"-")) { |
| heads.filename = config->headerfile; |
| heads.alloc_filename = FALSE; |
| } |
| else |
| heads.stream=stdout; |
| heads.config = config; |
| } |
| |
| /* loop through the list of given URLs */ |
| while(urlnode) { |
| int up; /* upload file counter within a single upload glob */ |
| char *dourl; |
| char *url; |
| char *infiles; /* might be a glob pattern */ |
| char *outfiles=NULL; |
| |
| /* get the full URL (it might be NULL) */ |
| dourl=urlnode->url; |
| |
| url = dourl; |
| |
| if(NULL == url) { |
| /* This node had no URL, skip it and continue to the next */ |
| if(urlnode->outfile) |
| free(urlnode->outfile); |
| |
| /* move on to the next URL */ |
| nextnode=urlnode->next; |
| free(urlnode); /* free the node */ |
| urlnode = nextnode; |
| continue; /* next please */ |
| } |
| |
| /* default output stream is stdout */ |
| outs.stream = stdout; |
| outs.config = config; |
| outs.bytes = 0; /* nothing written yet */ |
| outs.filename = NULL; |
| |
| /* save outfile pattern before expansion */ |
| if(urlnode->outfile) { |
| outfiles = strdup(urlnode->outfile); |
| if(!outfiles) { |
| clean_getout(config); |
| 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 != CURLE_OK) { |
| clean_getout(config); |
| if(outfiles) |
| free(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 && !infiles) || |
| ((uploadfile = inglob? |
| glob_next_url(inglob): |
| (!up?strdup(infiles):NULL)) != NULL); |
| up++) { |
| int separator = 0; |
| long retry_numretries; |
| uploadfilesize=-1; |
| |
| if(!config->globoff) { |
| /* Unless explicitly shut off, we expand '{...}' and '[...]' |
| expressions and return total number of URLs in pattern set */ |
| res = glob_url(&urls, dourl, &urlnum, |
| config->showerror?config->errors:NULL); |
| if(res != CURLE_OK) { |
| 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; |
| ((url = urls?glob_next_url(urls):(i?NULL:strdup(url))) != NULL); |
| i++) { |
| /* NOTE: In the condition expression in the for() statement above, the |
| 'url' variable is only ever strdup()ed if(i == 0) and thus never |
| when this loops later on. Further down in this function we call |
| free(url) and then the code loops. Static code parsers may thus get |
| tricked into believing that we have a potential access-after-free |
| here. I can however not spot any such case. */ |
| |
| int infd = STDIN_FILENO; |
| bool infdopen; |
| char *outfile; |
| struct timeval retrystart; |
| outfile = outfiles?strdup(outfiles):NULL; |
| |
| if((urlnode->flags&GETOUT_USEREMOTE) || |
| (outfile && !curlx_strequal("-", outfile)) ) { |
| |
| /* |
| * 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(url); |
| if((!outfile || !*outfile) && !config->content_disposition) { |
| helpf(config->errors, "Remote file name has no length!\n"); |
| res = CURLE_WRITE_ERROR; |
| free(url); |
| break; |
| } |
| #if defined(MSDOS) || defined(WIN32) |
| /* 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; |
| break; |
| } |
| #endif /* MSDOS || WIN32 */ |
| } |
| else if(urls) { |
| /* fill '#1' ... '#9' terms from URL pattern */ |
| char *storefile = outfile; |
| outfile = glob_match_url(storefile, urls); |
| free(storefile); |
| if(!outfile) { |
| /* bad globbing */ |
| warnf(config, "bad output glob!\n"); |
| free(url); |
| res = CURLE_FAILED_INIT; |
| break; |
| } |
| } |
| |
| /* Create the directory hierarchy, if not pre-existent to a multiple |
| file output call */ |
| |
| if(config->create_dirs && |
| (-1 == create_dir_hierarchy(outfile, config->errors))) { |
| free(url); |
| res = CURLE_WRITE_ERROR; |
| break; |
| } |
| |
| 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; |
| } |
| |
| outs.filename = outfile; |
| outs.alloc_filename = FALSE; |
| |
| if(config->resume_from) { |
| outs.init = config->resume_from; |
| /* open file for output: */ |
| outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb"); |
| if(!outs.stream) { |
| helpf(config->errors, "Can't open '%s'!\n", outfile); |
| free(url); |
| res = CURLE_WRITE_ERROR; |
| break; |
| } |
| } |
| else { |
| outs.stream = NULL; /* open when needed */ |
| outs.bytes = 0; /* reset byte counter */ |
| } |
| } |
| infdopen=FALSE; |
| if(uploadfile && !stdin_upload(uploadfile)) { |
| /* |
| * We have specified a file to upload and it isn't "-". |
| */ |
| struct_stat fileinfo; |
| |
| url = add_file_name_to_url(curl, url, uploadfile); |
| if(!url) { |
| helpf(config->errors, "out of memory\n"); |
| res = CURLE_OUT_OF_MEMORY; |
| break; |
| } |
| /* 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); |
| |
| /* Free the list of remaining URLs and globbed upload files |
| * to force curl to exit immediately |
| */ |
| if(urls) { |
| glob_cleanup(urls); |
| urls = NULL; |
| } |
| if(inglob) { |
| glob_cleanup(inglob); |
| inglob = NULL; |
| } |
| |
| 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"); |
| } |
| |
| SET_BINMODE(stdin); |
| infd = STDIN_FILENO; |
| if(curlx_strequal(uploadfile, ".")) { |
| if(curlx_nonblock((curl_socket_t)infd, TRUE) < 0) |
| warnf(config, |
| "fcntl failed on fd=%d: %s\n", infd, strerror(errno)); |
| } |
| } |
| |
| if(uploadfile && config->resume_from_current) |
| config->resume_from = -1; /* -1 will then force get-it-yourself */ |
| |
| if(output_expected(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, url, outfile ? outfile : "<stdout>"); |
| if(separator) |
| printf("%s%s\n", CURLseparator, url); |
| } |
| if(httpgetfields) { |
| char *urlbuffer; |
| /* Find out whether the url contains a file name */ |
| const char *pc =strstr(url, "://"); |
| char sep='?'; |
| if(pc) |
| pc+=3; |
| else |
| pc=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(url) + strlen(httpgetfields) + 3); |
| if(!urlbuffer) { |
| helpf(config->errors, "out of memory\n"); |
| |
| /* Free the list of remaining URLs and globbed upload files |
| * to force curl to exit immediately |
| */ |
| if(urls) { |
| glob_cleanup(urls); |
| urls = NULL; |
| } |
| if(inglob) { |
| glob_cleanup(inglob); |
| inglob = NULL; |
| } |
| |
| res = CURLE_OUT_OF_MEMORY; |
| goto quit_urls; |
| } |
| if(pc) |
| sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields); |
| else |
| /* Append / before the ? to create a well-formed url |
| if the url contains a hostname only |
| */ |
| sprintf(urlbuffer, "%s/?%s", url, httpgetfields); |
| |
| free(url); /* free previous URL */ |
| 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, my_fwrite); |
| |
| /* 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:", url)) |
| my_setopt(curl, CURLOPT_READFUNCTION, my_fread); |
| |
| /* 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, my_seek); |
| |
| 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, url); /* what to fetch */ |
| my_setopt_str(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */ |
| if(config->proxy) |
| my_setopt(curl, CURLOPT_PROXYTYPE, config->proxyver); |
| my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress); |
| if(config->no_body) { |
| my_setopt(curl, CURLOPT_NOBODY, 1); |
| my_setopt(curl, CURLOPT_HEADER, 1); |
| } |
| else |
| my_setopt(curl, CURLOPT_HEADER, config->include_headers); |
| |
| my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror); |
| my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE); |
| my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); |
| my_setopt(curl, CURLOPT_APPEND, config->ftp_append); |
| |
| if(config->netrc_opt) |
| my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); |
| else if(config->netrc || 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_FOLLOWLOCATION, config->followlocation); |
| my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, config->unrestricted_auth); |
| my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); |
| my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd); |
| my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); |
| my_setopt(curl, CURLOPT_NOPROXY, config->noproxy); |
| my_setopt_str(curl, CURLOPT_RANGE, config->range); |
| my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); |
| my_setopt(curl, CURLOPT_TIMEOUT, config->timeout); |
| |
| switch(config->httpreq) { |
| case HTTPREQ_SIMPLEPOST: |
| my_setopt_str(curl, CURLOPT_POSTFIELDS, config->postfields); |
| my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, config->postfieldsize); |
| break; |
| case HTTPREQ_POST: |
| my_setopt(curl, CURLOPT_HTTPPOST, config->httppost); |
| break; |
| default: |
| break; |
| } |
| my_setopt_str(curl, CURLOPT_REFERER, config->referer); |
| my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer); |
| my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent); |
| my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); |
| my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, |
| config->low_speed_limit); |
| my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); |
| my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, |
| config->sendpersecond); |
| my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, |
| config->recvpersecond); |
| my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, |
| config->use_resume?config->resume_from:0); |
| my_setopt_str(curl, CURLOPT_COOKIE, config->cookie); |
| my_setopt(curl, CURLOPT_HTTPHEADER, config->headers); |
| my_setopt(curl, CURLOPT_SSLCERT, config->cert); |
| my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type); |
| my_setopt(curl, CURLOPT_SSLKEY, config->key); |
| my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type); |
| my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd); |
| |
| /* SSH private key uses the same command-line option as SSL private |
| key */ |
| my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); |
| my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); |
| |
| /* 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); |
| |
| /* default to strict verifyhost */ |
| /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); */ |
| if(config->cacert || config->capath) { |
| if(config->cacert) |
| my_setopt_str(curl, CURLOPT_CAINFO, config->cacert); |
| |
| if(config->capath) |
| my_setopt_str(curl, CURLOPT_CAPATH, config->capath); |
| my_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE); |
| } |
| if(config->crlfile) |
| my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile); |
| if(config->insecure_ok) { |
| /* new stuff needed for libcurl 7.10 */ |
| my_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); |
| my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1); |
| } |
| else { |
| char *home = homedir(); |
| char *file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR); |
| if(home) |
| free(home); |
| |
| if(file) { |
| my_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file); |
| curl_free(file); |
| } |
| else { |
| /* Free the list of remaining URLs and globbed upload files |
| * to force curl to exit immediately |
| */ |
| if(urls) { |
| glob_cleanup(urls); |
| urls = NULL; |
| } |
| if(inglob) { |
| glob_cleanup(inglob); |
| inglob = NULL; |
| } |
| |
| res = CURLE_OUT_OF_MEMORY; |
| goto quit_urls; |
| } |
| } |
| |
| if(config->no_body || config->remote_time) { |
| /* no body or use remote time */ |
| my_setopt(curl, CURLOPT_FILETIME, TRUE); |
| } |
| |
| my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); |
| my_setopt(curl, CURLOPT_CRLF, config->crlf); |
| my_setopt(curl, CURLOPT_QUOTE, config->quote); |
| my_setopt(curl, CURLOPT_POSTQUOTE, config->postquote); |
| my_setopt(curl, CURLOPT_PREQUOTE, config->prequote); |
| my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile); |
| /* cookie jar was added in 7.9 */ |
| if(config->cookiejar) |
| my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar); |
| /* cookie session added in 7.9.7 */ |
| my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession); |
| |
| my_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version); |
| my_setopt(curl, CURLOPT_TIMECONDITION, config->timecond); |
| my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime); |
| my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); |
| my_setopt(curl, CURLOPT_STDERR, config->errors); |
| |
| /* three new ones in libcurl 7.3: */ |
| my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); |
| my_setopt_str(curl, CURLOPT_INTERFACE, config->iface); |
| my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel); |
| |
| progressbarinit(&progressbar, config); |
| if((config->progressmode == CURL_PROGRESS_BAR) && |
| !config->noprogress && !config->mute) { |
| /* we want the alternative style, then we have to implement it |
| ourselves! */ |
| my_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress); |
| 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); |
| |
| if(config->httpversion) |
| my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion); |
| |
| /* 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); |
| |
| /* new in libcurl 7.10.6 (default is Basic) */ |
| if(config->authtype) |
| my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype); |
| |
| if(config->tracetype != TRACE_NONE) { |
| my_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); |
| my_setopt(curl, CURLOPT_DEBUGDATA, config); |
| my_setopt(curl, CURLOPT_VERBOSE, TRUE); |
| } |
| |
| res = CURLE_OK; |
| |
| /* new in curl ?? */ |
| if(config->engine) { |
| res = my_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); |
| my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1); |
| } |
| |
| if(res != CURLE_OK) |
| goto show_error; |
| |
| if(config->encoding) |
| my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, ""); |
| |
| if(config->tr_encoding) |
| my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 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); |
| 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 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); |
| |
| /* new in curl 7.11.1, modified in 7.15.2 */ |
| if(config->socksproxy) { |
| my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy); |
| my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver); |
| } |
| |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) |
| /* 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, sockoptcallback); |
| my_setopt(curl, CURLOPT_SOCKOPTDATA, config); |
| } |
| |
| /* 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)); |
| |
| /* 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, header_callback); |
| 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 */ |
| my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); |
| my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password); |
| |
| /* new in 7.22.0 */ |
| if(config->gssapi_delegation) |
| my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION, |
| config->gssapi_delegation); |
| |
| retry_numretries = config->req_retry; |
| |
| retrystart = cutil_tvnow(); |
| |
| for(;;) { |
| res = curl_easy_perform(curl); |
| if(!curl_slist_append(easycode, "ret = curl_easy_perform(hnd);")) { |
| res = CURLE_OUT_OF_MEMORY; |
| break; |
| } |
| |
| if(config->content_disposition && outs.stream && !config->mute && |
| outs.filename) |
| printf("curl: Saved to filename '%s'\n", outs.filename); |
| |
| /* if retry-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*1000)) ) { |
| 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 *this_url=NULL; |
| curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url); |
| if(this_url && |
| checkprefix("http", this_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/1000, retry_numretries); |
| |
| go_sleep(retry_sleep); |
| retry_numretries--; |
| if(!config->retry_delay) { |
| retry_sleep *= 2; |
| if(retry_sleep > RETRY_SLEEP_MAX) |
| retry_sleep = RETRY_SLEEP_MAX; |
| } |
| if(outs.bytes && outs.filename) { |
| /* 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"); |
| break; |
| } |
| /* 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; |
| } |
| } /* if retry_numretries */ |
| |
| /* In all ordinary cases, just break out of loop here */ |
| retry_sleep = retry_sleep_default; |
| break; |
| |
| } |
| |
| 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 |
| |
| show_error: |
| |
| #ifdef __VMS |
| if(is_vms_shell()) { |
| /* VMS DCL shell behavior */ |
| if(!config->showerror) { |
| vms_show = VMSSTS_HIDE; |
| } |
| } |
| else |
| #endif |
| { |
| if((res!=CURLE_OK) && config->showerror) { |
| fprintf(config->errors, "curl: (%d) %s\n", res, |
| errorbuffer[0]? errorbuffer: |
| curl_easy_strerror((CURLcode)res)); |
| if(CURLE_SSL_CACERT == res) { |
| fprintf(config->errors, "%s%s", |
| CURL_CA_CERT_ERRORMSG1, |
| CURL_CA_CERT_ERRORMSG2 ); |
| } |
| } |
| } |
| if(outfile && !curlx_strequal(outfile, "-") && outs.stream) { |
| int rc; |
| |
| if(config->xattr) { |
| rc = fwrite_xattr(curl, fileno(outs.stream) ); |
| if(rc) |
| warnf(config, "Error setting extended attributes: %s\n", |
| strerror(errno) ); |
| } |
| if(outs.alloc_filename) |
| free(outs.filename); |
| |
| 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); |
| } |
| } |
| |
| #ifdef HAVE_UTIME |
| /* Important that we set the time _after_ the file has been |
| closed, as is done above here */ |
| if(config->remote_time && outs.filename) { |
| /* ask libcurl if we got a time. Pretty please */ |
| long filetime; |
| curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); |
| if(filetime >= 0) { |
| struct utimbuf times; |
| times.actime = (time_t)filetime; |
| times.modtime = (time_t)filetime; |
| utime(outs.filename, ×); /* set the time we got */ |
| } |
| } |
| #endif |
| #ifdef __AMIGA__ |
| /* Set the url as comment for the file. (up to 80 chars are allowed) |
| */ |
| if(strlen(url) > 78) |
| url[79] = '\0'; |
| |
| SetComment( outs.filename, url); |
| #endif |
| |
| quit_urls: |
| if(url) |
| free(url); |
| |
| if(outfile) |
| free(outfile); |
| |
| if(infdopen) |
| close(infd); |
| |
| } /* loop to the next URL */ |
| |
| if(urls) { |
| /* cleanup memory used for URL globbing patterns */ |
| glob_cleanup(urls); |
| urls = NULL; |
| } |
| |
| if(uploadfile) |
| free(uploadfile); |
| |
| } /* loop to the next globbed upload file */ |
| |
| if(inglob) { |
| glob_cleanup(inglob); |
| inglob = NULL; |
| } |
| |
| if(outfiles) |
| free(outfiles); |
| |
| /* empty this urlnode struct */ |
| if(urlnode->url) |
| free(urlnode->url); |
| if(urlnode->outfile) |
| free(urlnode->outfile); |
| if(urlnode->infile) |
| free(urlnode->infile); |
| |
| /* move on to the next URL */ |
| nextnode=urlnode->next; |
| free(urlnode); /* free the node */ |
| urlnode = nextnode; |
| |
| } /* while-loop through all URLs */ |
| |
| quit_curl: |
| if(httpgetfields) |
| free(httpgetfields); |
| |
| if(config->engine) |
| free(config->engine); |
| |
| /* cleanup the curl handle! */ |
| curl_easy_cleanup(curl); |
| config->easy = NULL; /* cleanup now */ |
| if(easycode) |
| curl_slist_append(easycode, "curl_easy_cleanup(hnd);"); |
| |
| if(heads.stream && (heads.stream != stdout)) |
| fclose(heads.stream); |
| |
| if(allocuseragent) |
| free(config->useragent); |
| |
| if(config->trace_fopened && config->trace_stream) |
| fclose(config->trace_stream); |
| |
| /* 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 */ |
| dumpeasycode(config); |
| |
| if(config->errors_fopened) |
| 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) { |
| free(retval); |
| return NULL; |
| } |
| retval = ptr; |
| strcat(retval, buf); |
| } |
| } |
| while(NULL == (nl = strchr(retval, '\n'))); |
| |
| if(NULL != nl) |
| *nl = '\0'; |
| |
| return retval; |
| } |
| |
| static void show_dir_errno(FILE *errors, const char *name) |
| { |
| switch (ERRNO) { |
| #ifdef EACCES |
| case EACCES: |
| fprintf(errors,"You don't have permission to create %s.\n", name); |
| break; |
| #endif |
| #ifdef ENAMETOOLONG |
| case ENAMETOOLONG: |
| fprintf(errors,"The directory name %s is too long.\n", name); |
| break; |
| #endif |
| #ifdef EROFS |
| case EROFS: |
| fprintf(errors,"%s resides on a read-only file system.\n", name); |
| break; |
| #endif |
| #ifdef ENOSPC |
| case ENOSPC: |
| fprintf(errors,"No space left on the file system that will " |
| "contain the directory %s.\n", name); |
| break; |
| #endif |
| #ifdef EDQUOT |
| case EDQUOT: |
| fprintf(errors,"Cannot create directory %s because you " |
| "exceeded your quota.\n", name); |
| break; |
| #endif |
| default : |
| fprintf(errors,"Error creating directory %s.\n", name); |
| break; |
| } |
| } |
| |
| /* Create the needed directory hierarchy recursively in order to save |
| multi-GETs in file output, ie: |
| curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt" |
| should create all the dir* automagically |
| */ |
| static int create_dir_hierarchy(const char *outfile, FILE *errors) |
| { |
| char *tempdir; |
| char *tempdir2; |
| char *outdup; |
| char *dirbuildup; |
| int result=0; |
| |
| outdup = strdup(outfile); |
| if(!outdup) |
| return -1; |
| |
| dirbuildup = malloc(sizeof(char) * strlen(outfile)); |
| if(!dirbuildup) { |
| free(outdup); |
| return -1; |
| } |
| dirbuildup[0] = '\0'; |
| |
| tempdir = strtok(outdup, DIR_CHAR); |
| |
| while(tempdir != NULL) { |
| tempdir2 = strtok(NULL, DIR_CHAR); |
| /* since strtok returns a token for the last word even |
| if not ending with DIR_CHAR, we need to prune it */ |
| if(tempdir2 != NULL) { |
| size_t dlen = strlen(dirbuildup); |
| if(dlen) |
| sprintf(&dirbuildup[dlen], "%s%s", DIR_CHAR, tempdir); |
| else { |
| if(0 != strncmp(outdup, DIR_CHAR, 1)) |
| strcpy(dirbuildup, tempdir); |
| else |
| sprintf(dirbuildup, "%s%s", DIR_CHAR, tempdir); |
| } |
| if(access(dirbuildup, F_OK) == -1) { |
| result = mkdir(dirbuildup,(mode_t)0000750); |
| if(-1 == result) { |
| show_dir_errno(errors, dirbuildup); |
| break; /* get out of loop */ |
| } |
| } |
| } |
| tempdir = tempdir2; |
| } |
| free(dirbuildup); |
| free(outdup); |
| |
| return result; /* 0 is fine, -1 is badness */ |
| } |
| |
| #if defined(MSDOS) || defined(WIN32) |
| |
| #ifndef HAVE_BASENAME |
| /* basename() returns a pointer to the last component of a pathname. |
| * Ripped from lib/formdata.c. |
| */ |
| static char *Curl_basename(char *path) |
| { |
| /* Ignore all the details above for now and make a quick and simple |
| implementaion here */ |
| char *s1; |
| char *s2; |
| |
| s1=strrchr(path, '/'); |
| s2=strrchr(path, '\\'); |
| |
| if(s1 && s2) { |
| path = (s1 > s2? s1 : s2)+1; |
| } |
| else if(s1) |
| path = s1 + 1; |
| else if(s2) |
| path = s2 + 1; |
| |
| return path; |
| } |
| #define basename(x) Curl_basename((x)) |
| #endif /* HAVE_BASENAME */ |
| |
| /* The following functions are taken with modification from the DJGPP |
| * port of tar 1.12. They use algorithms originally from DJTAR. */ |
| |
| static const char * |
| msdosify (const char *file_name) |
| { |
| static char dos_name[PATH_MAX]; |
| static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ |
| "|<>\\\":?*"; /* illegal in DOS & W95 */ |
| static const char *illegal_chars_w95 = &illegal_chars_dos[8]; |
| int idx, dot_idx; |
| const char *s = file_name; |
| char *d = dos_name; |
| const char * const dlimit = dos_name + sizeof(dos_name) - 1; |
| const char *illegal_aliens = illegal_chars_dos; |
| size_t len = sizeof (illegal_chars_dos) - 1; |
| |
| /* Support for Windows 9X VFAT systems, when available. */ |
| if(_use_lfn (file_name)) { |
| illegal_aliens = illegal_chars_w95; |
| len -= (illegal_chars_w95 - illegal_chars_dos); |
| } |
| |
| /* Get past the drive letter, if any. */ |
| if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { |
| *d++ = *s++; |
| *d++ = *s++; |
| } |
| |
| for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { |
| if(memchr (illegal_aliens, *s, len)) { |
| /* Dots are special: DOS doesn't allow them as the leading character, |
| and a file name cannot have more than a single dot. We leave the |
| first non-leading dot alone, unless it comes too close to the |
| beginning of the name: we want sh.lex.c to become sh_lex.c, not |
| sh.lex-c. */ |
| if(*s == '.') { |
| if(idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) { |
| /* Copy "./" and "../" verbatim. */ |
| *d++ = *s++; |
| if(*s == '.') |
| *d++ = *s++; |
| *d = *s; |
| } |
| else if(idx == 0) |
| *d = '_'; |
| else if(dot_idx >= 0) { |
| if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ |
| d[dot_idx - idx] = '_'; /* replace previous dot */ |
| *d = '.'; |
| } |
| else |
| *d = '-'; |
| } |
| else |
| *d = '.'; |
| |
| if(*s == '.') |
| dot_idx = idx; |
| } |
| else if(*s == '+' && s[1] == '+') { |
| if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ |
| *d++ = 'x'; |
| *d = 'x'; |
| } |
| else { |
| /* libg++ etc. */ |
| memcpy (d, "plus", 4); |
| d += 3; |
| } |
| s++; |
| idx++; |
| } |
| else |
| *d = '_'; |
| } |
| else |
| *d = *s; |
| if(*s == '/') { |
| idx = 0; |
| dot_idx = -1; |
| } |
| else |
| idx++; |
| } |
| |
| *d = '\0'; |
| return dos_name; |
| } |
| |
| static char * |
| rename_if_dos_device_name (char *file_name) |
| { |
| /* We could have a file whose name is a device on MS-DOS. Trying to |
| * retrieve such a file would fail at best and wedge us at worst. We need |
| * to rename such files. */ |
| char *base; |
| struct_stat st_buf; |
| char fname[PATH_MAX]; |
| |
| strncpy(fname, file_name, PATH_MAX-1); |
| fname[PATH_MAX-1] = 0; |
| base = basename(fname); |
| if(((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { |
| size_t blen = strlen (base); |
| |
| if(strlen(fname) >= PATH_MAX-1) { |
| /* Make room for the '_' */ |
| blen--; |
| base[blen] = 0; |
| } |
| /* Prepend a '_'. */ |
| memmove (base + 1, base, blen + 1); |
| base[0] = '_'; |
| strcpy (file_name, fname); |
| } |
| return file_name; |
| } |
| |
| /* Replace bad characters in the file name before using it. |
| * fn will always be freed before return |
| * The returned pointer must be freed by the caller if not NULL |
| */ |
| static char *sanitize_dos_name(char *fn) |
| { |
| char tmpfn[PATH_MAX]; |
| if(strlen(fn) >= PATH_MAX) |
| fn[PATH_MAX-1]=0; /* truncate it */ |
| strcpy(tmpfn, msdosify(fn)); |
| free(fn); |
| return strdup(rename_if_dos_device_name(tmpfn)); |
| } |
| #endif /* MSDOS || WIN32 */ |