Newer
Older
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
***************************************************************************/
/* This is now designed to have its own local setup.h */
#include "setup.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
#ifdef USE_ENVIRONMENT
#include "writeenv.h"
#endif
#if defined(WIN32)&&!defined(__CYGWIN32__)
Daniel Stenberg
committed
#ifdef TIME_WITH_SYS_TIME
/* We can include both fine */
#include <sys/time.h>
Daniel Stenberg
committed
#include <time.h>
#else
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#else
# include <time.h>
#endif
#endif
Daniel Stenberg
committed
#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
Daniel Stenberg
committed
#include <utime.h>
Daniel Stenberg
committed
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#endif
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#ifdef CURLDEBUG
/* 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 "../lib/memdebug.h"
#endif
#define DEFAULT_MAXREDIRS 50L
#ifdef __DJGPP__
void *xmalloc(size_t);
char *msdosify(char *);
char *rename_if_dos_device_name(char *);
void xfree(void *);
#include <limits.h>
#include <fcntl.h>
struct pollfd {
int fd;
int events; /* in param: what to poll for */
int revents; /* out param: what events occured */
};
int poll (struct pollfd *, int, int);
#endif /* __DJGPP__ */
#define CURL_PROGRESS_STATS 0 /* default progress display */
#define CURL_PROGRESS_BAR 1
Daniel Stenberg
committed
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_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
#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
int vms_show = 0;
#define FAC_CURL 0xC01
#define FAC_SYSTEM 0
#define MSG_NORMAL 0
#define VMS_STS(c,f,e,s) (((c&0xF)<<28)|((f&0xFFF)<<16)|((e&0x1FFF)<3)|(s&7))
#define VMSSTS_HIDE VMS_STS(1,0,0,0)
#define SEV_WARNING 0
#define SEV_SUCCESS 1
#define SEV_ERROR 2
#define SEV_INFO 3 /* success, with an extra hint */
#define SEV_FATAL 4
globalvalue int CURL_UNSUPPROTO; /* these are from curlmsg.msg file..... */
globalvalue int CURL_FAILINIT;
globalvalue int CURL_BADURLSYN;
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
globalvalue int CURL_BADURLUSER;
globalvalue int CURL_BADPROXY;
globalvalue int CURL_BADHOST;
globalvalue int CURL_FAILHOST;
globalvalue int CURL_FTPUNKREPLY;
globalvalue int CURL_FTPNOACC;
globalvalue int CURL_FTPUSRPW;
globalvalue int CURL_FTPBADPASS;
globalvalue int CURL_FTPBADUSER;
globalvalue int CURL_FTPBADPASV;
globalvalue int CURL_FTPBAD227;
globalvalue int CURL_FTPBADHOST227;
globalvalue int CURL_FTPNORECONN;
globalvalue int CURL_FTPNOBIN;
globalvalue int CURL_PARTIALFILE;
globalvalue int CURL_FTPNORETR;
globalvalue int CURL_FTPWRITERR;
globalvalue int CURL_FTPNOQUOTE;
globalvalue int CURL_HTTPPNF;
globalvalue int CURL_WRITERR;
globalvalue int CURL_BADUSER;
globalvalue int CURL_FTPNOSTOR;
globalvalue int CURL_READERR;
globalvalue int CURL_OUTOFMEM;
globalvalue int CURL_TIMEOUT;
globalvalue int CURL_FTPNOASCII;
globalvalue int CURL_FTPNOPORT;
globalvalue int CURL_FTPNOREST;
globalvalue int CURL_FTPNOSIZE;
globalvalue int CURL_HTTPRNGERR;
globalvalue int CURL_HTTPPOSTERR;
globalvalue int CURL_SSLNOCONN;
globalvalue int CURL_FTPBADRESUME;
globalvalue int CURL_FILENOACC;
globalvalue int CURL_LDAPNOBIND;
globalvalue int CURL_LDAPNOSRCH;
globalvalue int CURL_LDAPNOLIB;
globalvalue int CURL_LDAPNOFUNC;
globalvalue int CURL_ABORTCB;
globalvalue int CURL_BADPARAM;
globalvalue int CURL_BADORDER;
globalvalue int CURL_BADPWD;
globalvalue int CURL_MNYREDIR;
globalvalue int CURL_UNKTELNET;
globalvalue int CURL_UNKMSG;
globalvalue int CURL_BADSSLCERT;
globalvalue int CURL_SRVNOERR;
globalvalue int CURL_MAXMSG;
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
long vms_cond[] = {
VMS_STS(1,FAC_SYSTEM,MSG_NORMAL,SEV_SUCCESS),
CURL_UNSUPPROTO, /* these are from curlmsg.msg file..... */
CURL_FAILINIT,
CURL_BADURLSYN,
CURL_BADURLUSER,
CURL_BADPROXY,
CURL_BADHOST,
CURL_FAILHOST,
CURL_FTPUNKREPLY,
CURL_FTPNOACC,
CURL_FTPUSRPW,
CURL_FTPBADPASS,
CURL_FTPBADUSER,
CURL_FTPBADPASV,
CURL_FTPBAD227,
CURL_FTPBADHOST227,
CURL_FTPNORECONN,
CURL_FTPNOBIN,
CURL_PARTIALFILE,
CURL_FTPNORETR,
CURL_FTPWRITERR,
CURL_FTPNOQUOTE,
CURL_HTTPPNF,
CURL_WRITERR,
CURL_BADUSER,
CURL_FTPNOSTOR,
CURL_READERR,
CURL_OUTOFMEM,
CURL_TIMEOUT,
CURL_FTPNOASCII,
CURL_FTPNOPORT,
CURL_FTPNOREST,
CURL_FTPNOSIZE,
CURL_HTTPRNGERR,
CURL_HTTPPOSTERR,
CURL_SSLNOCONN,
CURL_FTPBADRESUME,
CURL_FILENOACC,
CURL_LDAPNOBIND,
CURL_LDAPNOSRCH,
CURL_LDAPNOLIB,
CURL_LDAPNOFUNC,
CURL_ABORTCB,
CURL_BADPARAM,
CURL_BADORDER,
CURL_BADPWD,
CURL_MNYREDIR,
CURL_UNKTELNET,
CURL_UNKMSG,
CURL_BADSSLCERT,
CURL_SRVNOERR,
CURL_MAXMSG
/*
* 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.
*/
return curl_global_init(CURL_GLOBAL_DEFAULT);
}
/*
* This is the main global destructor for the app. Call this after
* _all_ libcurl usage is done.
*/
void main_free(void)
{
Daniel Stenberg
committed
curl_global_cleanup();
Daniel Stenberg
committed
int SetHTTPrequest(HttpReq req, HttpReq *store)
{
if((*store == HTTPREQ_UNSPEC) ||
(*store == req)) {
*store = req;
return 0;
Daniel Stenberg
committed
}
fprintf(stderr, "You can only select one HTTP request!\n");
return 1;
Daniel Stenberg
committed
}
{
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' or "
"'curl --manual' 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 */
int i;
const char *help[]={
"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 all cookies to this file after operation (H)",
" -C/--continue-at <offset> Specify absolute resume 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 - req GSS-lib)",
" --digest Enable HTTP Digest Authentication (H)",
" --disable-eprt Prevents curl from using EPRT or LPRT (F)",
" --disable-epsv Prevents 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)",
#ifdef USE_ENVIRONMENT
" --environment Write result codes to environment variables (RISC OS)",
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
" -e/--referer Referer page (H)",
" -E/--cert <cert[:passwd]> Specifies your certificate file and password (SSL)",
" --cert-type <type> Specifies certificate file type (DER/PEM/ENG) (SSL)",
" --key <key> Specifies private key file (SSL)",
" --key-type <type> Specifies private key file type (DER/PEM/ENG) (SSL)",
" --pass <pass> Specifies passphrase for the private key (SSL)",
" --engine <eng> Specifies the crypto engine to use (HTTPS)",
" --cacert <file> CA certificate to verify peer against (SSL)",
" --capath <directory> CA directory (made using c_rehash) to verify",
" peer against (SSL)",
" --ciphers <list> What SSL ciphers to use (SSL)",
" --compressed Request a compressed response (using deflate or gzip).",
" --connect-timeout <seconds> Maximum time allowed for connection",
" --create-dirs Create the necessary local directory hierarchy",
" --crlf Convert LF to CRLF in upload. Useful for MVS (OS/390)",
" -f/--fail Fail silently (no output at all) on errors (H)",
" --ftp-create-dirs Create the remote dirs if not present (F)",
" -F/--form <name=content> Specify HTTP 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 the HTTP-header in the output (H)",
" -I/--head Fetch document info only (HTTP HEAD/FTP SIZE)",
" -j/--junk-session-cookies Ignore session cookies read from file (H)",
" --interface <interface> Specify the interface to be used",
" --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 how fast transfers to allow",
" -L/--location Follow Location: hints (H)",
" --location-trusted Same, and continue to send authentication when ",
" following locations, even when hostname changed",
" -m/--max-time <seconds> Maximum time allowed for the transfer",
" --max-redirs <num> Set maximum number of redirections allowed (H)",
" -M/--manual Display huge help text",
" -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 Disables the 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 Perform non-HTTP services through a HTTP proxy",
" --proxy-ntlm Use NTLM authentication on the proxy (H)",
" -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)",
" -q When used as the first parameter disables .curlrc",
" -Q/--quote <cmd> Send QUOTE command to FTP before file transfer (F)",
" -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
" --random-file <file> File to use for reading random data from (SSL)",
" -R/--remote-time Set the remote file's time on the local output",
" -s/--silent Silent mode. Don't output anything",
" -S/--show-error Show error. With -s, make curl show errors when they occur",
" --stderr <file> Where to redirect stderr. - means stdout.",
" -t/--telnet-option <OPT=val> Set telnet option",
" --trace <file> Dump a network/debug trace to the given file",
" --trace-ascii <file> Like --trace but without the hex output",
" -T/--upload-file <file> Transfer/upload <file> to remote site",
" --url <URL> Another way to specify URL to work with",
" -u/--user <user[:password]> Specify user and password to use",
" Overrides -n and --netrc-optional",
" -U/--proxy-user <user[:password]> Specify Proxy authentication",
" -v/--verbose Makes the operation more talkative",
" -V/--version Outputs version number then quits",
" --wdebug Turns on WATT-32 debugging under DJGPP",
" -w/--write-out [format] What to output after completion",
" -x/--proxy <host[:port]> Use proxy. (Default port is 1080)",
" -X/--request <command> Specific 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> Includes a time condition to the server (H)",
" -0/--http1.0 Force usage of HTTP 1.0 (H)",
" -1/--tlsv1 Force usage of TLSv1 (H)",
" -2/--sslv2 Force usage of SSLv2 (H)",
" -3/--sslv3 Force usage of SSLv3 (H)",
" -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; help[i]; i++)
puts(help[i]);
const char *letter;
const char *lname;
bool extraparam;
};
struct Configurable {
bool remote_time;
char *random_file;
char *egd_file;
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 */
Daniel Stenberg
committed
bool resume_from_current;
unsigned short porttouse;
char *range;
int low_speed_limit;
int low_speed_time;
bool showerror;
char *userpwd;
char *proxyuserpwd;
char *proxy;
bool configread;
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 *key;
char *key_type;
char *key_passwd;
char *engine;
Daniel Stenberg
committed
char *trace_dump; /* file to dump the network trace to, or NULL */
FILE *trace_stream;
bool nobuffer;
Daniel Stenberg
committed
bool globoff;
Daniel Stenberg
committed
bool use_httpget;
bool insecure_ok; /* set TRUE to allow insecure SSL connects */
bool create_dirs;
Daniel Stenberg
committed
char *writeout; /* %-styled format string to output */
bool writeenv; /* write results to environment, if available */
Daniel Stenberg
committed
bool errors_fopened;
curl_TimeCond timecond;
struct curl_slist *headers;
Daniel Stenberg
committed
struct curl_httppost *httppost;
struct curl_httppost *last_post;
Daniel Stenberg
committed
struct curl_slist *telnet_options;
Daniel Stenberg
committed
HttpReq httpreq;
/* for bandwidth limiting features: */
size_t sendpersecond; /* send to peer */
size_t recvpersecond; /* receive from peer */
time_t lastsendtime;
size_t lastsendsize;
time_t lastrecvtime;
size_t lastrecvsize;
/* global variable to hold info about libcurl */
static curl_version_info_data *curlinfo;
static int parseconfig(const char *filename,
Daniel Stenberg
committed
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;
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
}
static char *file2string(FILE *file)
{
char buffer[256];
char *ptr;
char *string=NULL;
int len=0;
int 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;
long 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 */
}
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 */
}
}
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
*/
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
multi = multi_type;
}
else {
free (multi);
return NULL;
}
}
Daniel Stenberg
committed
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 {
Daniel Stenberg
committed
free (multi);
return NULL;
}
}
Daniel Stenberg
committed
if (*multi_current)
(*multi_current)->next = multi;
*multi_current = multi;
return *multi_current;
}
/* Free the items of the list.
*/
Daniel Stenberg
committed
static void FreeMultiInfo (struct multi_files *multi_start)
{
struct multi_files *multi;
Daniel Stenberg
committed
while (multi_start) {
multi = multi_start;
multi_start = multi_start->next;
free (multi);
}
}
/***************************************************************************
*
* 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'
*
Daniel Stenberg
committed
* 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,
Daniel Stenberg
committed
struct curl_httppost **httppost,
struct curl_httppost **last_post)
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
{
/* 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;
/* Preallocate contents to the length of input to make sure we don't
overwrite anything. */
contents = malloc(strlen(input));
contents[0] = '\000';
if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
/* the input was using the correct format */
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
Daniel Stenberg
committed
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! */
}
Daniel Stenberg
committed
type = NULL;
if(sep) {
/* if we got here on a comma, don't do much */
Daniel Stenberg
committed
if(FORM_FILE_SEPARATOR == *sep)
ptr = NULL;
else
Daniel Stenberg
committed
ptr = sep+1;
*sep=0; /* terminate file name at separator */
Daniel Stenberg
committed
while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
/* pass all white spaces */
while(isspace((int)*ptr))
ptr++;
if(curl_strnequal("type=", ptr, 5)) {
/* set type pointer */
type = &ptr[5];
Daniel Stenberg
committed
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
/* 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;
*sep=0; /* zero terminate type string */
ptr=sep+1;
}
else if(curl_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;
}
Daniel Stenberg
committed
/* 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 */
Daniel Stenberg
committed
if (!AddMultiFiles (contp, type, filename, &multi_start,
&multi_current)) {
fprintf(stderr, "Error building form post!\n");
free(contents);
Daniel Stenberg
committed
FreeMultiInfo (multi_start);
return 3;
}
contp = sep; /* move the contents pointer to after the separator */
Daniel Stenberg
committed
} while(sep && *sep); /* loop if there's another file name */
Daniel Stenberg
committed
/* 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);
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
FreeMultiInfo (multi_start);
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
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 {
if( contp[0]=='<' ) {
if (curl_formadd (httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_FILECONTENT, contp+1, CURLFORM_END) != 0) {
fprintf(stderr, "curl_formadd failed!\n");
free(contents);
return 6;
}
}
else {
if (curl_formadd (httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_COPYCONTENTS, contp, CURLFORM_END) != 0) {
fprintf(stderr, "curl_formadd failed!\n");
free(contents);
return 7;
}
}
}
}
else {
fprintf(stderr, "Illegally formatted input field!\n");
free(contents);
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_LAST
} ParameterError;
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
}
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 subletter=0; /* subletters can only occur on long options */
bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
/* single-letter,
long-name,
boolean whether it takes an additional argument
*/
struct LongShort aliases[]= {
/* all these ones, starting with "*" 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
{"*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},
{"*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},
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"4", "ipv4", FALSE},
{"6", "ipv6", FALSE},
{"a", "append", FALSE},
{"A", "user-agent", TRUE},
{"b", "cookie", TRUE},
{"B", "use-ascii", FALSE},
{"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},
{"Eb","cert-type", TRUE},
{"Ec","key", TRUE},
{"Ed","key-type", TRUE},
{"Ee","pass", TRUE},
{"Ef","engine", TRUE},
Daniel Stenberg
committed
{"g", "globoff", FALSE},
Daniel Stenberg
committed
{"G", "get", FALSE},
{"h", "help", FALSE},
{"H", "header", TRUE},
{"i", "include", FALSE},
{"I", "head", FALSE},
{"j", "junk-session-cookies", 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", "ftpport", 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},
Daniel Stenberg
committed
{"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;
int fnam=strlen(word);
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
Daniel Stenberg
committed
if(curl_strnequal(aliases[j].lname, word, fnam)) {
Daniel Stenberg
committed
if(curl_strequal(aliases[j].lname, word)) {
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;
return PARAM_OPTION_UNKNOWN;
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?*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++) {
hit = j;
break;
}
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
return PARAM_OPTION_UNKNOWN;
Daniel Stenberg
committed
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 */
config->connecttimeout=atoi(nextarg);
break;
case 'd': /* ciphers */
GetStr(&config->cipher_list, nextarg);
break;
case 'e': /* --disable-epsv */
config->disable_epsv ^= TRUE;
break;
case 'f': /* --disable-eprt */
config->disable_eprt ^= TRUE;
break;
#ifdef USE_ENVIRONMENT
case 'f':
config->writeenv ^= TRUE;
break;
#endif
Daniel Stenberg
committed
case 'g': /* --trace */
GetStr(&config->trace_dump, nextarg);
break;
case 'h': /* --trace-ascii */
GetStr(&config->trace_dump, nextarg);
config->trace_ascii = TRUE;
Daniel Stenberg
committed
break;
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
case 'i': /* --limit-rate */
{
/* We support G, M, K too */
char *unit;
unsigned long value = strtol(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 'l': /* --negotiate */
config->authtype = CURLAUTH_GSSNEGOTIATE;
break;
config->authtype = CURLAUTH_NTLM;
break;
case 'n': /* --basic for completeness */
config->authtype = CURLAUTH_BASIC;
break;
case 'o': /* --anyauth, let libcurl pick it */
config->authtype = CURLAUTH_ANY;
#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)) */
config->maxredirs = atoi(nextarg);
break;
case 't': /* --proxy-ntlm */
config->proxyntlm ^= TRUE;
break;
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
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 */
GetStr(&config->krb4level, nextarg);
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 '#': /* 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;
config->ssl_version = CURL_SSLVERSION_SSLv2;
/* SSL version 3 */
config->ssl_version = CURL_SSLVERSION_SSLv3;
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);
/* use ASCII/text when transfering */
config->conf ^= CONF_GETTEXT;
/* 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 */
Daniel Stenberg
committed
if(!curl_strequal(nextarg, "-")) {
Daniel Stenberg
committed
config->resume_from_current = FALSE;
}
else {
config->resume_from_current = TRUE;
config->resume_from = 0;
}
config->use_resume=TRUE;
break;
case 'd':
/* postfield data */
Daniel Stenberg
committed
{
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 @ */
Daniel Stenberg
committed
if(curl_strequal("-", nextarg))
Daniel Stenberg
committed
file = stdin;
else
file = fopen(nextarg, "rb");
Daniel Stenberg
committed
if(subletter == 'b') /* forced binary */
postdata = file2memory(file, &config->postfieldsize);
else
postdata = file2string(file);
if(file && (file != stdin))
fclose(file);
Daniel Stenberg
committed
}
else {
GetStr(&postdata, nextarg);
}
if(config->postfields) {
Daniel Stenberg
committed
/* we already have a string, we append this one
with a separating &-letter */
char *oldpost=config->postfields;
config->postfields=aprintf("%s&%s", oldpost, postdata);
Daniel Stenberg
committed
free(oldpost);
free(postdata);
}
Daniel Stenberg
committed
config->postfields=postdata;
Daniel Stenberg
committed
/*
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.
Daniel Stenberg
committed
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);
}
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);
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. */
Daniel Stenberg
committed
#ifdef WIN32
if(ptr &&
(ptr == &nextarg[1]) &&
(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 */
Daniel Stenberg
committed
#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;
Daniel Stenberg
committed
if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
return PARAM_BAD_USE;
Daniel Stenberg
committed
case 'g': /* g disables URLglobbing */
config->globoff ^= TRUE;
break;
Daniel Stenberg
committed
case 'G': /* HTTP GET */
config->use_httpget = TRUE;
break;
return PARAM_HELP_REQUESTED;
/* A custom header to append to a list */
config->headers = curl_slist_append(config->headers, nextarg);
break;
case 'i':
config->conf ^= CONF_HEADER; /* include the HTTP header as well */
break;
case 'j':
config->cookiesession ^= TRUE;
break;
/*
* 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;
}
case 'k': /* allow insecure SSL connects */
config->insecure_ok ^= TRUE;
break;
case 'K': /* parse config file */
res = parseconfig(nextarg, config);
config->configread = TRUE;
if(res)
return res;
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 */
config->timeout = atoi(nextarg);
break;
case 'M': /* M for manual, huge help */
hugehelp();
return PARAM_HELP_REQUESTED;
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;
}
case 'N':
/* disable the output I/O buffering */
config->nobuffer ^= 1;
break;
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
{
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;
case 'q': /* if used first, already taken care of, we do it like
this so we don't cause an error! */
break;
case 'Q':
/* QUOTE command to send to FTP server */
switch(nextarg[0]) {
case '-':
/* prefixed with a dash makes it a POST TRANSFER one */
nextarg++;
config->postquote = curl_slist_append(config->postquote, nextarg);
break;
case '+':
/* prefixed with a plus makes it a just-before-transfer one */
nextarg++;
config->prequote = curl_slist_append(config->prequote, nextarg);
break;
default:
config->quote = curl_slist_append(config->quote, nextarg);
}
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':
Daniel Stenberg
committed
config->telnet_options =
curl_slist_append(config->telnet_options, nextarg);
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
{
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);
break;
case 'U':
/* Proxy user:password */
GetStr(&config->proxyuserpwd, nextarg);
cleanarg(nextarg);
break;
case 'v':
config->conf ^= CONF_VERBOSE; /* talk a lot */
break;
case 'V':
{
const char **proto;
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;
};
struct feat feats[] = {
{"IPv6", CURL_VERSION_IPV6},
{"krb4", CURL_VERSION_KERBEROS4},
{"SSL", CURL_VERSION_SSL},
{"libz", CURL_VERSION_LIBZ},
{"NTLM", CURL_VERSION_NTLM},
{"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
{"Debug", CURL_VERSION_DEBUG},
{"AsynchDNS", CURL_VERSION_ASYNCHDNS},
{"SPNEGO", CURL_VERSION_SPNEGO}
};
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;
Daniel Stenberg
committed
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 @ */
Daniel Stenberg
committed
if(curl_strequal("-", nextarg))
file = stdin;
else
file = fopen(nextarg, "r");
config->writeout = file2string(file);
if(file && (file != stdin))
fclose(file);
}
else
GetStr(&config->writeout, nextarg);
Daniel Stenberg
committed
break;
/* set custom request */
GetStr(&config->customrequest, nextarg);
break;
/* low speed time */
config->low_speed_time = atoi(nextarg);
if(!config->low_speed_limit)
config->low_speed_limit = 1;
break;
/* low speed limit */
config->low_speed_limit = atoi(nextarg);
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) */
nextarg++;
break;
}
now=time(NULL);
config->condtime=curl_getdate(nextarg, &now);
/* 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 */
}
else {
/* pull the time out from the file */
config->condtime = statbuf.st_mtime;
}
}
break;
return PARAM_OPTION_UNKNOWN;
} while(!longopt && !singleopt && *++parse && !*usedarg);
return PARAM_OK;
static int parseconfig(const char *filename,
struct Configurable *config)
{
int res;
FILE *file;
char filebuffer[512];
char *home;
if(!filename || !*filename) {
/* NULL or no file name attempts to load .curlrc from the homedir! */
#define CURLRC DOT_CHAR "curlrc"
filename = CURLRC; /* sensible default */
home = curl_getenv("HOME"); /* portable environment reader */
if(home) {
if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
snprintf(filebuffer, sizeof(filebuffer),
"%s%s%s", home, DIR_CHAR, CURLRC);
filename = filebuffer;
}
curl_free(home); /* we've used it, now free it */
}
}
if(strcmp(filename,"-"))
file = fopen(filename, "r");
else
file = stdin;
Daniel Stenberg
committed
if(file) {
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
/* lines with # in the fist column is a comment! */
line++;
switch(*line) {
case '#':
case '/':
case '\r':
case '\n':
case '*':
case '\0':
Daniel Stenberg
committed
continue;
}
/* the option keywords starts here */
option = line;
while(*line && !isspace((int)*line) && !isseparator(*line))
line++;
/* ... and has ended here */
*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(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 == '\\') {
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 */
#ifdef DEBUG_CONFIG
fprintf(stderr, "PARAM: \"%s\"\n", param);
#endif
res = getparameter(option, param, &usedarg, config);
Daniel Stenberg
committed
if(*param && !usedarg)
/* we passed in a parameter that wasn't used! */
res = PARAM_GOT_EXTRA_PARAMETER;
Daniel Stenberg
committed
if(res != PARAM_OK) {
/* the help request isn't really an error */
if(!strcmp(filename, "-")) {
}
if(PARAM_HELP_REQUESTED != res) {
switch(res) {
default:
case PARAM_GOT_EXTRA_PARAMETER:
reason = "had unsupported trailing garbage";
break;
case PARAM_OPTION_UNKNOWN:
reason = "is unknown";
break;
case PARAM_OPTION_AMBIGUOUS:
reason = "is ambiguous";
break;
case PARAM_REQUIRES_PARAMETER:
reason = "requires parameter";
break;
case PARAM_BAD_USE:
reason = "is badly used here";
Daniel Stenberg
committed
break;
}
fprintf(stderr, "%s:%d: warning: '%s' %s\n",
filename, lineno, option, reason);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
if(alloced_param)
free(param);
free(aline);
return 0;
#ifdef HAVE_POLL
/* portable subsecond "sleep" */
poll((void *)0, 0, ms);
#else
/* systems without poll() need other solutions */
#ifdef WIN32
/* Windows offers a millisecond sleep */
Sleep(ms);
#else
/* Other systems must use select() for this */
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = ms * 1000;
select(0, NULL, NULL, NULL, &timeout);
#endif
#endif
struct OutStruct {
char *filename;
FILE *stream;
struct Configurable *config;
int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
Daniel Stenberg
committed
int rc;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure */
}
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
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.
*/
time_t timediff;
time_t now;
now = time(NULL);
timediff = now - config->lastrecvtime;
if( size*nmemb > config->recvpersecond*timediff) {
/* figure out how many milliseconds to rest */
go_sleep ( (size*nmemb)*1000/config->recvpersecond - timediff*1000 );
now = time(NULL);
}
config->lastrecvtime = now;
}
Daniel Stenberg
committed
rc = fwrite(buffer, size, nmemb, out->stream);
if(config->nobuffer)
/* disable output buffering */
fflush(out->stream);
return rc;
struct InStruct {
FILE *stream;
struct Configurable *config;
};
int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
{
struct InStruct *in=(struct InStruct *)userp;
struct Configurable *config = in->config;
if(config->sendpersecond) {
/*
* We know when we sent data the previous time. We know how much data
Loading
Loading full blame...