/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * $Id$ ***************************************************************************/ #include "setup.h" #include #include #include #include #include #include #include #include #include #include "urlglob.h" #include "writeout.h" #include "getpass.h" #include "homedir.h" #include "curlutil.h" #ifdef USE_MANUAL #include "hugehelp.h" #endif #ifdef USE_ENVIRONMENT #include "writeenv.h" #endif #define CURLseparator "--_curl_--" #ifdef NETWARE #ifdef __NOVELL_LIBC__ #include #else #include #define mkdir mkdir_510 #endif #endif #include "version.h" #ifdef HAVE_IO_H /* typical win32 habit */ #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UTIME_H #include #else #ifdef HAVE_SYS_UTIME_H #include #endif #endif /* HAVE_UTIME_H */ #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_SYS_POLL_H #include #elif defined(HAVE_POLL_H) #include #endif #ifdef HAVE_LOCALE_H #include /* for setlocale() */ #endif #define ENABLE_CURLX_PRINTF /* make the curlx header define all printf() functions to use the curlx_* versions instead */ #include /* header from the libcurl directory */ #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) #include /* 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 HAVE_NETINET_IN_H #include /* for IPPROTO_TCP */ #endif #ifdef HAVE_NETINET_TCP_H #include /* for TCP_KEEPIDLE, TCP_KEEPINTVL */ #endif /* 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 #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 #ifdef MSDOS #include static const char *msdosify(const char *); static char *rename_if_dos_device_name(char *); #ifdef DJGPP /* we want to glob our own argv[] */ char **__crt0_glob_function (char *arg) { (void)arg; return (char**)0; } #endif /* __DJGPP__ */ #endif /* MSDOS */ #ifndef STDIN_FILENO #define STDIN_FILENO fileno(stdin) #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO fileno(stdout) #endif #ifndef STDERR_FILENO #define STDERR_FILENO fileno(stderr) #endif #define CURL_PROGRESS_STATS 0 /* default progress display */ #define CURL_PROGRESS_BAR 1 /** * @def MIN * standard MIN macro */ #ifndef MIN #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) #endif typedef enum { HTTPREQ_UNSPEC, HTTPREQ_GET, HTTPREQ_HEAD, HTTPREQ_POST, HTTPREQ_SIMPLEPOST, HTTPREQ_CUSTOM, HTTPREQ_LAST } HttpReq; #ifdef WIN32 #include #define F_OK 0 #define mkdir(x,y) (mkdir)(x) #endif #ifdef VMS #include "curlmsg_vms.h" #endif /* * Large file support (>2Gb) using WIN32 functions. */ #ifdef USE_WIN32_LARGE_FILES # include # include # include # 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 #endif /* * Small file support (<2Gb) using WIN32 functions. */ #ifdef USE_WIN32_SMALL_FILES # include # include # include # define lseek(fdes,offset,whence) _lseek(fdes, offset, whence) # define fstat(fdes,stp) _fstat(fdes, stp) # define stat(fname,stp) _stat(fname, stp) # define struct_stat struct _stat #endif #ifdef CURL_DOES_CONVERSIONS #ifdef HAVE_ICONV iconv_t inbound_cd = (iconv_t)-1; iconv_t outbound_cd = (iconv_t)-1; /* * convert_to_network() is an internal function to convert * from the host encoding to ASCII on non-ASCII platforms. */ static CURLcode convert_to_network(char *buffer, size_t length) { CURLcode rc; /* translate from the host encoding to the network encoding */ char *input_ptr, *output_ptr; size_t in_bytes, out_bytes; /* open an iconv conversion descriptor if necessary */ if(outbound_cd == (iconv_t)-1) { outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, CURL_ICONV_CODESET_OF_HOST); if(outbound_cd == (iconv_t)-1) { return CURLE_CONV_FAILED; } } /* call iconv */ input_ptr = output_ptr = buffer; in_bytes = out_bytes = length; rc = iconv(outbound_cd, &input_ptr, &in_bytes, &output_ptr, &out_bytes); if ((rc == -1) || (in_bytes != 0)) { return CURLE_CONV_FAILED; } return CURLE_OK; } /* * convert_from_network() is an internal function * for performing ASCII conversions on non-ASCII platforms. */ static CURLcode convert_from_network(char *buffer, size_t length) { CURLcode rc; /* translate from the network encoding to the host encoding */ char *input_ptr, *output_ptr; size_t in_bytes, out_bytes; /* open an iconv conversion descriptor if necessary */ if(inbound_cd == (iconv_t)-1) { inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, CURL_ICONV_CODESET_OF_NETWORK); if(inbound_cd == (iconv_t)-1) { return CURLE_CONV_FAILED; } } /* call iconv */ input_ptr = output_ptr = buffer; in_bytes = out_bytes = length; rc = iconv(inbound_cd, &input_ptr, &in_bytes, &output_ptr, &out_bytes); if ((rc == -1) || (in_bytes != 0)) { return CURLE_CONV_FAILED; } return CURLE_OK; } #endif /* HAVE_ICONV */ static char convert_char(curl_infotype infotype, char this_char) { /* determine how this specific character should be displayed */ switch(infotype) { case CURLINFO_DATA_IN: case CURLINFO_DATA_OUT: case CURLINFO_SSL_DATA_IN: case CURLINFO_SSL_DATA_OUT: /* data, treat as ASCII */ if ((this_char >= 0x20) && (this_char < 0x7f)) { /* printable ASCII hex value: convert to host encoding */ convert_from_network(&this_char, 1); } else { /* non-printable ASCII, use a replacement character */ return UNPRINTABLE_CHAR; } /* fall through to default */ default: /* treat as host encoding */ if (ISPRINT(this_char) && (this_char != '\t') && (this_char != '\r') && (this_char != '\n')) { /* printable characters excluding tabs and line end characters */ return this_char; } break; } /* non-printable, use a replacement character */ return UNPRINTABLE_CHAR; } #endif /* CURL_DOES_CONVERSIONS */ #ifdef WIN32 /* * Truncate a file handle at a 64-bit position 'where'. * Borland doesn't even support 64-bit types. */ #ifdef __BORLANDC__ #define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence) #endif #ifndef HAVE_FTRUNCATE #define HAVE_FTRUNCATE 1 #endif static int ftruncate64 (int fd, curl_off_t where) { if(_lseeki64(fd, where, SEEK_SET) < 0) return -1; if(!SetEndOfFile((HANDLE)_get_osfhandle(fd))) return -1; return 0; } #define ftruncate(fd,where) ftruncate64(fd,where) #endif typedef enum { TRACE_NONE, /* 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; FILE *stream; struct Configurable *config; curl_off_t bytes; /* amount written so far */ curl_off_t init; /* original size (non-zero when appending) */ }; struct Configurable { CURL *easy; /* once we have one, we keep it here */ bool remote_time; char *random_file; char *egd_file; char *useragent; char *cookie; /* single line with specified cookies */ char *cookiejar; /* write to this file */ char *cookiefile; /* read from this file */ bool cookiesession; /* new session? */ bool encoding; /* Accept-Encoding please */ long authtype; /* auth bitmask */ bool use_resume; bool resume_from_current; bool disable_epsv; bool disable_eprt; curl_off_t resume_from; char *postfields; 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 *proxyuserpwd; char *proxy; 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; 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 *key; char *key_type; char *key_passwd; char *pubkey; char *hostpubmd5; char *engine; bool list_engines; bool crlf; char *customrequest; char *krblevel; char *trace_dump; /* file to dump the network trace to, or NULL */ FILE *trace_stream; bool trace_fopened; trace tracetype; bool tracetime; /* include timestamp? */ long httpversion; bool progressmode; bool nobuffer; bool globoff; bool use_httpget; bool insecure_ok; /* set TRUE to allow insecure SSL connects */ bool create_dirs; bool ftp_create_dirs; bool ftp_skip_ip; bool proxynegotiate; bool proxyntlm; bool proxydigest; bool proxybasic; bool proxyanyauth; char *writeout; /* %-styled format string to output */ bool writeenv; /* write results to environment, if available */ FILE *errors; /* if stderr redirect is requested */ bool errors_fopened; struct curl_slist *quote; struct curl_slist *postquote; struct curl_slist *prequote; long ssl_version; long ip_version; curl_TimeCond timecond; time_t condtime; struct curl_slist *headers; struct curl_httppost *httppost; struct curl_httppost *last_post; struct curl_slist *telnet_options; HttpReq httpreq; /* for bandwidth limiting features: */ curl_off_t sendpersecond; /* send to peer */ curl_off_t recvpersecond; /* receive from peer */ struct timeval lastsendtime; size_t lastsendsize; struct timeval lastrecvtime; size_t lastrecvsize; bool ftp_ssl; bool ftp_ssl_reqd; bool ftp_ssl_control; bool ftp_ssl_ccc; int ftp_ssl_ccc_mode; char *socksproxy; /* set to server string */ int socksver; /* set to CURLPROXY_SOCKS* define */ bool tcp_nodelay; long req_retry; /* number of retries */ long retry_delay; /* delay between retries (in seconds) */ long retry_maxtime; /* maximum time to keep retrying */ char *ftp_account; /* for ACCT */ char *ftp_alternative_to_user; /* send command if USER/PASS fails */ int ftp_filemethod; bool ignorecl; /* --ignore-content-length */ bool disable_sessionid; char *libcurl; /* output libcurl code to this file name */ bool raw; bool post301; bool nokeepalive; /* for keepalive needs */ long alivetime; int default_node_flags; /* default flags to seach for each 'node', which is basically each given URL to transfer */ struct OutStruct *outs; }; #define WARN_PREFIX "Warning: " #define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX)) /* produce this text message to the user unless mute was selected */ static void warnf(struct Configurable *config, const char *fmt, ...) { if(!config->mute) { va_list ap; int len; char *ptr; char print_buffer[256]; va_start(ap, fmt); va_start(ap, fmt); len = vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap); va_end(ap); ptr = print_buffer; while(len > 0) { fputs(WARN_PREFIX, config->errors); if(len > (int)WARN_TEXTWIDTH) { int cut = WARN_TEXTWIDTH-1; while(!ISSPACE(ptr[cut]) && cut) { cut--; } if(0 == cut) /* not a single cutting position was found, just cut it at the max text width then! */ cut = WARN_TEXTWIDTH-1; fwrite(ptr, cut + 1, 1, config->errors); fputs("\n", config->errors); ptr += cut+1; /* skip the space too */ len -= cut; } else { fputs(ptr, config->errors); len = 0; } } } } /* * This is the main global constructor for the app. Call this before * _any_ libcurl usage. If this fails, *NO* libcurl functions may be * used, or havoc may be the result. */ static CURLcode main_init(void) { #ifdef DJGPP /* stop stat() wasting time */ _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; #endif return curl_global_init(CURL_GLOBAL_DEFAULT); } /* * This is the main global destructor for the app. Call this after * _all_ libcurl usage is done. */ static void main_free(void) { curl_global_cleanup(); #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) /* close iconv conversion descriptor */ if(inbound_cd != (iconv_t)-1) iconv_close(inbound_cd); if(outbound_cd != (iconv_t)-1) iconv_close(outbound_cd); #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ } static int SetHTTPrequest(struct Configurable *config, HttpReq req, HttpReq *store) { if((*store == HTTPREQ_UNSPEC) || (*store == req)) { *store = req; return 0; } warnf(config, "You can only select one HTTP request!\n"); return 1; } static void helpf(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...] ", "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 CA certificate to verify peer against (SSL)", " --capath CA directory to verify peer against (SSL)", " -E/--cert Client certificate file and password (SSL)", " --cert-type Certificate file type (DER/PEM/ENG) (SSL)", " --ciphers SSL ciphers to use (SSL)", " --compressed Request compressed response (using deflate or gzip)", " -K/--config Specify which config file to read", " --connect-timeout Maximum time allowed for connection", " -C/--continue-at Resumed transfer offset", " -b/--cookie Cookie string or file to read cookies from (H)", " -c/--cookie-jar Write cookies to this file after operation (H)", " --create-dirs Create necessary local directory hierarchy", " --crlf Convert LF to CRLF in upload", " -d/--data HTTP POST data (H)", " --data-ascii HTTP POST ASCII data (H)", " --data-binary HTTP POST binary data (H)", " --data-urlencode HTTP POST data url encoded (H)", " --digest Use HTTP Digest Authentication (H)", " --disable-eprt Inhibit using EPRT or LPRT (F)", " --disable-epsv Inhibit using EPSV (F)", " -D/--dump-header Write the headers to this file", " --egd-file EGD socket path for random data (SSL)", " --engine Crypto engine to use (SSL). \"--engine list\" for list", #ifdef USE_ENVIRONMENT " --environment Write results to environment variables (RISC OS)", #endif " -f/--fail Fail silently (no output at all) on HTTP errors (H)", " -F/--form Specify HTTP multipart POST data (H)", " --form-string Specify HTTP multipart POST data (H)", " --ftp-account Account data to send when requested by server (F)", " --ftp-alternative-to-user String to replace \"USER [name]\" (F)", " --ftp-create-dirs Create the remote dirs if not present (F)", " --ftp-method [multicwd/nocwd/singlecwd] Control CWD usage (F)", " --ftp-pasv Use PASV/EPSV instead of PORT (F)", " -P/--ftp-port
Use PORT with address instead of PASV (F)", " --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n" " --ftp-ssl Try SSL/TLS for ftp transfer (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)", " --ftp-ssl-reqd Require SSL/TLS for ftp transfer (F)", " -G/--get Send the -d data with a HTTP GET (H)", " -g/--globoff Disable URL sequences and ranges using {} and []", " -H/--header Custom header to pass to server (H)", " -I/--head Show document info only", " -h/--help This help text", " --hostpubmd5 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 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 Interval between keepalive probes", " --key Private key file name (SSL/SSH)", " --key-type Private key file type (DER/PEM/ENG) (SSL)", " --krb Enable Kerberos with specified security level (F)", " --libcurl Dump libcurl equivalent code of this command line", " --limit-rate Limit transfer speed to this rate", " -l/--list-only List only names of an FTP directory (F)", " --local-port [-num] Force use of these local port numbers", " -L/--location Follow Location: hints (H)", " --location-trusted Follow Location: and send auth to other hosts (H)", " -M/--manual Display the full manual", " --max-filesize Maximum file size to download (H/F)", " --max-redirs Maximum number of redirects allowed (H)", " -m/--max-time Maximum time allowed for the transfer", " --negotiate Use HTTP Negotiate Authentication (H)", " -n/--netrc Must read .netrc for user name and password", " --netrc-optional Use either .netrc or URL; overrides -n", " -N/--no-buffer Disable buffering of the output stream", " --no-keepalive Disable keepalive use on the connection", " --no-sessionid Disable SSL session-ID reusing (SSL)", " --ntlm Use HTTP NTLM authentication (H)", " -o/--output Write output to instead of stdout", " --pass Pass phrase for the private key (SSL/SSH)", " --post301 Do not switch to GET after following a 301 redirect (H)", " -#/--progress-bar Display transfer progress as a progress bar", " -x/--proxy Use HTTP proxy on given port", " --proxy-anyauth Pick \"any\" proxy authentication method (H)", " --proxy-basic Use Basic authentication on the proxy (H)", " --proxy-digest Use Digest authentication on the proxy (H)", " --proxy-negotiate Use Negotiate authentication on the proxy (H)", " --proxy-ntlm Use NTLM authentication on the proxy (H)", " -U/--proxy-user Set proxy user and password", " -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)", " --pubkey Public key file name (SSH)", " -Q/--quote Send command(s) to server before file transfer (F/SFTP)", " --random-file File for reading random data from (SSL)", " -r/--range Retrieve a byte range from a HTTP/1.1 or FTP server", " --raw Pass HTTP \"raw\", without any transfer decoding (H)", " -e/--referer Referer URL (H)", " -O/--remote-name Write output to a file named as the remote file", " --remote-name-all Use the remote file name for all URLs", " -R/--remote-time Set the remote file's time on the local output", " -X/--request Specify request command to use", " --retry Retry request times if transient problems occur", " --retry-delay When retrying, wait this many seconds between each", " --retry-max-time 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 SOCKS4 proxy on given host + port", " --socks4a SOCKS4a proxy on given host + port", " --socks5 SOCKS5 proxy on given host + port", " --socks5-hostname SOCKS5 proxy, pass host name to proxy", " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs", " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30", " -2/--sslv2 Use SSLv2 (SSL)", " -3/--sslv3 Use SSLv3 (SSL)", " --stderr Where to redirect stderr. - means stdout", " --tcp-nodelay Use the TCP_NODELAY option", " -t/--telnet-option Set telnet option", " -z/--time-cond