| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 1998 - 2005, Daniel Stenberg, <[email protected]>, et al. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://curl.haxx.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| * $Id$ |
| ***************************************************************************/ |
| |
| #include "setup.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <ctype.h> |
| #include <errno.h> |
| |
| #include <curl/curl.h> |
| |
| #include "urlglob.h" |
| #include "writeout.h" |
| #include "getpass.h" |
| #include "homedir.h" |
| #ifdef USE_MANUAL |
| #include "hugehelp.h" |
| #endif |
| #ifdef USE_ENVIRONMENT |
| #include "writeenv.h" |
| #endif |
| |
| #define CURLseparator "--_curl_--" |
| |
| #if defined(WIN32)&&!defined(__CYGWIN32__) |
| #include <winsock2.h> |
| #endif |
| |
| #ifdef __NOVELL_LIBC__ |
| #include <screen.h> |
| #endif |
| |
| #ifdef TIME_WITH_SYS_TIME |
| /* We can include both fine */ |
| #include <sys/time.h> |
| #include <time.h> |
| #else |
| #ifdef HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| #else |
| # include <time.h> |
| #endif |
| #endif |
| |
| |
| #include "version.h" |
| |
| #ifdef HAVE_IO_H /* typical win32 habit */ |
| #include <io.h> |
| #endif |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| |
| #ifdef HAVE_UTIME_H |
| #include <utime.h> |
| #else |
| #ifdef HAVE_SYS_UTIME_H |
| #include <sys/utime.h> |
| #endif |
| |
| #endif /* HAVE_UTIME_H */ |
| |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| |
| #ifdef HAVE_SYS_POLL_H |
| #include <sys/poll.h> |
| #endif |
| |
| #ifdef HAVE_LOCALE_H |
| #include <locale.h> /* for setlocale() */ |
| #endif |
| |
| #define ENABLE_CURLX_PRINTF |
| /* make the curlx header define all printf() functions to use the curlx_* |
| versions instead */ |
| #include <curlx.h> /* header from the libcurl directory */ |
| |
| /* The last #include file should be: */ |
| #ifdef CURLDEBUG |
| #ifndef CURLTOOLDEBUG |
| #define MEMDEBUG_NODEFINES |
| #endif |
| /* This is low-level hard-hacking memory leak tracking and similar. Using |
| the library level code from this client-side is ugly, but we do this |
| anyway for convenience. */ |
| #include "memdebug.h" |
| #endif |
| |
| #define DEFAULT_MAXREDIRS 50L |
| |
| #ifdef __DJGPP__ |
| #include <dos.h> |
| |
| char *msdosify(char *); |
| char *rename_if_dos_device_name(char *); |
| |
| /* we want to glob our own argv[] */ |
| char **__crt0_glob_function (char *arg) |
| { |
| (void)arg; |
| return (char**)0; |
| } |
| #endif /* __DJGPP__ */ |
| |
| #define CURL_PROGRESS_STATS 0 /* default progress display */ |
| #define CURL_PROGRESS_BAR 1 |
| |
| /** |
| * @def MIN |
| * standard MIN macro |
| */ |
| #ifndef MIN |
| #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) |
| #endif |
| |
| typedef enum { |
| HTTPREQ_UNSPEC, |
| HTTPREQ_GET, |
| HTTPREQ_HEAD, |
| HTTPREQ_POST, |
| HTTPREQ_SIMPLEPOST, |
| HTTPREQ_CUSTOM, |
| HTTPREQ_LAST |
| } HttpReq; |
| |
| /* Just a set of bits */ |
| #define CONF_DEFAULT 0 |
| |
| #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */ |
| #define CONF_VERBOSE (1<<5) /* talk a lot */ |
| #define CONF_HEADER (1<<8) /* throw the header out too */ |
| #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */ |
| #define CONF_NOBODY (1<<11) /* use HEAD to get http document */ |
| #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */ |
| #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */ |
| #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */ |
| #define CONF_NETRC (1<<22) /* read user+password from .netrc */ |
| #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */ |
| #define CONF_GETTEXT (1<<24) /* use ASCII/text for transfer */ |
| #define CONF_MUTE (1<<28) /* force NOPROGRESS */ |
| |
| #define CONF_NETRC_OPT (1<<29) /* read user+password from either |
| * .netrc or URL*/ |
| #define CONF_UNRESTRICTED_AUTH (1<<30) |
| /* Send authentication (user+password) when following |
| * locations, even when hostname changed */ |
| |
| #ifndef HAVE_STRDUP |
| /* Ultrix doesn't have strdup(), so make a quick clone: */ |
| char *strdup(char *str) |
| { |
| int len; |
| char *newstr; |
| |
| len = strlen(str); |
| newstr = (char *) malloc((len+1)*sizeof(char)); |
| if (!newstr) |
| return (char *)NULL; |
| |
| strcpy(newstr,str); |
| |
| return newstr; |
| |
| } |
| #endif |
| |
| #ifdef WIN32 |
| #include <direct.h> |
| #define F_OK 0 |
| #define mkdir(x,y) (mkdir)(x) |
| #endif |
| |
| #ifdef VMS |
| #include "curlmsg_vms.h" |
| #endif |
| |
| /* Support uploading and resuming of >2GB files |
| */ |
| #if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4) |
| #define struct_stat struct _stati64 |
| #define stat(file,st) _stati64(file,st) |
| #else |
| #define struct_stat struct stat |
| #endif |
| |
| #ifdef WIN32 |
| /* |
| * Truncate a file handle at a 64-bit position 'where'. |
| * Borland doesn't even support 64-bit types. |
| */ |
| #ifdef __BORLANDC__ |
| #define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence) |
| #endif |
| |
| static int ftruncate64 (int fd, curl_off_t where) |
| { |
| curl_off_t curr; |
| int rc = 0; |
| |
| if ((curr = _lseeki64(fd, 0, SEEK_CUR)) < 0) |
| return -1; |
| |
| if (_lseeki64(fd, where, SEEK_SET) < 0) |
| return -1; |
| |
| if (write(fd, 0, 0) < 0) |
| rc = -1; |
| _lseeki64(fd, curr, SEEK_SET); |
| return rc; |
| } |
| #define ftruncate(fd,where) ftruncate64(fd,where) |
| #endif |
| |
| /* |
| * 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) |
| { |
| 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(); |
| } |
| |
| static int SetHTTPrequest(HttpReq req, HttpReq *store) |
| { |
| if((*store == HTTPREQ_UNSPEC) || |
| (*store == req)) { |
| *store = req; |
| return 0; |
| } |
| fprintf(stderr, "You can only select one HTTP request!\n"); |
| return 1; |
| } |
| |
| static void helpf(const char *fmt, ...) |
| { |
| va_list ap; |
| if(fmt) { |
| va_start(ap, fmt); |
| fputs("curl: ", stderr); /* prefix it */ |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| } |
| fprintf(stderr, "curl: try 'curl --help' " |
| #ifdef USE_MANUAL |
| "or 'curl --manual' " |
| #endif |
| "for more information\n"); |
| } |
| |
| /* |
| * A chain of these nodes contain URL to get and where to put the URL's |
| * contents. |
| */ |
| struct getout { |
| struct getout *next; /* next one */ |
| char *url; /* the URL we deal with */ |
| char *outfile; /* where to store the output */ |
| char *infile; /* file to upload, if GETOUT_UPLOAD is set */ |
| int flags; /* options */ |
| }; |
| #define GETOUT_OUTFILE (1<<0) /* set when outfile is deemed done */ |
| #define GETOUT_URL (1<<1) /* set when URL is deemed done */ |
| #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */ |
| #define GETOUT_UPLOAD (1<<3) /* if set, -T has been used */ |
| #define GETOUT_NOUPLOAD (1<<4) /* if set, -T "" has been used */ |
| |
| |
| static void help(void) |
| { |
| int i; |
| static const char * const helptext[]={ |
| "Usage: curl [options...] <url>", |
| "Options: (H) means HTTP/HTTPS only, (F) means FTP only", |
| " -a/--append Append to target file when uploading (F)", |
| " -A/--user-agent <string> User-Agent to send to server (H)", |
| " --anyauth Tell curl to choose authentication method (H)", |
| " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)", |
| " --basic Enable HTTP Basic Authentication (H)", |
| " -B/--use-ascii Use ASCII/text transfer", |
| " -c/--cookie-jar <file> Write cookies to this file after operation (H)", |
| " -C/--continue-at <offset> Resumed transfer offset", |
| " -d/--data <data> HTTP POST data (H)", |
| " --data-ascii <data> HTTP POST ASCII data (H)", |
| " --data-binary <data> HTTP POST binary data (H)", |
| " --negotiate Enable HTTP Negotiate Authentication (H)", |
| " --digest Enable HTTP Digest Authentication (H)", |
| " --disable-eprt Prevent curl from using EPRT or LPRT (F)", |
| " --disable-epsv Prevent curl from using EPSV (F)", |
| " -D/--dump-header <file> Write the headers to this file", |
| " --egd-file <file> EGD socket path for random data (SSL)", |
| " --tcp-nodelay Set the TCP_NODELAY option", |
| #ifdef USE_ENVIRONMENT |
| " --environment Write result codes to environment variables (RISC OS)", |
| #endif |
| " -e/--referer Referer URL (H)", |
| " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)", |
| " --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)", |
| " --key <key> Private key file name (SSL)", |
| " --key-type <type> Private key file type (DER/PEM/ENG) (SSL)", |
| " --pass <pass> Pass phrase for the private key (SSL)", |
| " --engine <eng> Crypto engine to use (SSL). \"--engine list\" for list", |
| " --cacert <file> CA certificate to verify peer against (SSL)", |
| " --capath <directory> CA directory (made using c_rehash) to verify", |
| " peer against (SSL)", |
| " --ciphers <list> SSL ciphers to use (SSL)", |
| " --compressed Request compressed response (using deflate or gzip)", |
| " --connect-timeout <seconds> Maximum time allowed for connection", |
| " --create-dirs Create necessary local directory hierarchy", |
| " --crlf Convert LF to CRLF in upload", |
| " -f/--fail Fail silently (no output at all) on errors (H)", |
| " --ftp-create-dirs Create the remote dirs if not present (F)", |
| " --ftp-pasv Use PASV instead of PORT (F)", |
| " --ftp-ssl Enable SSL/TLS for the ftp transfer (F)", |
| " -F/--form <name=content> Specify HTTP multipart POST data (H)", |
| " -g/--globoff Disable URL sequences and ranges using {} and []", |
| " -G/--get Send the -d data with a HTTP GET (H)", |
| " -h/--help This help text", |
| " -H/--header <line> Custom header to pass to server (H)", |
| " -i/--include Include protocol headers in the output (H/F)", |
| " -I/--head Show document info only", |
| " -j/--junk-session-cookies Ignore session cookies read from file (H)", |
| " --interface <interface> Specify network interface to use", |
| " --krb4 <level> Enable krb4 with specified security level (F)", |
| " -k/--insecure Allow curl to connect to SSL sites without certs (H)", |
| " -K/--config Specify which config file to read", |
| " -l/--list-only List only names of an FTP directory (F)", |
| " --limit-rate <rate> Limit transfer speed to this rate", |
| " -L/--location Follow Location: hints (H)", |
| " --location-trusted Follow Location: and send authentication even ", |
| " to other hostnames (H)", |
| " -m/--max-time <seconds> Maximum time allowed for the transfer", |
| " --max-redirs <num> Maximum number of redirects allowed (H)", |
| " --max-filesize <bytes> Maximum file size to download (H/F)", |
| " -M/--manual Display the full manual", |
| " -n/--netrc Must read .netrc for user name and password", |
| " --netrc-optional Use either .netrc or URL; overrides -n", |
| " --ntlm Enable HTTP NTLM authentication (H)", |
| " -N/--no-buffer Disable buffering of the output stream", |
| " -o/--output <file> Write output to <file> instead of stdout", |
| " -O/--remote-name Write output to a file named as the remote file", |
| " -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)", |
| " --proxy-basic Enable Basic authentication on the proxy (H)", |
| " --proxy-digest Enable Digest authentication on the proxy (H)", |
| " --proxy-ntlm Enable NTLM authentication on the proxy (H)", |
| " -P/--ftp-port <address> Use PORT with address instead of PASV (F)", |
| " -q If used as the first parameter disables .curlrc", |
| " -Q/--quote <cmd> Send command(s) to server before file transfer (F)", |
| " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server", |
| " --random-file <file> File for reading random data from (SSL)", |
| " -R/--remote-time Set the remote file's time on the local output", |
| " --retry <num> Retry request <num> times if transient problems occur", |
| " --retry-delay <seconds> When retrying, wait this many seconds between each", |
| " --retry-max-time <seconds> Retry only within this period", |
| " -s/--silent Silent mode. Don't output anything", |
| " -S/--show-error Show error. With -s, make curl show errors when they occur", |
| " --socks <host[:port]> Use SOCKS5 proxy on given host + port", |
| " --stderr <file> Where to redirect stderr. - means stdout", |
| " -t/--telnet-option <OPT=val> Set telnet option", |
| " --trace <file> Write a debug trace to the given file", |
| " --trace-ascii <file> Like --trace but without the hex output", |
| " -T/--upload-file <file> Transfer <file> to remote site", |
| " --url <URL> Spet URL to work with", |
| " -u/--user <user[:password]> Set server user and password", |
| " -U/--proxy-user <user[:password]> Set proxy user and password", |
| " -v/--verbose Make the operation more talkative", |
| " -V/--version Show version number and quit", |
| #ifdef __DJGPP__ |
| " --wdebug Turn on Watt-32 debugging under DJGPP", |
| #endif |
| " -w/--write-out [format] What to output after completion", |
| " -x/--proxy <host[:port]> Use HTTP proxy on given port", |
| " -X/--request <command> Specify request command to use", |
| " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30", |
| " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs", |
| " -z/--time-cond <time> Transfer based on a time condition", |
| " -0/--http1.0 Use HTTP 1.0 (H)", |
| " -1/--tlsv1 Use TLSv1 (SSL)", |
| " -2/--sslv2 Use SSLv2 (SSL)", |
| " -3/--sslv3 Use SSLv3 (SSL)", |
| " --3p-quote like -Q for the source URL for 3rd party transfer (F)", |
| " --3p-url source URL to activate 3rd party transfer (F)", |
| " --3p-user user and password for source 3rd party transfer (F)", |
| " -4/--ipv4 Resolve name to IPv4 address", |
| " -6/--ipv6 Resolve name to IPv6 address", |
| " -#/--progress-bar Display transfer progress as a progress bar", |
| NULL |
| }; |
| for(i=0; helptext[i]; i++) { |
| puts(helptext[i]); |
| #ifdef __NOVELL_LIBC__ |
| if (i && ((i % 23) == 0)) |
| pressanykey(); |
| #endif |
| } |
| } |
| |
| struct LongShort { |
| const char *letter; |
| const char *lname; |
| bool extraparam; |
| }; |
| |
| struct Configurable { |
| bool remote_time; |
| char *random_file; |
| char *egd_file; |
| char *useragent; |
| char *cookie; /* single line with specified cookies */ |
| char *cookiejar; /* write to this file */ |
| char *cookiefile; /* read from this file */ |
| bool cookiesession; /* new session? */ |
| bool encoding; /* Accept-Encoding please */ |
| long authtype; /* auth bitmask */ |
| bool use_resume; |
| bool resume_from_current; |
| bool disable_epsv; |
| bool disable_eprt; |
| curl_off_t resume_from; |
| char *postfields; |
| long postfieldsize; |
| char *referer; |
| long timeout; |
| long connecttimeout; |
| long maxredirs; |
| curl_off_t max_filesize; |
| char *headerfile; |
| char *ftpport; |
| char *iface; |
| unsigned short porttouse; |
| char *range; |
| long low_speed_limit; |
| long low_speed_time; |
| bool showerror; |
| char *userpwd; |
| char *proxyuserpwd; |
| char *proxy; |
| bool proxytunnel; |
| long conf; |
| struct getout *url_list; /* point to the first node */ |
| struct getout *url_last; /* point to the last/current node */ |
| struct getout *url_get; /* point to the node to fill in URL */ |
| struct getout *url_out; /* point to the node to fill in outfile */ |
| char *cipher_list; |
| char *cert; |
| char *cert_type; |
| char *cacert; |
| char *capath; |
| char *key; |
| char *key_type; |
| char *key_passwd; |
| char *engine; |
| bool list_engines; |
| bool crlf; |
| char *customrequest; |
| char *krb4level; |
| char *trace_dump; /* file to dump the network trace to, or NULL */ |
| FILE *trace_stream; |
| bool trace_fopened; |
| bool trace_ascii; |
| 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 proxyntlm; |
| bool proxydigest; |
| bool proxybasic; |
| 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; |
| char *socks5proxy; |
| 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 *tp_url; /* third party URL */ |
| char *tp_user; /* third party userpwd */ |
| struct curl_slist *tp_quote; |
| struct curl_slist *tp_postquote; |
| struct curl_slist *tp_prequote; |
| char *ftp_account; /* for ACCT */ |
| }; |
| |
| /* global variable to hold info about libcurl */ |
| static curl_version_info_data *curlinfo; |
| |
| static void parseconfig(const char *filename, |
| struct Configurable *config); |
| static char *my_get_line(FILE *fp); |
| static int create_dir_hierarchy(char *outfile); |
| |
| static void GetStr(char **string, |
| char *value) |
| { |
| if(*string) |
| free(*string); |
| if(value) |
| *string = strdup(value); |
| else |
| *string = NULL; |
| } |
| |
| static char *file2string(FILE *file) |
| { |
| char buffer[256]; |
| char *ptr; |
| char *string=NULL; |
| size_t len=0; |
| size_t stringlen; |
| |
| if(file) { |
| while(fgets(buffer, sizeof(buffer), file)) { |
| ptr= strchr(buffer, '\r'); |
| if(ptr) |
| *ptr=0; |
| ptr= strchr(buffer, '\n'); |
| if(ptr) |
| *ptr=0; |
| stringlen=strlen(buffer); |
| if(string) |
| string = realloc(string, len+stringlen+1); |
| else |
| string = malloc(stringlen+1); |
| |
| strcpy(string+len, buffer); |
| |
| len+=stringlen; |
| } |
| return string; |
| } |
| else |
| return NULL; /* no string */ |
| } |
| |
| static char *file2memory(FILE *file, long *size) |
| { |
| char buffer[1024]; |
| char *string=NULL; |
| char *newstring=NULL; |
| size_t len=0; |
| long stringlen=0; |
| |
| if(file) { |
| while((len = fread(buffer, 1, sizeof(buffer), file))) { |
| if(string) { |
| newstring = realloc(string, len+stringlen); |
| if(newstring) |
| string = newstring; |
| else |
| break; /* no more strings attached! :-) */ |
| } |
| else |
| string = malloc(len); |
| memcpy(&string[stringlen], buffer, len); |
| stringlen+=len; |
| } |
| *size = stringlen; |
| return string; |
| } |
| else |
| return NULL; /* no string */ |
| } |
| |
| static void clean_getout(struct Configurable *config) |
| { |
| struct getout *node=config->url_list; |
| struct getout *next; |
| |
| while(node) { |
| next = node->next; |
| if(node->url) |
| free(node->url); |
| if(node->outfile) |
| free(node->outfile); |
| if(node->infile) |
| free(node->infile); |
| free(node); |
| |
| node = next; /* GOTO next */ |
| } |
| } |
| |
| static struct getout *new_getout(struct Configurable *config) |
| { |
| struct getout *node =malloc(sizeof(struct getout)); |
| struct getout *last= config->url_last; |
| if(node) { |
| /* clear the struct */ |
| memset(node, 0, sizeof(struct getout)); |
| |
| /* append this new node last in the list */ |
| if(last) |
| last->next = node; |
| else |
| config->url_list = node; /* first node */ |
| |
| /* move the last pointer */ |
| config->url_last = node; |
| } |
| return node; |
| } |
| |
| /* Structure for storing the information needed to build a multiple files |
| * section |
| */ |
| struct multi_files { |
| struct curl_forms form; |
| struct multi_files *next; |
| }; |
| |
| /* Add a new list entry possibly with a type_name |
| */ |
| static struct multi_files * |
| AddMultiFiles (const char *file_name, |
| const char *type_name, |
| const char *show_filename, |
| struct multi_files **multi_start, |
| struct multi_files **multi_current) |
| { |
| struct multi_files *multi; |
| struct multi_files *multi_type = NULL; |
| struct multi_files *multi_name = NULL; |
| multi = (struct multi_files *)malloc(sizeof(struct multi_files)); |
| if (multi) { |
| memset(multi, 0, sizeof(struct multi_files)); |
| multi->form.option = CURLFORM_FILE; |
| multi->form.value = file_name; |
| } |
| else |
| return NULL; |
| |
| if (!*multi_start) |
| *multi_start = multi; |
| |
| if (type_name) { |
| multi_type = (struct multi_files *)malloc(sizeof(struct multi_files)); |
| if (multi_type) { |
| memset(multi_type, 0, sizeof(struct multi_files)); |
| multi_type->form.option = CURLFORM_CONTENTTYPE; |
| multi_type->form.value = type_name; |
| multi->next = multi_type; |
| |
| multi = multi_type; |
| } |
| else { |
| free (multi); |
| return NULL; |
| } |
| } |
| if (show_filename) { |
| multi_name = (struct multi_files *)malloc(sizeof(struct multi_files)); |
| if (multi_name) { |
| memset(multi_name, 0, sizeof(struct multi_files)); |
| multi_name->form.option = CURLFORM_FILENAME; |
| multi_name->form.value = show_filename; |
| multi->next = multi_name; |
| |
| multi = multi_name; |
| } |
| else { |
| free (multi); |
| return NULL; |
| } |
| } |
| |
| if (*multi_current) |
| (*multi_current)->next = multi; |
| |
| *multi_current = multi; |
| |
| return *multi_current; |
| } |
| |
| /* Free the items of the list. |
| */ |
| static void FreeMultiInfo (struct multi_files *multi_start) |
| { |
| struct multi_files *multi; |
| while (multi_start) { |
| multi = multi_start; |
| multi_start = multi_start->next; |
| free (multi); |
| } |
| } |
| |
| /* Print list of OpenSSL engines supported. |
| */ |
| static void list_engines (const struct curl_slist *engines) |
| { |
| puts ("Build-time engines:"); |
| if (!engines) { |
| puts (" <none>"); |
| return; |
| } |
| for ( ; engines; engines = engines->next) |
| printf (" %s\n", engines->data); |
| } |
| |
| /*************************************************************************** |
| * |
| * formparse() |
| * |
| * Reads a 'name=value' paramter and builds the appropriate linked list. |
| * |
| * Specify files to upload with 'name=@filename'. Supports specified |
| * given Content-Type of the files. Such as ';type=<content-type>'. |
| * |
| * 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(char *input, |
| struct curl_httppost **httppost, |
| struct curl_httppost **last_post) |
| { |
| /* nextarg MUST be a string in the format 'name=contents' and we'll |
| build a linked list with the info */ |
| char name[256]; |
| char *contents; |
| char major[128]; |
| char minor[128]; |
| char *contp; |
| const char *type = NULL; |
| char *sep; |
| char *sep2; |
| |
| if((1 == sscanf(input, "%255[^=]=", name)) && |
| (contp = strchr(input, '='))) { |
| /* the input was using the correct format */ |
| |
| /* Allocate the contents */ |
| contents = strdup(contp+1); |
| if(!contents) { |
| fprintf(stderr, "out of memory\n"); |
| return 1; |
| } |
| contp = contents; |
| |
| if('@' == contp[0]) { |
| 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((int)*ptr)) |
| ptr++; |
| |
| if(curlx_strnequal("type=", ptr, 5)) { |
| /* set type pointer */ |
| type = &ptr[5]; |
| |
| /* verify that this is a fine type specifier */ |
| if(2 != sscanf(type, "%127[^/]/%127[^;,\n]", |
| major, minor)) { |
| fprintf(stderr, "Illegally formatted content-type field!\n"); |
| free(contents); |
| FreeMultiInfo (multi_start); |
| return 2; /* illegal content-type syntax! */ |
| } |
| /* now point beyond the content-type specifier */ |
| sep = (char *)type + strlen(major)+strlen(minor)+1; |
| |
| if(*sep) { |
| *sep=0; /* zero terminate type string */ |
| |
| ptr=sep+1; |
| } |
| else |
| ptr = NULL; /* end */ |
| } |
| else if(curlx_strnequal("filename=", ptr, 9)) { |
| filename = &ptr[9]; |
| ptr=strchr(filename, FORM_TYPE_SEPARATOR); |
| if(!ptr) { |
| ptr=strchr(filename, FORM_FILE_SEPARATOR); |
| } |
| if(ptr) { |
| *ptr=0; /* zero terminate */ |
| ptr++; |
| } |
| } |
| else |
| /* confusion, bail out of loop */ |
| break; |
| } |
| /* find the following comma */ |
| if(ptr) |
| sep=strchr(ptr, FORM_FILE_SEPARATOR); |
| else |
| sep=NULL; |
| } |
| else { |
| sep=strchr(contp, FORM_FILE_SEPARATOR); |
| } |
| if(sep) { |
| /* the next file name starts here */ |
| *sep =0; |
| sep++; |
| } |
| /* if type == NULL curl_formadd takes care of the problem */ |
| |
| if (!AddMultiFiles (contp, type, filename, &multi_start, |
| &multi_current)) { |
| fprintf(stderr, "Error building form post!\n"); |
| free(contents); |
| FreeMultiInfo (multi_start); |
| return 3; |
| } |
| contp = sep; /* move the contents pointer to after the separator */ |
| |
| } while(sep && *sep); /* loop if there's another file name */ |
| |
| /* now we add the multiple files section */ |
| if (multi_start) { |
| struct curl_forms *forms = NULL; |
| struct multi_files *ptr = multi_start; |
| unsigned int i, count = 0; |
| while (ptr) { |
| ptr = ptr->next; |
| ++count; |
| } |
| forms = |
| (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms)); |
| if (!forms) |
| { |
| fprintf(stderr, "Error building form post!\n"); |
| free(contents); |
| FreeMultiInfo (multi_start); |
| return 4; |
| } |
| for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next) |
| { |
| forms[i].option = ptr->form.option; |
| forms[i].value = ptr->form.value; |
| } |
| forms[count].option = CURLFORM_END; |
| FreeMultiInfo (multi_start); |
| if (curl_formadd(httppost, last_post, |
| CURLFORM_COPYNAME, name, |
| CURLFORM_ARRAY, forms, CURLFORM_END) != 0) { |
| fprintf(stderr, "curl_formadd failed!\n"); |
| free(forms); |
| free(contents); |
| return 5; |
| } |
| free(forms); |
| } |
| } |
| else { |
| struct curl_forms info[4]; |
| int i = 0; |
| char *ct = 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]=='<' ) { |
| 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) { |
| fprintf(stderr, "curl_formadd failed, possibly the file %s is bad!\n", |
| contp+1); |
| free(contents); |
| return 6; |
| } |
| } |
| else { |
| 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) { |
| fprintf(stderr, "curl_formadd failed!\n"); |
| free(contents); |
| return 7; |
| } |
| } |
| } |
| |
| } |
| else { |
| fprintf(stderr, "Illegally formatted input field!\n"); |
| return 1; |
| } |
| free(contents); |
| return 0; |
| } |
| |
| |
| typedef enum { |
| PARAM_OK, |
| PARAM_OPTION_AMBIGUOUS, |
| PARAM_OPTION_UNKNOWN, |
| PARAM_REQUIRES_PARAMETER, |
| PARAM_BAD_USE, |
| PARAM_HELP_REQUESTED, |
| PARAM_GOT_EXTRA_PARAMETER, |
| PARAM_BAD_NUMERIC, |
| PARAM_LIBCURL_DOESNT_SUPPORT, |
| PARAM_NO_MEM, |
| PARAM_LAST |
| } ParameterError; |
| |
| static const char *param2text(int res) |
| { |
| ParameterError error = (ParameterError)res; |
| switch(error) { |
| case PARAM_GOT_EXTRA_PARAMETER: |
| return "had unsupported trailing garbage"; |
| case PARAM_OPTION_UNKNOWN: |
| return "is unknown"; |
| case PARAM_OPTION_AMBIGUOUS: |
| return "is ambiguous"; |
| case PARAM_REQUIRES_PARAMETER: |
| return "requires parameter"; |
| case PARAM_BAD_USE: |
| return "is badly used here"; |
| case PARAM_BAD_NUMERIC: |
| return "expected a proper numerical parameter"; |
| case PARAM_LIBCURL_DOESNT_SUPPORT: |
| return "the installed libcurl version doesn't support this"; |
| case PARAM_NO_MEM: |
| return "out of memory"; |
| default: |
| return "unknown error"; |
| } |
| } |
| |
| static void cleanarg(char *str) |
| { |
| #ifdef HAVE_WRITABLE_ARGV |
| /* now that GetStr has copied the contents of nextarg, wipe the next |
| * argument out so that the username:password isn't displayed in the |
| * system process list */ |
| if (str) { |
| size_t len = strlen(str); |
| memset(str, ' ', len); |
| } |
| #else |
| (void)str; |
| #endif |
| } |
| |
| /* |
| * Parse the string and write the integer in the given address. Return |
| * non-zero on failure, zero on success. |
| * |
| * The string must start with a digit to be valid. |
| */ |
| |
| static int str2num(long *val, char *str) |
| { |
| int retcode = 0; |
| if(isdigit((int)*str)) |
| *val = atoi(str); |
| else |
| retcode = 1; /* badness */ |
| return retcode; |
| } |
| |
| /** |
| * Parses the given string looking for an offset (which may be |
| * a larger-than-integer value). |
| * |
| * @param val the offset to populate |
| * @param str the buffer containing the offset |
| * @return zero if successful, non-zero if failure. |
| */ |
| static int str2offset(curl_off_t *val, char *str) |
| { |
| #if SIZEOF_CURL_OFF_T > 4 |
| /* Ugly, but without going through a bunch of rigmarole, we don't have the |
| * definitions for LLONG_{MIN,MAX} or LONG_LONG_{MIN,MAX}. |
| */ |
| #ifndef LLONG_MAX |
| #ifdef _MSC_VER |
| #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFi64 |
| #define LLONG_MIN (curl_off_t)0x8000000000000000i64 |
| #else |
| #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFLL |
| #define LLONG_MIN (curl_off_t)0x8000000000000000LL |
| #endif |
| #endif |
| |
| /* this is a duplicate of the function that is also used in libcurl */ |
| *val = curlx_strtoofft(str, NULL, 0); |
| |
| if ((*val == LLONG_MAX || *val == LLONG_MIN) && errno == ERANGE) |
| return 1; |
| #else |
| *val = strtol(str, NULL, 0); |
| if ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| static void checkpasswd(const char *kind, /* for what purpose */ |
| char **userpwd) /* pointer to allocated string */ |
| { |
| char *ptr; |
| if(!*userpwd) |
| return; |
| |
| ptr = strchr(*userpwd, ':'); |
| if(!ptr) { |
| /* no password present, prompt for one */ |
| char passwd[256]=""; |
| char prompt[256]; |
| size_t passwdlen; |
| size_t userlen = strlen(*userpwd); |
| char *passptr; |
| |
| /* build a nice-looking prompt */ |
| curlx_msnprintf(prompt, sizeof(prompt), |
| "Enter %s password for user '%s':", |
| kind, *userpwd); |
| |
| /* get password */ |
| getpass_r(prompt, passwd, sizeof(passwd)); |
| passwdlen = strlen(passwd); |
| |
| /* extend the allocated memory area to fit the password too */ |
| passptr = realloc(*userpwd, |
| passwdlen + 1 + /* an extra for the colon */ |
| userlen + 1); /* an extra for the zero */ |
| |
| if(passptr) { |
| /* append the password separated with a colon */ |
| passptr[userlen]=':'; |
| memcpy(&passptr[userlen+1], passwd, passwdlen+1); |
| *userpwd = passptr; |
| } |
| } |
| } |
| |
| static ParameterError add2list(struct curl_slist **list, |
| char *ptr) |
| { |
| struct curl_slist *newlist = curl_slist_append(*list, ptr); |
| if(newlist) |
| *list = newlist; |
| else |
| return PARAM_NO_MEM; |
| |
| return PARAM_OK; |
| } |
| |
| static 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 */ |
| |
| const char *parse=NULL; |
| unsigned int j; |
| time_t now; |
| int hit=-1; |
| bool longopt=FALSE; |
| bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */ |
| ParameterError err; |
| |
| /* single-letter, |
| long-name, |
| boolean whether it takes an additional argument |
| */ |
| static const struct LongShort aliases[]= { |
| /* all these ones, starting with "*" or "$" as a short-option have *no* |
| short option to mention. */ |
| {"*", "url", TRUE}, |
| {"*a", "random-file", TRUE}, |
| {"*b", "egd-file", TRUE}, |
| {"*c", "connect-timeout", TRUE}, |
| {"*d", "ciphers", TRUE}, |
| {"*e", "disable-epsv", FALSE}, |
| #ifdef USE_ENVIRONMENT |
| {"*f", "environment", FALSE}, |
| #endif |
| {"*g", "trace", TRUE}, |
| {"*h", "trace-ascii", TRUE}, |
| {"*i", "limit-rate", TRUE}, |
| {"*j", "compressed", FALSE}, /* might take an arg someday */ |
| {"*k", "digest", FALSE}, |
| {"*l", "negotiate", FALSE}, |
| {"*m", "ntlm", FALSE}, |
| {"*n", "basic", FALSE}, |
| {"*o", "anyauth", FALSE}, |
| #ifdef __DJGPP__ |
| {"*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", "krb4", TRUE}, |
| {"*y", "max-filesize", TRUE}, |
| {"*z", "disable-eprt", FALSE}, |
| {"$a", "ftp-ssl", FALSE}, |
| {"$b", "ftp-pasv", FALSE}, |
| {"$c", "socks5", TRUE}, |
| {"$d", "tcp-nodelay",FALSE}, |
| {"$e", "proxy-digest", FALSE}, |
| {"$f", "proxy-basic", FALSE}, |
| {"$g", "retry", TRUE}, |
| {"$h", "retry-delay", TRUE}, |
| {"$i", "retry-max-time", TRUE}, |
| {"$j", "3p-url", TRUE}, |
| {"$k", "3p-user", TRUE}, |
| {"$l", "3p-quote", TRUE}, |
| {"$m", "ftp-account", 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}, |
| {"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}, |
| {"f", "fail", FALSE}, |
| {"F", "form", TRUE}, |
| {"g", "globoff", FALSE}, |
| {"G", "get", FALSE}, |
| {"h", "help", FALSE}, |
| {"H", "header", TRUE}, |
| {"i", "include", FALSE}, |
| {"I", "head", FALSE}, |
| {"j", "junk-session-cookies", FALSE}, |
| {"k", "insecure", FALSE}, |
| {"K", "config", TRUE}, |
| {"l", "list-only", FALSE}, |
| {"L", "location", FALSE}, |
| {"Lt", "location-trusted", FALSE}, |
| {"m", "max-time", TRUE}, |
| {"M", "manual", FALSE}, |
| {"n", "netrc", FALSE}, |
| {"no", "netrc-optional", FALSE}, |
| {"N", "no-buffer", FALSE}, |
| {"o", "output", TRUE}, |
| {"O", "remote-name", FALSE}, |
| {"p", "proxytunnel", FALSE}, |
| {"P", "ftpport", TRUE}, /* older version */ |
| {"P", "ftp-port", TRUE}, |
| {"q", "disable", FALSE}, |
| {"Q", "quote", TRUE}, |
| {"r", "range", TRUE}, |
| {"R", "remote-time", FALSE}, |
| {"s", "silent", FALSE}, |
| {"S", "show-error", FALSE}, |
| {"t", "telnet-options", TRUE}, |
| {"T", "upload-file", TRUE}, |
| {"u", "user", TRUE}, |
| {"U", "proxy-user", TRUE}, |
| {"v", "verbose", FALSE}, |
| {"V", "version", FALSE}, |
| {"w", "write-out", TRUE}, |
| {"x", "proxy", TRUE}, |
| {"X", "request", TRUE}, |
| {"X", "http-request", TRUE}, /* OBSOLETE VERSION */ |
| {"Y", "speed-limit", TRUE}, |
| {"y", "speed-time", TRUE}, |
| {"z", "time-cond", TRUE}, |
| {"#", "progress-bar",FALSE}, |
| }; |
| |
| if(('-' != flag[0]) || |
| (('-' == flag[0]) && ('-' == flag[1]))) { |
| /* this should be a long name */ |
| char *word=('-' == flag[0])?flag+2:flag; |
| size_t fnam=strlen(word); |
| int numhits=0; |
| for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) { |
| if(curlx_strnequal(aliases[j].lname, word, fnam)) { |
| longopt = TRUE; |
| numhits++; |
| if(curlx_strequal(aliases[j].lname, word)) { |
| parse = aliases[j].letter; |
| hit = j; |
| numhits = 1; /* a single unique hit */ |
| break; |
| } |
| parse = aliases[j].letter; |
| hit = j; |
| } |
| } |
| if(numhits>1) { |
| /* this is at least the second match! */ |
| return PARAM_OPTION_AMBIGUOUS; |
| } |
| if(hit < 0) { |
| return PARAM_OPTION_UNKNOWN; |
| } |
| } |
| else { |
| flag++; /* prefixed with one dash, pass it */ |
| hit=-1; |
| parse = flag; |
| } |
| |
| do { |
| /* we can loop here if we have multiple single-letters */ |
| |
| if(!longopt) |
| letter = parse?(char)*parse:'\0'; |
| else { |
| letter = parse[0]; |
| subletter = parse[1]; |
| } |
| *usedarg = FALSE; /* default is that we don't use the arg */ |
| |
| #if 0 |
| fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>"); |
| #endif |
| if(hit < 0) { |
| for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) { |
| if(letter == aliases[j].letter[0]) { |
| hit = j; |
| break; |
| } |
| } |
| if(hit < 0) { |
| return PARAM_OPTION_UNKNOWN; |
| } |
| } |
| if(hit < 0) { |
| return PARAM_OPTION_UNKNOWN; |
| } |
| if(!longopt && aliases[hit].extraparam && parse[1]) { |
| nextarg=(char *)&parse[1]; /* this is the actual extra parameter */ |
| singleopt=TRUE; /* don't loop anymore after this */ |
| } |
| else if(!nextarg && aliases[hit].extraparam) { |
| return PARAM_REQUIRES_PARAMETER; |
| } |
| else if(nextarg && aliases[hit].extraparam) |
| *usedarg = TRUE; /* mark it as used */ |
| |
| switch(letter) { |
| case '*': /* options without a short option */ |
| switch(subletter) { |
| case 'a': /* random-file */ |
| GetStr(&config->random_file, nextarg); |
| break; |
| case 'b': /* egd-file */ |
| GetStr(&config->egd_file, nextarg); |
| break; |
| case 'c': /* connect-timeout */ |
| if(str2num(&config->connecttimeout, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| case 'd': /* ciphers */ |
| GetStr(&config->cipher_list, nextarg); |
| break; |
| case 'e': /* --disable-epsv */ |
| config->disable_epsv ^= TRUE; |
| break; |
| #ifdef USE_ENVIRONMENT |
| case 'f': |
| config->writeenv ^= TRUE; |
| break; |
| #endif |
| case 'g': /* --trace */ |
| GetStr(&config->trace_dump, nextarg); |
| break; |
| case 'h': /* --trace-ascii */ |
| GetStr(&config->trace_dump, nextarg); |
| config->trace_ascii = TRUE; |
| break; |
| case 'i': /* --limit-rate */ |
| { |
| /* We support G, M, K too */ |
| char *unit; |
| curl_off_t value = curlx_strtoofft(nextarg, &unit, 0); |
| switch(nextarg[strlen(nextarg)-1]) { |
| case 'G': |
| case 'g': |
| value *= 1024*1024*1024; |
| break; |
| case 'M': |
| case 'm': |
| value *= 1024*1024; |
| break; |
| case 'K': |
| case 'k': |
| value *= 1024; |
| break; |
| } |
| config->recvpersecond = value; |
| config->sendpersecond = value; |
| } |
| break; |
| |
| case 'j': /* --compressed */ |
| config->encoding ^= TRUE; |
| break; |
| |
| case 'k': /* --digest */ |
| config->authtype = CURLAUTH_DIGEST; |
| break; |
| |
| case 'l': /* --negotiate */ |
| if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE) |
| config->authtype = CURLAUTH_GSSNEGOTIATE; |
| else |
| return PARAM_LIBCURL_DOESNT_SUPPORT; |
| break; |
| |
| case 'm': /* --ntlm */ |
| if(curlinfo->features & CURL_VERSION_NTLM) |
| config->authtype = CURLAUTH_NTLM; |
| else |
| return PARAM_LIBCURL_DOESNT_SUPPORT; |
| break; |
| |
| case 'n': /* --basic for completeness */ |
| config->authtype = CURLAUTH_BASIC; |
| break; |
| |
| case 'o': /* --anyauth, let libcurl pick it */ |
| config->authtype = CURLAUTH_ANY; |
| break; |
| |
| #ifdef __DJGPP__ |
| case 'p': /* --wdebug */ |
| dbug_init(); |
| break; |
| #endif |
| case 'q': /* --ftp-create-dirs */ |
| config->ftp_create_dirs ^= TRUE; |
| break; |
| |
| case 'r': /* --create-dirs */ |
| config->create_dirs = TRUE; |
| break; |
| |
| case 's': /* --max-redirs */ |
| /* specified max no of redirects (http(s)) */ |
| if(str2num(&config->maxredirs, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| |
| case 't': /* --proxy-ntlm */ |
| if(curlinfo->features & CURL_VERSION_NTLM) |
| config->proxyntlm ^= TRUE; |
| else |
| return PARAM_LIBCURL_DOESNT_SUPPORT; |
| break; |
| |
| case 'u': /* --crlf */ |
| /* LF -> CRLF conversinon? */ |
| config->crlf = TRUE; |
| break; |
| |
| case 'v': /* --stderr */ |
| if(strcmp(nextarg, "-")) { |
| config->errors = fopen(nextarg, "wt"); |
| config->errors_fopened = TRUE; |
| } |
| else |
| config->errors = stdout; |
| break; |
| case 'w': /* --interface */ |
| /* interface */ |
| GetStr(&config->iface, nextarg); |
| break; |
| case 'x': /* --krb4 */ |
| /* krb4 level string */ |
| if(curlinfo->features & CURL_VERSION_KERBEROS4) |
| GetStr(&config->krb4level, nextarg); |
| else |
| return PARAM_LIBCURL_DOESNT_SUPPORT; |
| break; |
| case 'y': /* --max-filesize */ |
| if(str2offset(&config->max_filesize, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| case 'z': /* --disable-eprt */ |
| config->disable_eprt ^= TRUE; |
| break; |
| |
| default: /* the URL! */ |
| { |
| struct getout *url; |
| if(config->url_get || (config->url_get=config->url_list)) { |
| /* there's a node here, if it already is filled-in continue to find |
| an "empty" node */ |
| while(config->url_get && (config->url_get->flags&GETOUT_URL)) |
| config->url_get = config->url_get->next; |
| } |
| |
| /* now there might or might not be an available node to fill in! */ |
| |
| if(config->url_get) |
| /* existing node */ |
| url = config->url_get; |
| else |
| /* there was no free node, create one! */ |
| url=new_getout(config); |
| |
| if(url) { |
| /* fill in the URL */ |
| GetStr(&url->url, nextarg); |
| url->flags |= GETOUT_URL; |
| } |
| } |
| } |
| break; |
| case '$': /* more options without a short option */ |
| switch(subletter) { |
| case 'a': /* --ftp-ssl */ |
| config->ftp_ssl ^= TRUE; |
| break; |
| case 'b': /* --ftp-pasv */ |
| if(config->ftpport) |
| free(config->ftpport); |
| config->ftpport = NULL; |
| break; |
| case 'c': /* --socks specifies a socks5 proxy to use */ |
| GetStr(&config->socks5proxy, nextarg); |
| break; |
| case 'd': /* --tcp-nodelay option */ |
| config->tcp_nodelay ^= TRUE; |
| break; |
| case 'e': /* --proxy-digest */ |
| config->proxydigest ^= TRUE; |
| break; |
| case 'f': /* --proxy-basic */ |
| config->proxybasic ^= TRUE; |
| break; |
| case 'g': /* --retry */ |
| if(str2num(&config->req_retry, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| case 'h': /* --retry-delay */ |
| if(str2num(&config->retry_delay, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| case 'i': /* --retry-max-time */ |
| if(str2num(&config->retry_maxtime, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| |
| case 'j': /* --3p-url */ |
| GetStr(&config->tp_url, nextarg); |
| break; |
| case 'k': /* --3p-user */ |
| GetStr(&config->tp_user, nextarg); |
| break; |
| case 'l': /* --3p-quote */ |
| /* QUOTE commands to send to source FTP server */ |
| err = PARAM_OK; |
| switch(nextarg[0]) { |
| case '-': |
| /* prefixed with a dash makes it a POST TRANSFER one */ |
| nextarg++; |
| err = add2list(&config->tp_postquote, nextarg); |
| break; |
| case '+': |
| /* prefixed with a plus makes it a just-before-transfer one */ |
| nextarg++; |
| err = add2list(&config->tp_prequote, nextarg); |
| break; |
| default: |
| err = add2list(&config->tp_quote, nextarg); |
| break; |
| } |
| if(err) |
| return err; |
| |
| break; |
| /* break */ |
| case 'm': /* --ftp-account */ |
| GetStr(&config->ftp_account, nextarg); |
| break; |
| } |
| break; |
| case '#': /* added 19990617 larsa */ |
| config->progressmode ^= CURL_PROGRESS_BAR; |
| break; |
| case '0': |
| /* HTTP version 1.0 */ |
| config->httpversion = CURL_HTTP_VERSION_1_0; |
| break; |
| case '1': |
| /* TLS version 1 */ |
| config->ssl_version = CURL_SSLVERSION_TLSv1; |
| break; |
| case '2': |
| /* SSL version 2 */ |
| config->ssl_version = CURL_SSLVERSION_SSLv2; |
| break; |
| case '3': |
| /* SSL version 3 */ |
| config->ssl_version = CURL_SSLVERSION_SSLv3; |
| break; |
| case '4': |
| /* IPv4 */ |
| config->ip_version = 4; |
| break; |
| case '6': |
| /* IPv6 */ |
| config->ip_version = 6; |
| break; |
| case 'a': |
| /* This makes the FTP sessions use APPE instead of STOR */ |
| config->conf ^= CONF_FTPAPPEND; |
| break; |
| case 'A': |
| /* This specifies the User-Agent name */ |
| GetStr(&config->useragent, nextarg); |
| break; |
| case 'b': /* cookie string coming up: */ |
| if(nextarg[0] == '@') { |
| nextarg++; |
| } |
| else if(strchr(nextarg, '=')) { |
| /* A cookie string must have a =-letter */ |
| GetStr(&config->cookie, nextarg); |
| break; |
| } |
| /* We have a cookie file to read from! */ |
| GetStr(&config->cookiefile, nextarg); |
| break; |
| case 'B': |
| /* use ASCII/text when transfering */ |
| config->conf ^= CONF_GETTEXT; |
| break; |
| case 'c': |
| /* get the file name to dump all cookies in */ |
| GetStr(&config->cookiejar, nextarg); |
| break; |
| case 'C': |
| /* This makes us continue an ftp transfer at given position */ |
| if(!curlx_strequal(nextarg, "-")) { |
| if(str2offset(&config->resume_from, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| config->resume_from_current = FALSE; |
| } |
| else { |
| config->resume_from_current = TRUE; |
| config->resume_from = 0; |
| } |
| config->use_resume=TRUE; |
| break; |
| case 'd': |
| /* postfield data */ |
| { |
| char *postdata=NULL; |
| |
| if('@' == *nextarg) { |
| /* the data begins with a '@' letter, it means that a file name |
| or - (stdin) follows */ |
| FILE *file; |
| |
| nextarg++; /* pass the @ */ |
| |
| if(curlx_strequal("-", nextarg)) |
| file = stdin; |
| else |
| file = fopen(nextarg, "rb"); |
| |
| if(subletter == 'b') /* forced binary */ |
| postdata = file2memory(file, &config->postfieldsize); |
| else |
| postdata = file2string(file); |
| if(file && (file != stdin)) |
| fclose(file); |
| } |
| else { |
| GetStr(&postdata, nextarg); |
| } |
| |
| if(config->postfields) { |
| /* we already have a string, we append this one |
| with a separating &-letter */ |
| char *oldpost=config->postfields; |
| config->postfields=aprintf("%s&%s", oldpost, postdata); |
| free(oldpost); |
| free(postdata); |
| } |
| else |
| config->postfields=postdata; |
| } |
| /* |
| We can't set the request type here, as this data might be used in |
| a simple GET if -G is used. Already or soon. |
| |
| if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) |
| return PARAM_BAD_USE; |
| */ |
| break; |
| case 'D': |
| /* dump-header to given file name */ |
| GetStr(&config->headerfile, nextarg); |
| break; |
| case 'e': |
| { |
| char *ptr = strstr(nextarg, ";auto"); |
| if(ptr) { |
| /* Automatic referer requested, this may be combined with a |
| set initial one */ |
| config->conf |= CONF_AUTO_REFERER; |
| *ptr = 0; /* zero terminate here */ |
| } |
| GetStr(&config->referer, nextarg); |
| } |
| break; |
| case 'E': |
| switch(subletter) { |
| case 'a': /* CA info PEM file */ |
| /* CA info PEM file */ |
| GetStr(&config->cacert, nextarg); |
| break; |
| case 'b': /* cert file type */ |
| GetStr(&config->cert_type, nextarg); |
| break; |
| case 'c': /* private key file */ |
| GetStr(&config->key, nextarg); |
| break; |
| case 'd': /* private key file type */ |
| GetStr(&config->key_type, nextarg); |
| break; |
| case 'e': /* private key passphrase */ |
| GetStr(&config->key_passwd, nextarg); |
| cleanarg(nextarg); |
| break; |
| case 'f': /* crypto engine */ |
| GetStr(&config->engine, nextarg); |
| if (config->engine && curlx_strequal(config->engine,"list")) |
| config->list_engines = TRUE; |
| break; |
| case 'g': /* CA info PEM file */ |
| /* CA cert directory */ |
| GetStr(&config->capath, nextarg); |
| break; |
| 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((int)nextarg[0])) ) |
| /* colon in the second column, followed by a backslash, and the |
| first character is an alphabetic letter: |
| |
| this is a drive letter colon */ |
| ptr = strchr(&nextarg[3], ':'); /* find the next one instead */ |
| #endif |
| if(ptr) { |
| /* we have a password too */ |
| *ptr=0; |
| ptr++; |
| GetStr(&config->key_passwd, ptr); |
| } |
| GetStr(&config->cert, nextarg); |
| cleanarg(nextarg); |
| } |
| } |
| break; |
| case 'f': |
| /* fail hard on errors */ |
| config->conf ^= CONF_FAILONERROR; |
| break; |
| case 'F': |
| /* "form data" simulation, this is a little advanced so lets do our best |
| to sort this out slowly and carefully */ |
| if(formparse(nextarg, |
| &config->httppost, |
| &config->last_post)) |
| return PARAM_BAD_USE; |
| if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq)) |
| return PARAM_BAD_USE; |
| break; |
| |
| case 'g': /* g disables URLglobbing */ |
| config->globoff ^= TRUE; |
| break; |
| |
| case 'G': /* HTTP GET */ |
| config->use_httpget = TRUE; |
| break; |
| |
| case 'h': /* h for help */ |
| help(); |
| return PARAM_HELP_REQUESTED; |
| case 'H': |
| /* A custom header to append to a list */ |
| err = add2list(&config->headers, nextarg); |
| if(err) |
| return err; |
| break; |
| case 'i': |
| config->conf ^= CONF_HEADER; /* include the HTTP header as well */ |
| break; |
| case 'j': |
| config->cookiesession ^= TRUE; |
| break; |
| case 'I': |
| /* |
| * This is a bit tricky. We either SET both bits, or we clear both |
| * bits. Let's not make any other outcomes from this. |
| */ |
| if((CONF_HEADER|CONF_NOBODY) != |
| (config->conf&(CONF_HEADER|CONF_NOBODY)) ) { |
| /* one of them weren't set, set both */ |
| config->conf |= (CONF_HEADER|CONF_NOBODY); |
| if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq)) |
| return PARAM_BAD_USE; |
| } |
| else { |
| /* both were set, clear both */ |
| config->conf &= ~(CONF_HEADER|CONF_NOBODY); |
| if(SetHTTPrequest(HTTPREQ_GET, &config->httpreq)) |
| return PARAM_BAD_USE; |
| } |
| break; |
| case 'k': /* allow insecure SSL connects */ |
| config->insecure_ok ^= TRUE; |
| break; |
| case 'K': /* parse config file */ |
| parseconfig(nextarg, config); |
| break; |
| case 'l': |
| config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */ |
| break; |
| case 'L': |
| config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */ |
| switch (subletter) { |
| case 't': |
| /* Continue to send authentication (user+password) when following |
| * locations, even when hostname changed */ |
| config->conf ^= CONF_UNRESTRICTED_AUTH; |
| break; |
| } |
| break; |
| case 'm': |
| /* specified max time */ |
| if(str2num(&config->timeout, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| break; |
| case 'M': /* M for manual, huge help */ |
| #ifdef USE_MANUAL |
| hugehelp(); |
| return PARAM_HELP_REQUESTED; |
| #else |
| fprintf(stderr, |
| "curl: built-in manual was disabled at build-time!\n"); |
| return PARAM_OPTION_UNKNOWN; |
| #endif |
| case 'n': |
| switch(subletter) { |
| case 'o': /* CA info PEM file */ |
| /* use .netrc or URL */ |
| config->conf ^= CONF_NETRC_OPT; |
| break; |
| default: |
| /* pick info from .netrc, if this is used for http, curl will |
| automatically enfore user+password with the request */ |
| config->conf ^= CONF_NETRC; |
| break; |
| } |
| break; |
| case 'N': |
| /* disable the output I/O buffering */ |
| config->nobuffer ^= 1; |
| break; |
| case 'o': |
| case 'O': |
| /* output file */ |
| { |
| struct getout *url; |
| if(config->url_out || (config->url_out=config->url_list)) { |
| /* there's a node here, if it already is filled-in continue to find |
| an "empty" node */ |
| while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE)) |
| config->url_out = config->url_out->next; |
| } |
| |
| /* now there might or might not be an available node to fill in! */ |
| |
| if(config->url_out) |
| /* existing node */ |
| url = config->url_out; |
| else |
| /* there was no free node, create one! */ |
| url=new_getout(config); |
| |
| if(url) { |
| /* fill in the outfile */ |
| if('o' == letter) |
| GetStr(&url->outfile, nextarg); |
| else { |
| url->outfile=NULL; /* leave it */ |
| url->flags |= GETOUT_USEREMOTE; |
| } |
| url->flags |= GETOUT_OUTFILE; |
| } |
| } |
| break; |
| case 'P': |
| /* This makes the FTP sessions use PORT instead of PASV */ |
| /* use <eth0> or <192.168.10.10> style addresses. Anything except |
| this will make us try to get the "default" address. |
| NOTE: this is a changed behaviour since the released 4.1! |
| */ |
| GetStr(&config->ftpport, nextarg); |
| break; |
| case 'p': |
| /* proxy tunnel for non-http protocols */ |
| config->proxytunnel ^= TRUE; |
| break; |
| |
| case 'q': /* if used first, already taken care of, we do it like |
| this so we don't cause an error! */ |
| break; |
| case 'Q': |
| /* QUOTE command to send to FTP server */ |
| err = PARAM_OK; |
| switch(nextarg[0]) { |
| case '-': |
| /* prefixed with a dash makes it a POST TRANSFER one */ |
| nextarg++; |
| err = add2list(&config->postquote, nextarg); |
| break; |
| case '+': |
| /* prefixed with a plus makes it a just-before-transfer one */ |
| nextarg++; |
| err = add2list(&config->prequote, nextarg); |
| break; |
| default: |
| err = add2list(&config->quote, nextarg); |
| break; |
| } |
| if(err) |
| return err; |
| break; |
| case 'r': |
| /* byte range requested */ |
| GetStr(&config->range, nextarg); |
| break; |
| case 'R': |
| /* use remote file's time */ |
| config->remote_time ^= TRUE; |
| break; |
| case 's': |
| /* don't show progress meter, don't show errors : */ |
| config->conf |= (CONF_MUTE|CONF_NOPROGRESS); |
| config->showerror ^= TRUE; /* toggle off */ |
| break; |
| case 'S': |
| /* show errors */ |
| config->showerror ^= TRUE; /* toggle on if used with -s */ |
| break; |
| case 't': |
| /* Telnet options */ |
| err = add2list(&config->telnet_options, nextarg); |
| if(err) |
| return err; |
| break; |
| case 'T': |
| /* we are uploading */ |
| { |
| struct getout *url; |
| if(config->url_out || (config->url_out=config->url_list)) { |
| /* there's a node here, if it already is filled-in continue to find |
| an "empty" node */ |
| while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD)) |
| config->url_out = config->url_out->next; |
| } |
| |
| /* now there might or might not be an available node to fill in! */ |
| |
| if(config->url_out) |
| /* existing node */ |
| url = config->url_out; |
| else |
| /* there was no free node, create one! */ |
| url=new_getout(config); |
| |
| if(url) { |
| url->flags |= GETOUT_UPLOAD; /* mark -T used */ |
| if(!*nextarg) |
| url->flags |= GETOUT_NOUPLOAD; |
| else { |
| /* "-" equals stdin, but keep the string around for now */ |
| GetStr(&url->infile, nextarg); |
| } |
| } |
| } |
| break; |
| case 'u': |
| /* user:password */ |
| GetStr(&config->userpwd, nextarg); |
| cleanarg(nextarg); |
| checkpasswd("host", &config->userpwd); |
| break; |
| case 'U': |
| /* Proxy user:password */ |
| GetStr(&config->proxyuserpwd, nextarg); |
| cleanarg(nextarg); |
| checkpasswd("proxy", &config->proxyuserpwd); |
| break; |
| case 'v': |
| config->conf ^= CONF_VERBOSE; /* talk a lot */ |
| break; |
| case 'V': |
| { |
| const char * const *proto; |
| |
| printf(CURL_ID "%s\n", curl_version()); |
| if (curlinfo->protocols) { |
| printf("Protocols: "); |
| for (proto=curlinfo->protocols; *proto; ++proto) { |
| printf("%s ", *proto); |
| } |
| puts(""); /* newline */ |
| } |
| if(curlinfo->features) { |
| unsigned int i; |
| struct feat { |
| const char *name; |
| int bitmask; |
| }; |
| static const struct feat feats[] = { |
| {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, |
| {"Debug", CURL_VERSION_DEBUG}, |
| {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, |
| {"IDN", CURL_VERSION_IDN}, |
| {"IPv6", CURL_VERSION_IPV6}, |
| {"Largefile", CURL_VERSION_LARGEFILE}, |
| {"NTLM", CURL_VERSION_NTLM}, |
| {"SPNEGO", CURL_VERSION_SPNEGO}, |
| {"SSL", CURL_VERSION_SSL}, |
| {"krb4", CURL_VERSION_KERBEROS4}, |
| {"libz", CURL_VERSION_LIBZ} |
| }; |
| printf("Features: "); |
| for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) { |
| if(curlinfo->features & feats[i].bitmask) |
| printf("%s ", feats[i].name); |
| } |
| puts(""); /* newline */ |
| } |
| } |
| return PARAM_HELP_REQUESTED; |
| case 'w': |
| /* get the output string */ |
| if('@' == *nextarg) { |
| /* the data begins with a '@' letter, it means that a file name |
| or - (stdin) follows */ |
| FILE *file; |
| nextarg++; /* pass the @ */ |
| if(curlx_strequal("-", nextarg)) |
| file = stdin; |
| else |
| file = fopen(nextarg, "r"); |
| config->writeout = file2string(file); |
| if(file && (file != stdin)) |
| fclose(file); |
| } |
| else |
| GetStr(&config->writeout, nextarg); |
| break; |
| case 'x': |
| /* proxy */ |
| GetStr(&config->proxy, nextarg); |
| break; |
| case 'X': |
| /* set custom request */ |
| GetStr(&config->customrequest, nextarg); |
| break; |
| case 'y': |
| /* low speed time */ |
| if(str2num(&config->low_speed_time, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| if(!config->low_speed_limit) |
| config->low_speed_limit = 1; |
| break; |
| case 'Y': |
| /* low speed limit */ |
| if(str2num(&config->low_speed_limit, nextarg)) |
| return PARAM_BAD_NUMERIC; |
| if(!config->low_speed_time) |
| config->low_speed_time=30; |
| break; |
| case 'z': /* time condition coming up */ |
| switch(*nextarg) { |
| case '+': |
| nextarg++; |
| default: |
| /* If-Modified-Since: (section 14.28 in RFC2068) */ |
| config->timecond = CURL_TIMECOND_IFMODSINCE; |
| break; |
| case '-': |
| /* If-Unmodified-Since: (section 14.24 in RFC2068) */ |
| config->timecond = CURL_TIMECOND_IFUNMODSINCE; |
| nextarg++; |
| break; |
| case '=': |
| /* Last-Modified: (section 14.29 in RFC2068) */ |
| config->timecond = CURL_TIMECOND_LASTMOD; |
| nextarg++; |
| break; |
| } |
| now=time(NULL); |
| config->condtime=curl_getdate(nextarg, &now); |
| if(-1 == (int)config->condtime) { |
| /* now let's see if it is a file name to get the time from instead! */ |
| struct_stat statbuf; |
| if(-1 == stat(nextarg, &statbuf)) { |
| /* failed, remove time condition */ |
| config->timecond = CURL_TIMECOND_NONE; |
| } |
| 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; |
| } |
| |
| |
| static void parseconfig(const char *filename, |
| struct Configurable *config) |
| { |
| int res; |
| FILE *file; |
| char filebuffer[512]; |
| bool usedarg; |
| char *home; |
| |
| if(!filename || !*filename) { |
| /* NULL or no file name attempts to load .curlrc from the homedir! */ |
| |
| #define CURLRC DOT_CHAR "curlrc" |
| |
| #ifndef AMIGA |
| filename = CURLRC; /* sensible default */ |
| home = homedir(); /* portable homedir finder */ |
| if(home) { |
| if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) { |
| snprintf(filebuffer, sizeof(filebuffer), |
| "%s%s%s", home, DIR_CHAR, CURLRC); |
| |
| filename = filebuffer; |
| } |
| free(home); /* we've used it, now free it */ |
| } |
| |
| # else /* AmigaOS */ |
| /* 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 isseparator(x) (((x)=='=') || ((x) == ':')) |
| |
| while (NULL != (aline = my_get_line(file))) { |
| lineno++; |
| line = aline; |
| alloced_param=FALSE; |
| |
| /* lines with # in the fist column is a comment! */ |
| while(*line && isspace((int)*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((int)*line) && !isseparator(*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((int)*line) || isseparator(*line))) |
| line++; |
| |
| /* the parameter starts here (unless quoted) */ |
| if(*line == '\"') { |
| char *ptr; |
| /* quoted parameter, do the qoute dance */ |
| line++; |
| param=strdup(line); /* parameter */ |
| alloced_param=TRUE; |
| |
| ptr=param; |
| while(*line && (*line != '\"')) { |
| if(*line == '\\') { |
| char out; |
| line++; |
| |
| /* default is to output the letter after the backslah */ |
| 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; |
| } |
| *ptr++=out; |
| line++; |
| } |
| else |
| *ptr++=*line++; |
| } |
| *ptr=0; /* always zero terminate */ |
| |
| } |
| else { |
| param=line; /* parameter starts here */ |
| while(*line && !isspace((int)*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); |
| fprintf(stderr, "%s:%d: warning: '%s' %s\n", |
| filename, lineno, option, reason); |
| } |
| } |
| |
| if(alloced_param) |
| { |
| free(param); |
| param = NULL; |
| } |
| |
| free(aline); |
| } |
| if(file != stdin) |
| fclose(file); |
| } |
| } |
| |
| 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 |
| } |
| |
| 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) */ |
| }; |
| |
| static int my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream) |
| { |
| int rc; |
| struct OutStruct *out=(struct OutStruct *)stream; |
| struct Configurable *config = out->config; |
| curl_off_t size = (curl_off_t)(sz * nmemb); /* typecast to prevent |
| warnings when converting from |
| unsigned to signed */ |
| if(out && !out->stream) { |
| /* open file for writing */ |
| out->stream=fopen(out->filename, "wb"); |
| if(!out->stream) |
| return -1; /* failure */ |
| } |
| |
| if(config->recvpersecond) { |
| /* |
| * We know when we received data the previous time. We know how much data |
| * we get now. Make sure that this is not faster than we are told to run. |
| * If we're faster, sleep a while *before* doing the fwrite() here. |
| */ |
| |
| struct timeval now; |
| long timediff; |
| long sleep_time; |
| |
| static curl_off_t addit = 0; |
| |
| now = curlx_tvnow(); |
| timediff = curlx_tvdiff(now, config->lastrecvtime); /* milliseconds */ |
| |
| if((config->recvpersecond > CURL_MAX_WRITE_SIZE) && (timediff < 100) ) { |
| /* If we allow a rather speedy transfer, add this amount for later |
| * checking. Also, do not modify the lastrecvtime as we will use a |
| * longer scope due to this addition. We wait for at least 100 ms to |
| * pass to get better values to do better math for the sleep. */ |
| addit += size; |
| } |
| else { |
| size += addit; /* add up the possibly added bonus rounds from the |
| zero timediff calls */ |
| addit = 0; /* clear the addition pool */ |
| |
| if( size*1000 > config->recvpersecond*timediff) { |
| /* figure out how many milliseconds to rest */ |
| sleep_time = (long)(size*1000/config->recvpersecond - timediff); |
| |
| /* |
| * Make sure we don't sleep for so long that we trigger the speed |
| * limit. This won't limit the bandwidth quite the way we've been |
| * asked to, but at least the transfer has a chance. |
| */ |
| if (config->low_speed_time > 0) |
| sleep_time = MIN(sleep_time,(config->low_speed_time * 1000) / 2); |
| |
| if(sleep_time > 0) { |
| go_sleep(sleep_time); |
| now = curlx_tvnow(); |
| } |
| } |
| config->lastrecvtime = now; |
| } |
| } |
| |
| rc = fwrite(buffer, sz, nmemb, out->stream); |
| |
| if((int)(sz * nmemb) == rc) { |
| /* we added this amount of data to the output */ |
| out->bytes += (sz * nmemb); |
| } |
| |
| if(config->nobuffer) |
| /* disable output buffering */ |
| fflush(out->stream); |
| |
| return rc; |
| } |
| |
| struct InStruct { |
| FILE *stream; |
| struct Configurable *config; |
| }; |
| |
| static curlioerr my_ioctl(CURL *handle, curliocmd cmd, void *userp) |
| { |
| struct InStruct *in=(struct InStruct *)userp; |
| (void)handle; /* not used in here */ |
| |
| switch(cmd) { |
| case CURLIOCMD_RESTARTREAD: |
| /* mr libcurl kindly asks as to rewind the read data stream to start */ |
| if(-1 == fseek(in->stream, 0, SEEK_SET)) |
| /* 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. */ |
| return CURLIOE_FAILRESTART; |
| |
| break; |
| |
| default: /* ignore unknown commands */ |
| return CURLIOE_UNKNOWNCMD; |
| } |
| return CURLIOE_OK; |
| } |
| |
| static int my_fread(void *buffer, size_t sz, size_t nmemb, void *userp) |
| { |
| int rc; |
| struct InStruct *in=(struct InStruct *)userp; |
| struct Configurable *config = in->config; |
| curl_off_t size = (curl_off_t)(sz * nmemb); /* typecast to prevent warnings |
| when converting from |
| unsigned to signed */ |
| |
| if(config->sendpersecond) { |
| /* |
| * We know when we sent data the previous time. We know how much data |
| * we sent. Make sure that this was not faster than we are told to run. |
| * If we're faster, sleep a while *before* doing the fread() here. |
| * Also, make no larger fread() than should be sent this second! |
| */ |
| |
| struct timeval now; |
| long timediff; |
| long sleep_time; |
| |
| static curl_off_t addit = 0; |
| |
| now = curlx_tvnow(); |
| timediff = curlx_tvdiff(now, config->lastsendtime); /* milliseconds */ |
| |
| if((config->sendpersecond > CURL_MAX_WRITE_SIZE) && |
| (timediff < 100)) { |
| /* |
| * We allow very fast transfers, then allow at least 100 ms between |
| * each sleeping mile-stone to create more accurate long-term rates. |
| */ |
| addit += size; |
| } |
| else { |
| /* If 'addit' is non-zero, it contains the total amount of bytes |
| uploaded during the last 'timediff' milliseconds. If it is zero, |
| we use the stored previous size. */ |
| curl_off_t xfered = addit?addit:(curl_off_t)config->lastsendsize; |
| addit = 0; /* clear it for the next round */ |
| |
| if( xfered*1000 > config->sendpersecond*timediff) { |
| /* figure out how many milliseconds to rest */ |
| sleep_time = (long)(xfered*1000/config->sendpersecond - timediff); |
| if(sleep_time > 0) { |
| go_sleep (sleep_time); |
| now = curlx_tvnow(); |
| } |
| } |
| config->lastsendtime = now; |
| |
| if(size > config->sendpersecond) { |
| /* lower the size to actually read */ |
| nmemb = (size_t)config->sendpersecond; |
| sz = 1; |
| } |
| } |
| |
| config->lastsendsize = sz*nmemb; |
| } |
| |
| rc = fread(buffer, sz, nmemb, in->stream); |
| #if 0 |
| fprintf(stderr, "CALLBACK returning %d bytes data\n", (int)rc); |
| #endif |
| return 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 */ |
| |
| 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); |
| i = 0; |
| 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) { |
| bar->width = atoi(colp); |
| 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 *text, |
| FILE *stream, unsigned char *ptr, size_t size, |
| bool nohex) |
| { |
| size_t i; |
| size_t c; |
| |
| unsigned int width=0x10; |
| |
| if(nohex) |
| /* without the hex output, we can fit more on screen */ |
| width = 0x40; |
| |
| fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size); |
| |
| for(i=0; i<size; i+= width) { |
| |
| fprintf(stream, "%04zx: ", i); |
| |
| if(!nohex) { |
| /* 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 (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) { |
| i+=(c+2-width); |
| break; |
| } |
| fprintf(stream, "%c", |
| (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.'); |
| /* check again for 0D0A, to avoid an extra \n if it's at width */ |
| if (nohex && (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; |
| (void)handle; /* prevent compiler warning */ |
| |
| if(!config->trace_stream) { |
| /* open for append */ |
| if(curlx_strequal("-", config->trace_dump)) |
| config->trace_stream = stdout; |
| else { |
| config->trace_stream = fopen(config->trace_dump, "w"); |
| config->trace_fopened = TRUE; |
| } |
| } |
| |
| if(config->trace_stream) |
| output = config->trace_stream; |
| |
| switch (type) { |
| case CURLINFO_TEXT: |
| fprintf(output, "== Info: %s", 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(text, output, data, size, config->trace_ascii); |
| 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->cookie) |
| free(config->cookie); |
| if(config->cookiefile) |
| free(config->cookiefile); |
| if(config->krb4level) |
| free(config->krb4level); |
| 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->cacert) |
| free(config->cacert); |
| if(config->capath) |
| free(config->capath); |
| if(config->cookiejar) |
| free(config->cookiejar); |
| if(config->tp_url) |
| free(config->tp_url); |
| if(config->tp_user) |
| free(config->tp_user); |
| if(config->ftp_account) |
| free(config->ftp_account); |
| |
| 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->tp_quote); |
| curl_slist_free_all(config->tp_prequote); |
| curl_slist_free_all(config->tp_postquote); |
| curl_slist_free_all(config->headers); |
| } |
| |
| #if defined(WIN32) && !defined(__CYGWIN32__) |
| |
| /* Function to find CACert bundle on a Win32 platform using SearchPath. |
| * (SearchPath is defined in windows.h, which is #included into libcurl) |
| * (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 = (char *) 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 int |
| operate(struct Configurable *config, int argc, char *argv[]) |
| { |
| char errorbuffer[CURL_ERROR_SIZE]; |
| char useragent[128]; /* 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; |
| |
| char *url = NULL; |
| |
| URLGlob *urls=NULL; |
| URLGlob *inglob=NULL; |
| int urlnum; |
| int infilenum; |
| char *outfiles; |
| char *infiles; /* might a glob pattern */ |
| char *uploadfile=NULL; /* a single file, never a glob */ |
| |
| int separator = 0; |
| |
| FILE *infd = stdin; |
| bool infdfopen; |
| FILE *headerfilep = NULL; |
| char *urlbuffer=NULL; |
| curl_off_t uploadfilesize; /* -1 means unknown */ |
| bool stillflags=TRUE; |
| |
| bool allocuseragent=FALSE; |
| |
| char *httpgetfields=NULL; |
| |
| CURL *curl; |
| int res = 0; |
| int i; |
| int up; /* upload file counter within a single upload glob */ |
| long retry_sleep_default = config->retry_delay? |
| config->retry_delay*1000:RETRY_SLEEP_DEFAULT; /* ms */ |
| long retry_numretries; |
| long retry_sleep = retry_sleep_default; |
| long response; |
| struct timeval retrystart; |
| |
| char *env; |
| #ifdef CURLDEBUG |
| /* this sends all memory debug messages to a logfile named memdump */ |
| env = curlx_getenv("CURL_MEMDEBUG"); |
| if(env) { |
| /* use the value as file name */ |
| char *s = strdup(env); |
| curl_free(env); |
| curl_memdebug(s); |
| free(s); |
| /* this weird strdup() and stuff here is to make the curl_free() get |
| called before the memdebug() as otherwise the memdebug tracing will |
| with tracing a free() without an alloc! */ |
| } |
| env = curlx_getenv("CURL_MEMLIMIT"); |
| if(env) { |
| curl_memlimit(atoi(env)); |
| curl_free(env); |
| } |
| #endif |
| |
| memset(&outs,0,sizeof(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 */ |
| if (main_init() != CURLE_OK) { |
| helpf("error initializing curl library\n"); |
| return CURLE_FAILED_INIT; |
| } |
| config->postfieldsize = -1; |
| config->showerror=TRUE; |
| config->conf=CONF_DEFAULT; |
| config->use_httpget=FALSE; |
| config->create_dirs=FALSE; |
| config->lastrecvtime = curlx_tvnow(); |
| config->lastsendtime = curlx_tvnow(); |
| |
| 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); |
| } |
| |
| if ((argc < 2) && !config->url_list) { |
| helpf(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) { |
| const char *reason = param2text(res); |
| if(res != PARAM_HELP_REQUESTED) |
| helpf("option %s: %s\n", origopt, reason); |
| clean_getout(config); |
| return CURLE_FAILED_INIT; |
| } |
| |
| 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; |
| } |
| } |
| |
| if((!config->url_list || !config->url_list->url) && !config->list_engines) { |
| clean_getout(config); |
| helpf("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 (non-cygwin), 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); |
| curl_free(env); |
| } |
| #if defined(WIN32) && !defined(__CYGWIN32__) |
| 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->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET), |
| &config->httpreq)) { |
| free(httpgetfields); |
| return PARAM_BAD_USE; |
| } |
| } |
| else { |
| if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) |
| return PARAM_BAD_USE; |
| } |
| } |
| |
| /* |
| * 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; |
| } |
| |
| |
| 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; |
| headerfilep=NULL; |
| } |
| else |
| headerfilep=stdout; |
| heads.stream = headerfilep; |
| heads.config = config; |
| } |
| |
| /* loop through the list of given URLs */ |
| while(urlnode) { |
| char *dourl; |
| |
| /* 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 */ |
| |
| /* save outfile pattern before expansion */ |
| outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL; |
| |
| infiles = urlnode->infile; |
| |
| if(!config->globoff && infiles) { |
| /* Unless explicitly shut off */ |
| res = glob_url(&inglob, infiles, &infilenum, |
| config->showerror? |
| (config->errors?config->errors:stderr):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)); |
| up++) { |
| 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?config->errors:stderr):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))); |
| i++) { |
| char *outfile; |
| 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) { |
| /* Find and get the remote file name */ |
| char * pc =strstr(url, "://"); |
| if(pc) |
| pc+=3; |
| else |
| pc=url; |
| pc = strrchr(pc, '/'); |
| |
| if(pc) { |
| /* duplicate the string beyond the slash */ |
| pc++; |
| outfile = *pc ? strdup(pc): NULL; |
| } |
| if(!outfile || !*outfile) { |
| helpf("Remote file name has no length!\n"); |
| res = CURLE_WRITE_ERROR; |
| free(url); |
| break; |
| } |
| #if defined(__DJGPP__) |
| { |
| /* This is for DOS, and then we do some major replacing of |
| bad characters in the file name before using it */ |
| char file1 [PATH_MAX]; |
| |
| strcpy(file1, msdosify(outfile)); |
| free (outfile); |
| outfile = strdup (rename_if_dos_device_name(file1)); |
| } |
| #endif /* __DJGPP__ */ |
| } |
| 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 */ |
| fprintf(stderr, "bad output glob!\n"); |
| free(url); |
| res = CURLE_FAILED_INIT; |
| break; |
| } |
| } |
| |
| /* Create the directory hierarchy, if not pre-existant to a multiple |
| file output call */ |
| |
| if(config->create_dirs && |
| (-1 == create_dir_hierarchy(outfile))) |
| return CURLE_WRITE_ERROR; |
| |
| 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; |
| |
| 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("Can't open '%s'!\n", outfile); |
| return CURLE_WRITE_ERROR; |
| } |
| } |
| else { |
| outs.stream = NULL; /* open when needed */ |
| } |
| } |
| infdfopen=FALSE; |
| if(uploadfile && !curlx_strequal(uploadfile, "-")) { |
| /* |
| * We have specified a file to upload and it isn't "-". |
| */ |
| struct_stat fileinfo; |
| |
| /* 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. */ |
| char *filep = strrchr(uploadfile, '/'); |
| char *file2 = strrchr(filep?filep:uploadfile, '\\'); |
| |
| if(file2) |
| filep = file2+1; |
| else if(filep) |
| filep++; |
| else |
| filep = uploadfile; |
| |
| /* URL encode the file name */ |
| filep = curl_escape(filep, 0 /* use strlen */); |
| |
| if(filep) { |
| |
| urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 3); |
| if(!urlbuffer) { |
| helpf("out of memory\n"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| if(ptr) |
| /* there is a trailing slash on the URL */ |
| sprintf(urlbuffer, "%s%s", url, filep); |
| else |
| /* thers is no trailing slash on the URL */ |
| sprintf(urlbuffer, "%s/%s", url, filep); |
| |
| curl_free(filep); |
| |
| free(url); |
| url = urlbuffer; /* use our new URL instead! */ |
| } |
| } |
| /* 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=(FILE *) fopen(uploadfile, "rb"); |
| if (!infd || stat(uploadfile, &fileinfo)) { |
| helpf("Can't open '%s'!\n", uploadfile); |
| return CURLE_READ_ERROR; |
| } |
| infdfopen=TRUE; |
| uploadfilesize=fileinfo.st_size; |
| |
| } |
| else if(uploadfile && curlx_strequal(uploadfile, "-")) { |
| infd = stdin; |
| } |
| |
| if(uploadfile && config->resume_from_current) |
| config->resume_from = -1; /* -1 will then force get-it-yourself */ |
| |
| if(outs.stream && isatty(fileno(outs.stream))) |
| /* we send the output to a tty, therefore we switch off the progress |
| meter */ |
| config->conf |= CONF_NOPROGRESS; |
| |
| if (urlnum > 1 && !(config->conf&CONF_MUTE)) { |
| fprintf(stderr, "\n[%d/%d]: %s --> %s\n", |
| i+1, urlnum, url, outfile ? outfile : "<stdout>"); |
| if (separator) |
| printf("%s%s\n", CURLseparator, url); |
| } |
| if (httpgetfields) { |
| /* 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=(char *)malloc(strlen(url) + strlen(httpgetfields) + 2); |
| if(!urlbuffer) { |
| helpf("out of memory\n"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| 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; |
| |
| #ifdef O_BINARY |
| if(!outfile && !(config->conf & CONF_GETTEXT)) { |
| /* We get the output to stdout and we have not got the ASCII/text flag, |
| then set stdout to be binary */ |
| setmode( fileno(stdout), O_BINARY ); |
| } |
| #endif |
| |
| if(1 == config->tcp_nodelay) |
| curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1); |
| |
| /* where to store */ |
| curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs); |
| /* what call to write */ |
| curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); |
| |
| /* for uploads */ |
| input.stream = infd; |
| input.config = config; |
| curl_easy_setopt(curl, CURLOPT_READDATA, &input); |
| /* what call to read */ |
| curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread); |
| |
| /* libcurl 7.12.3 business: */ |
| curl_easy_setopt(curl, CURLOPT_IOCTLDATA, &input); |
| curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, my_ioctl); |
| |
| if(config->recvpersecond) { |
| /* tell libcurl to use a smaller sized buffer as it allows us to |
| make better sleeps! 7.9.9 stuff! */ |
| curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond); |
| } |
| |
| /* size of uploaded file: */ |
| curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); |
| curl_easy_setopt(curl, CURLOPT_URL, url); /* what to fetch */ |
| curl_easy_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */ |
| curl_easy_setopt(curl, CURLOPT_HEADER, config->conf&CONF_HEADER); |
| curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config->conf&CONF_NOPROGRESS); |
| curl_easy_setopt(curl, CURLOPT_NOBODY, config->conf&CONF_NOBODY); |
| curl_easy_setopt(curl, CURLOPT_FAILONERROR, |
| config->conf&CONF_FAILONERROR); |
| curl_easy_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE); |
| curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, |
| config->conf&CONF_FTPLISTONLY); |
| curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND); |
| |
| if (config->conf&CONF_NETRC_OPT) |
| curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); |
| else if (config->conf&CONF_NETRC) |
| curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); |
| else |
| curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); |
| |
| curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, |
| config->conf&CONF_FOLLOWLOCATION); |
| curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, |
| config->conf&CONF_UNRESTRICTED_AUTH); |
| curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT); |
| curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd); |
| curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); |
| curl_easy_setopt(curl, CURLOPT_RANGE, config->range); |
| curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); |
| curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout); |
| |
| switch(config->httpreq) { |
| case HTTPREQ_SIMPLEPOST: |
| curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields); |
| curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize); |
| break; |
| case HTTPREQ_POST: |
| curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost); |
| break; |
| default: |
| break; |
| } |
| curl_easy_setopt(curl, CURLOPT_REFERER, config->referer); |
| curl_easy_setopt(curl, CURLOPT_AUTOREFERER, |
| config->conf&CONF_AUTO_REFERER); |
| curl_easy_setopt(curl, CURLOPT_USERAGENT, config->useragent); |
| curl_easy_setopt(curl, CURLOPT_FTPPORT, config->ftpport); |
| curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit); |
| curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); |
| curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, |
| config->use_resume?config->resume_from:0); |
| curl_easy_setopt(curl, CURLOPT_COOKIE, config->cookie); |
| curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers); |
| curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert); |
| curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type); |
| curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key); |
| curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type); |
| curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd); |
| |
| /* default to strict verifyhost */ |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); |
| if(config->cacert || config->capath) { |
| if (config->cacert) |
| curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert); |
| |
| if (config->capath) |
| curl_easy_setopt(curl, CURLOPT_CAPATH, config->capath); |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE); |
| } |
| if(config->insecure_ok) { |
| /* new stuff needed for libcurl 7.10 */ |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1); |
| } |
| |
| if((config->conf&CONF_NOBODY) || |
| config->remote_time) { |
| /* no body or use remote time */ |
| curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE); |
| } |
| |
| if (config->maxredirs) |
| curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); |
| else |
| curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS); |
| |
| curl_easy_setopt(curl, CURLOPT_CRLF, config->crlf); |
| curl_easy_setopt(curl, CURLOPT_QUOTE, config->quote); |
| curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config->postquote); |
| curl_easy_setopt(curl, CURLOPT_PREQUOTE, config->prequote); |
| curl_easy_setopt(curl, CURLOPT_WRITEHEADER, |
| config->headerfile?&heads:NULL); |
| curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile); |
| /* cookie jar was added in 7.9 */ |
| if(config->cookiejar) |
| curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar); |
| /* cookie session added in 7.9.7 */ |
| curl_easy_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession); |
| |
| curl_easy_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version); |
| curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config->timecond); |
| curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config->condtime); |
| curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); |
| curl_easy_setopt(curl, CURLOPT_STDERR, config->errors); |
| |
| /* three new ones in libcurl 7.3: */ |
| curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); |
| curl_easy_setopt(curl, CURLOPT_INTERFACE, config->iface); |
| curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level); |
| |
| progressbarinit(&progressbar, config); |
| if((config->progressmode == CURL_PROGRESS_BAR) && |
| !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) { |
| /* we want the alternative style, then we have to implement it |
| ourselves! */ |
| curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress); |
| curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar); |
| } |
| |
| /* new in libcurl 7.6.2: */ |
| curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); |
| |
| /* new in libcurl 7.7: */ |
| curl_easy_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file); |
| curl_easy_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file); |
| curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout); |
| |
| if(config->cipher_list) |
| curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list); |
| |
| if(config->httpversion) |
| curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion); |
| |
| /* new in libcurl 7.9.2: */ |
| if(config->disable_epsv) |
| /* disable it */ |
| curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE); |
| |
| /* new in libcurl 7.10.5 */ |
| if(config->disable_eprt) |
| /* disable it */ |
| curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE); |
| |
| /* new in libcurl 7.10.6 (default is Basic) */ |
| if(config->authtype) |
| curl_easy_setopt(curl, CURLOPT_HTTPAUTH, config->authtype); |
| |
| /* new in curl 7.9.7 */ |
| if(config->trace_dump) { |
| curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); |
| curl_easy_setopt(curl, CURLOPT_DEBUGDATA, config); |
| config->conf |= CONF_VERBOSE; /* force verbose */ |
| } |
| curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE); |
| |
| res = CURLE_OK; |
| |
| /* new in curl ?? */ |
| if (config->engine) { |
| res = curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine); |
| curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1); |
| } |
| |
| if (res != CURLE_OK) |
| goto show_error; |
| |
| /* new in curl 7.10 */ |
| curl_easy_setopt(curl, CURLOPT_ENCODING, |
| (config->encoding) ? "" : NULL); |
| |
| /* new in curl 7.10.7 */ |
| curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, |
| config->ftp_create_dirs); |
| if(config->proxyntlm) |
| curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM); |
| else if(config->proxydigest) |
| curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); |
| else if(config->proxybasic) |
| curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); |
| |
| /* new in curl 7.10.8 */ |
| if(config->max_filesize) |
| curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, |
| config->max_filesize); |
| |
| if(4 == config->ip_version) |
| curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); |
| else if(6 == config->ip_version) |
| curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); |
| else |
| curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); |
| |
| /* new in curl 7.11.0 */ |
| if(config->ftp_ssl) |
| curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY); |
| |
| /* new in curl 7.11.1 */ |
| if(config->socks5proxy) { |
| curl_easy_setopt(curl, CURLOPT_PROXY, config->socks5proxy); |
| curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); |
| } |
| |
| /* curl 7.13.0 */ |
| curl_easy_setopt(curl, CURLOPT_SOURCE_URL, config->tp_url); |
| curl_easy_setopt(curl, CURLOPT_SOURCE_USERPWD, config->tp_user); |
| curl_easy_setopt(curl, CURLOPT_SOURCE_PREQUOTE, config->tp_prequote); |
| curl_easy_setopt(curl, CURLOPT_SOURCE_POSTQUOTE, config->tp_postquote); |
| curl_easy_setopt(curl, CURLOPT_SOURCE_QUOTE, config->tp_quote); |
| curl_easy_setopt(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); |
| |
| retry_numretries = config->req_retry; |
| |
| retrystart = curlx_tvnow(); |
| |
| do { |
| res = curl_easy_perform(curl); |
| |
| /* if retry-max-time is non-zero, make sure we haven't exceeded the |
| time */ |
| if(retry_numretries && |
| (!config->retry_maxtime || |
| (curlx_tvdiff(curlx_tvnow(), retrystart)< |
| config->retry_maxtime*1000)) ) { |
| enum { |
| RETRY_NO, |
| RETRY_TIMEOUT, |
| RETRY_HTTP, |
| RETRY_FTP, |
| RETRY_LAST /* not used */ |
| } retry = RETRY_NO; |
| if(CURLE_OPERATION_TIMEDOUT == res) |
| /* retry timeout always */ |
| retry = RETRY_TIMEOUT; |
| else if(CURLE_OK == res) { |
| /* Check for HTTP transient errors */ |
| char *url=NULL; |
| curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); |
| if(url && |
| curlx_strnequal(url, "http", 4)) { |
| /* 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((CURLE_FTP_USER_PASSWORD_INCORRECT == res) || |
| (CURLE_LOGIN_DENIED == res)) { |
| curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
| |
| if(response/100 == 5) |
| /* |
| * This is typically when the FTP server only allows a certain |
| * amount of users and we are not one of them. It mostly |
| * returns 530 in this case, but all 5xx codes are transient. |
| */ |
| retry = RETRY_FTP; |
| } |
| |
| if(retry) { |
| if(!(config->conf&CONF_MUTE)) { |
| static const char * const m[]={NULL, |
| "timeout", |
| "HTTP error", |
| "FTP error" |
| }; |
| fprintf(stderr, "Transient problem: %s\n" |
| "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->conf&CONF_MUTE)) |
| fprintf(stderr, "Throwing away " CURL_FORMAT_OFF_T |
| " bytes\n", outs.bytes); |
| fflush(outs.stream); |
| /* truncate file at the position where we started appending */ |
| ftruncate( fileno(outs.stream), outs.init); |
| /* now seek to the end of the file, the position where we |
| just truncated the file */ |
| fseek(outs.stream, 0, SEEK_END); |
| 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; |
| |
| } while(1); |
| |
| 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 (!config->showerror) { |
| vms_show = VMSSTS_HIDE; |
| } |
| #else |
| if((res!=CURLE_OK) && config->showerror) { |
| fprintf(config->errors, "curl: (%d) %s\n", (int)res, |
| errorbuffer[0]? errorbuffer: |
| curl_easy_strerror((CURLcode)res)); |
| if(CURLE_SSL_CACERT == res) { |
| #define CURL_CA_CERT_ERRORMSG1 \ |
| "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \ |
| "curl performs SSL certificate verification by default, using a \"bundle\"\n" \ |
| " of Certificate Authority (CA) public keys (CA certs). The default\n" \ |
| " bundle is named curl-ca-bundle.crt; you can specify an alternate file\n" \ |
| " using the --cacert option.\n" |
| |
| #define CURL_CA_CERT_ERRORMSG2 \ |
| "If this HTTPS server uses a certificate signed by a CA represented in\n" \ |
| " the bundle, the certificate verification probably failed due to a\n" \ |
| " problem with the certificate (it might be expired, or the name might\n" \ |
| " not match the domain name in the URL).\n" \ |
| "If you'd like to turn off curl's verification of the certificate, use\n" \ |
| " the -k (or --insecure) option.\n" |
| |
| fprintf(config->errors, "%s%s", |
| CURL_CA_CERT_ERRORMSG1, |
| CURL_CA_CERT_ERRORMSG2 ); |
| } |
| } |
| #endif |
| |
| if (outfile && !curlx_strequal(outfile, "-") && outs.stream) |
| fclose(outs.stream); |
| |
| #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) { |
| /* as 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 |
| |
| if(headerfilep) |
| fclose(headerfilep); |
| |
| if(url) |
| free(url); |
| |
| if(outfile) |
| free(outfile); |
| |
| if(infdfopen) |
| fclose(infd); |
| |
| } /* loop to the next URL */ |
| |
| if(urls) |
| /* cleanup memory used for URL globbing patterns */ |
| glob_cleanup(urls); |
| |
| if(uploadfile) |
| free(uploadfile); |
| |
| } /* loop to the next globbed upload file */ |
| |
| if(inglob) |
| glob_cleanup(inglob); |
| |
| 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); |
| |
| if(config->headerfile && !headerfilep && heads.stream) |
| fclose(heads.stream); |
| |
| if(allocuseragent) |
| free(config->useragent); |
| |
| /* cleanup the curl handle! */ |
| curl_easy_cleanup(curl); |
| |
| if(config->trace_fopened && config->trace_stream) |
| fclose(config->trace_stream); |
| |
| if(config->errors_fopened) |
| fclose(config->errors); |
| |
| main_free(); /* cleanup */ |
| |
| return res; |
| } |
| |
| static void checkfds(void); |
| |
| 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 ) |
| pipe(fd); |
| |
| close(fd[0]); |
| close(fd[1]); |
| #endif |
| } |
| |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| int res; |
| struct Configurable config; |
| memset(&config, 0, sizeof(struct Configurable)); |
| |
| checkfds(); |
| |
| res = operate(&config, argc, argv); |
| free_config_fields(&config); |
| |
| #ifdef __NOVELL_LIBC__ |
| pressanykey(); |
| #endif |
| #ifdef VMS |
| if (res > CURL_LAST) res = CURL_LAST; /* If CURL_LAST exceeded then */ |
| return (vms_cond[res]|vms_show); /* curlmsg.h is out of sync. */ |
| #else |
| return res; |
| #endif |
| } |
| |
| static char *my_get_line(FILE *fp) |
| { |
| char buf[4096]; |
| char *nl = NULL; |
| char *retval = NULL; |
| |
| do { |
| if (NULL == fgets(buf, sizeof(buf), fp)) |
| break; |
| if (NULL == retval) |
| retval = strdup(buf); |
| else { |
| if (NULL == (retval = realloc(retval, |
| strlen(retval) + strlen(buf) + 1))) |
| break; |
| strcat(retval, buf); |
| } |
| } |
| while (NULL == (nl = strchr(retval, '\n'))); |
| |
| if (NULL != nl) |
| *nl = '\0'; |
| |
| return retval; |
| } |
| |
| |
| /* 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(char *outfile) |
| { |
| char *tempdir; |
| char *tempdir2; |
| char *outdup; |
| char *dirbuildup; |
| int result=0; |
| |
| outdup = strdup(outfile); |
| dirbuildup = malloc(sizeof(char) * strlen(outfile)); |
| if(!dirbuildup) |
| 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) { |
| if (strlen(dirbuildup) > 0) |
| sprintf(dirbuildup,"%s%s%s",dirbuildup, DIR_CHAR, tempdir); |
| else { |
| if (0 != strncmp(outdup, DIR_CHAR, 1)) |
| sprintf(dirbuildup,"%s",tempdir); |
| else |
| sprintf(dirbuildup,"%s%s", DIR_CHAR, tempdir); |
| } |
| if (access(dirbuildup, F_OK) == -1) { |
| result = mkdir(dirbuildup,(mode_t)0000750); |
| if (-1 == result) { |
| switch (errno) { |
| #ifdef EACCES |
| case EACCES: |
| fprintf(stderr,"You don't have permission to create %s.\n", |
| dirbuildup); |
| break; |
| #endif |
| #ifdef ENAMETOOLONG |
| case ENAMETOOLONG: |
| fprintf(stderr,"The directory name %s is too long.\n", |
| dirbuildup); |
| break; |
| #endif |
| #ifdef EROFS |
| case EROFS: |
| fprintf(stderr,"%s resides on a read-only file system.\n", |
| dirbuildup); |
| break; |
| #endif |
| #ifdef ENOSPC |
| case ENOSPC: |
| fprintf(stderr,"No space left on the file system that will " |
| "contain the directory %s.\n", dirbuildup); |
| break; |
| #endif |
| #ifdef EDQUOT |
| case EDQUOT: |
| fprintf(stderr,"Cannot create directory %s because you " |
| "exceeded your quota.\n", dirbuildup); |
| break; |
| #endif |
| default : |
| fprintf(stderr,"Error creating directory %s.\n", dirbuildup); |
| break; |
| } |
| break; /* get out of loop */ |
| } |
| } |
| } |
| tempdir = tempdir2; |
| } |
| free(dirbuildup); |
| free(outdup); |
| |
| return result; /* 0 is fine, -1 is badness */ |
| } |
| |
| #ifdef __DJGPP__ |
| /* The following functions are taken with modification from the DJGPP |
| * port of tar 1.12. They use algorithms originally from DJTAR. */ |
| |
| char * |
| msdosify (char *file_name) |
| { |
| static char dos_name[PATH_MAX]; |
| static const char illegal_chars_dos[] = ".+, ;=[]|<>\\\":?*"; |
| static const char *illegal_chars_w95 = &illegal_chars_dos[8]; |
| int idx, dot_idx; |
| char *s = file_name, *d = dos_name; |
| const char *illegal_aliens = illegal_chars_dos; |
| size_t len = sizeof (illegal_chars_dos) - 1; |
| int lfn = 0; |
| |
| /* Support for Windows 9X VFAT systems, when available. */ |
| if (_use_lfn (file_name)) |
| lfn = 1; |
| if (lfn) { |
| 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; 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; |
| } |
| |
| 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]; |
| |
| strcpy (fname, file_name); |
| base = basename (fname); |
| if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { |
| size_t blen = strlen (base); |
| |
| /* Prepend a '_'. */ |
| memmove (base + 1, base, blen + 1); |
| base[0] = '_'; |
| strcpy (file_name, fname); |
| } |
| return file_name; |
| } |
| |
| #endif /* __DJGPP__ */ |