curl/src/main.c

1926 lines
54 KiB
C
Raw Normal View History

1999-12-29 15:20:26 +01:00
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
2001-01-03 10:29:33 +01:00
* Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
1999-12-29 15:20:26 +01:00
*
2001-01-03 10:29:33 +01:00
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
1999-12-29 15:20:26 +01:00
*
2001-01-03 10:29:33 +01:00
* 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 MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
1999-12-29 15:20:26 +01:00
*
2001-01-03 10:29:33 +01:00
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
1999-12-29 15:20:26 +01:00
*
2001-01-03 10:29:33 +01:00
* $Id$
*****************************************************************************/
1999-12-29 15:20:26 +01:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <ctype.h>
#include <curl/curl.h>
2000-05-22 16:12:12 +02:00
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
2000-10-09 23:35:40 +02:00
#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
1999-12-29 15:20:26 +01:00
#include <curl/mprintf.h>
1999-12-29 15:20:26 +01:00
#include "urlglob.h"
2000-11-21 10:38:41 +01:00
#include "writeout.h"
1999-12-29 15:20:26 +01:00
#define CURLseparator "--_curl_--"
/* This define make use of the "Curlseparator" as opposed to the
MIMEseparator. We might add support for the latter one in the
future, and that's why this is left in the source. */
#define CURL_SEPARATORS
1999-12-29 15:20:26 +01:00
/* This is now designed to have its own local setup.h */
#include "setup.h"
2001-01-24 15:44:05 +01:00
#if defined(WIN32)&&!defined(__CYGWIN32__)
2000-05-22 19:20:29 +02:00
#include <winsock.h>
#endif
1999-12-29 15:20:26 +01:00
#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
2000-10-09 13:13:17 +02:00
/* The last #include file should be: */
#ifdef MALLOCDEBUG
/* this is low-level hard-hacking memory leak tracking shit */
#include "../lib/memdebug.h"
#endif
#define DEFAULT_MAXREDIRS 50L
2000-10-09 23:35:40 +02:00
#ifndef __cplusplus /* (rabe) */
typedef char bool;
#endif /* (rabe) */
#define CURL_PROGRESS_STATS 0 /* default progress display */
#define CURL_PROGRESS_BAR 1
typedef enum {
HTTPREQ_UNSPEC,
HTTPREQ_GET,
HTTPREQ_HEAD,
HTTPREQ_POST,
HTTPREQ_SIMPLEPOST,
HTTPREQ_CUSTOM,
HTTPREQ_LAST
} HttpReq;
2000-05-22 16:12:12 +02:00
/* Just a set of bits */
#define CONF_DEFAULT 0
2000-11-22 14:51:11 +01:00
#define CONF_USEREMOTETIME (1<<0) /* set the remote time on the local file */
#define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
2000-05-22 16:12:12 +02:00
#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_UPLOAD (1<<14) /* this is an upload */
#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 */
2000-05-22 16:12:12 +02:00
#define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
2000-05-22 16:12:12 +02:00
#define CONF_MUTE (1<<28) /* force NOPROGRESS */
#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
1999-12-29 15:20:26 +01:00
extern void hugehelp(void);
/***********************************************************************
* Start with some silly functions to make win32-systems survive
***********************************************************************/
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
static void win32_cleanup(void)
{
WSACleanup();
}
2000-05-22 19:20:29 +02:00
static CURLcode win32_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
/* Tell the user that we couldn't find a useable */
/* winsock.dll. */
2000-05-22 16:12:12 +02:00
return CURLE_FAILED_INIT;
/* Confirm that the Windows Sockets DLL supports 1.1.*/
/* Note that if the DLL supports versions greater */
/* than 1.1 in addition to 1.1, it will still return */
/* 1.1 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we couldn't find a useable */
/* winsock.dll. */
WSACleanup();
2000-05-22 16:12:12 +02:00
return CURLE_FAILED_INIT;
}
2000-05-22 16:12:12 +02:00
return CURLE_OK;
}
/* The Windows Sockets DLL is acceptable. Proceed. */
#else
2000-05-22 16:12:12 +02:00
static CURLcode win32_init(void) { return CURLE_OK; }
#define win32_cleanup()
#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.
*/
2000-05-22 16:12:12 +02:00
CURLcode main_init(void)
{
return win32_init();
}
/*
* This is the main global destructor for the app. Call this after
* _all_ libcurl usage is done.
*/
void main_free(void)
{
win32_cleanup();
}
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;
}
1999-12-29 15:20:26 +01:00
static void helpf(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' for more information\n");
}
2001-01-08 08:37:44 +01:00
/*
* A chain of these nodes contain URL to get and where to put the URL's
* contents.
*/
struct getout {
struct getout *next;
char *url;
char *outfile;
int flags;
};
#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 */
1999-12-29 15:20:26 +01:00
static void help(void)
{
printf(CURL_ID "%s\n"
"Usage: curl [options...] <url>\n"
"Options: (H) means HTTP/HTTPS only, (F) means FTP only\n"
" -a/--append Append to target file when uploading (F)\n"
" -A/--user-agent <string> User-Agent to send to server (H)\n"
" -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)\n"
" -B/--use-ascii Use ASCII/text transfer\n",
curl_version());
puts(" -C/--continue-at <offset> Specify absolute resume offset\n"
2000-08-24 19:56:20 +02:00
" -d/--data <data> HTTP POST data (H)\n"
" --data-ascii <data> HTTP POST ASCII data (H)\n"
" --data-binary <data> HTTP POST binary data (H)\n"
1999-12-29 15:20:26 +01:00
" -D/--dump-header <file> Write the headers to this file\n"
" -e/--referer Referer page (H)");
puts(" -E/--cert <cert[:passwd]> Specifies your certificate file and password (HTTPS)\n"
2000-11-22 14:51:11 +01:00
" --cacert <file> CA certifciate to verify peer against (HTTPS)\n"
1999-12-29 15:20:26 +01:00
" -f/--fail Fail silently (no output at all) on errors (H)\n"
" -F/--form <name=content> Specify HTTP POST data (H)\n"
" -g/--globoff Disable URL sequences and ranges using {} and []\n"
1999-12-29 15:20:26 +01:00
" -h/--help This help text\n"
" -H/--header <line> Custom header to pass to server. (H)");
puts(" -i/--include Include the HTTP-header in the output (H)\n"
1999-12-29 15:20:26 +01:00
" -I/--head Fetch document info only (HTTP HEAD/FTP SIZE)\n"
2000-09-18 23:54:46 +02:00
" --interface <interface> Specify the interface to be used\n"
2000-09-21 10:52:08 +02:00
" --krb4 <level> Enable krb4 with specified security level (F)\n"
1999-12-29 15:20:26 +01:00
" -K/--config Specify which config file to read\n"
" -l/--list-only List only names of an FTP directory (F)");
puts(" -L/--location Follow Location: hints (H)\n"
1999-12-29 15:20:26 +01:00
" -m/--max-time <seconds> Maximum time allowed for the transfer\n"
" -M/--manual Display huge help text\n"
" -n/--netrc Read .netrc for user name and password\n"
" -N/--no-buffer Disables the buffering of the output stream");
puts(" -o/--output <file> Write output to <file> instead of stdout\n"
1999-12-29 15:20:26 +01:00
" -O/--remote-name Write output to a file named as the remote file\n"
2000-09-15 08:11:46 +02:00
" -p/--proxytunnel Perform non-HTTP services through a HTTP proxy\n"
1999-12-29 15:20:26 +01:00
" -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)\n"
" -q When used as the first parameter disables .curlrc\n"
" -Q/--quote <cmd> Send QUOTE command to FTP before file transfer (F)");
puts(" -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server\n"
1999-12-29 15:20:26 +01:00
" -s/--silent Silent mode. Don't output anything\n"
" -S/--show-error Show error. With -s, make curl show errors when they occur\n"
" -t/--telnet-option <OPT=val> Set telnet option\n"
1999-12-29 15:20:26 +01:00
" -T/--upload-file <file> Transfer/upload <file> to remote site\n"
" --url <URL> Another way to specify URL to work with");
puts(" -u/--user <user[:password]> Specify user and password to use\n"
" -U/--proxy-user <user[:password]> Specify Proxy authentication\n"
1999-12-29 15:20:26 +01:00
" -v/--verbose Makes the operation more talkative\n"
" -V/--version Outputs version number then quits\n"
" -w/--write-out [format] What to output after completion\n"
" -x/--proxy <host[:port]> Use proxy. (Default port is 1080)\n"
" -X/--request <command> Specific request command to use");
puts(" -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30\n"
" -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs\n"
1999-12-29 15:20:26 +01:00
" -z/--time-cond <time> Includes a time condition to the server (H)\n"
" -Z/--max-redirs <num> Set maximum number of redirections allowed (H)\n"
1999-12-29 15:20:26 +01:00
" -2/--sslv2 Force usage of SSLv2 (H)\n"
" -3/--sslv3 Force usage of SSLv3 (H)");
puts(" -#/--progress-bar Display transfer progress as a progress bar\n"
1999-12-29 15:20:26 +01:00
" --crlf Convert LF to CRLF in upload. Useful for MVS (OS/390)\n"
" --stderr <file> Where to redirect stderr. - means stdout.");
1999-12-29 15:20:26 +01:00
}
struct LongShort {
char *letter;
char *lname;
bool extraparam;
};
struct Configurable {
char *useragent;
char *cookie;
bool use_resume;
int resume_from;
char *postfields;
2000-08-24 19:56:20 +02:00
long postfieldsize;
1999-12-29 15:20:26 +01:00
char *referer;
long timeout;
long maxredirs;
1999-12-29 15:20:26 +01:00
char *headerfile;
char *ftpport;
2000-09-21 10:52:08 +02:00
char *iface;
1999-12-29 15:20:26 +01:00
unsigned short porttouse;
char *range;
int low_speed_limit;
int low_speed_time;
bool showerror;
char *infile;
char *userpwd;
char *proxyuserpwd;
char *proxy;
bool configread;
2000-09-15 08:11:46 +02:00
bool proxytunnel;
1999-12-29 15:20:26 +01:00
long conf;
2001-01-08 08:37:44 +01:00
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 */
1999-12-29 15:20:26 +01:00
char *cert;
2000-11-22 14:51:11 +01:00
char *cacert;
1999-12-29 15:20:26 +01:00
char *cert_passwd;
bool crlf;
char *cookiefile;
char *customrequest;
2000-09-21 10:52:08 +02:00
char *krb4level;
1999-12-29 15:20:26 +01:00
bool progressmode;
bool nobuffer;
bool globoff;
1999-12-29 15:20:26 +01:00
char *writeout; /* %-styled format string to output */
1999-12-29 15:20:26 +01:00
FILE *errors; /* if stderr redirect is requested */
struct curl_slist *quote;
2000-01-11 00:36:14 +01:00
struct curl_slist *postquote;
1999-12-29 15:20:26 +01:00
long ssl_version;
TimeCond timecond;
time_t condtime;
struct curl_slist *headers;
1999-12-29 15:20:26 +01:00
struct HttpPost *httppost;
struct HttpPost *last_post;
struct curl_slist *telnet_options;
HttpReq httpreq;
1999-12-29 15:20:26 +01:00
};
static int parseconfig(char *filename,
struct Configurable *config);
static char *my_get_line(FILE *fp);
1999-12-29 15:20:26 +01:00
static void GetStr(char **string,
char *value)
{
if(*string)
free(*string);
2000-05-22 16:12:12 +02:00
if(value && *value)
*string = strdup(value);
else
*string = NULL;
1999-12-29 15:20:26 +01:00
}
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 */
}
2000-08-24 19:56:20 +02:00
static char *file2memory(FILE *file, long *size)
{
char buffer[1024];
char *string=NULL;
char *newstring=NULL;
long len=0;
long stringlen=0;
if(file) {
2000-11-21 10:38:41 +01:00
while((len = fread(buffer, 1, sizeof(buffer), file))) {
2000-08-24 19:56:20 +02:00
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);
free(node);
node = next; /* GOTO next */
}
}
2001-01-08 08:37:44 +01:00
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;
}
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 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)
1999-12-29 15:20:26 +01:00
{
char letter;
2000-08-24 19:56:20 +02:00
char subletter=0; /* subletters can only occur on long options */
1999-12-29 15:20:26 +01:00
char *parse=NULL;
int res;
int j;
time_t now;
int hit=-1;
2000-08-24 19:56:20 +02:00
bool longopt=FALSE;
bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
1999-12-29 15:20:26 +01:00
/* single-letter,
long-name,
boolean whether it takes an additional argument
*/
struct LongShort aliases[]= {
{"9", "crlf", FALSE},
{"8", "stderr", TRUE},
2000-09-18 23:54:46 +02:00
{"7", "interface", TRUE},
2000-09-21 10:52:08 +02:00
{"6", "krb4", TRUE},
{"5", "url", TRUE},
1999-12-29 15:20:26 +01:00
{"2", "sslv2", FALSE},
{"3", "sslv3", FALSE},
{"a", "append", FALSE},
{"A", "user-agent", TRUE},
{"b", "cookie", TRUE},
{"B", "ftp-ascii", FALSE}, /* this long format is OBSOLETEE now! */
{"B", "use-ascii", FALSE},
1999-12-29 15:20:26 +01:00
{"c", "continue", FALSE},
{"C", "continue-at", TRUE},
{"d", "data", TRUE},
2000-08-24 19:56:20 +02:00
{"da", "data-ascii", TRUE},
{"db", "data-binary", TRUE},
1999-12-29 15:20:26 +01:00
{"D", "dump-header", TRUE},
{"e", "referer", TRUE},
{"E", "cert", TRUE},
2000-11-22 14:51:11 +01:00
{"Ea", "cacert", TRUE},
1999-12-29 15:20:26 +01:00
{"f", "fail", FALSE},
{"F", "form", TRUE},
{"g", "globoff", FALSE},
1999-12-29 15:20:26 +01:00
{"h", "help", FALSE},
{"H", "header", TRUE},
{"i", "include", FALSE},
{"I", "head", FALSE},
{"K", "config", TRUE},
{"l", "list-only", FALSE},
{"L", "location", FALSE},
{"m", "max-time", TRUE},
{"M", "manual", FALSE},
{"n", "netrc", FALSE},
{"N", "no-buffer", FALSE},
1999-12-29 15:20:26 +01:00
{"o", "output", TRUE},
{"O", "remote-name", FALSE},
2000-09-15 08:11:46 +02:00
{"p", "proxytunnel", FALSE},
1999-12-29 15:20:26 +01:00
{"P", "ftpport", TRUE},
{"q", "disable", FALSE},
{"Q", "quote", TRUE},
{"r", "range", TRUE},
{"s", "silent", FALSE},
{"S", "show-error", FALSE},
{"t", "telnet-options", TRUE},
1999-12-29 15:20:26 +01:00
{"T", "upload-file", TRUE},
{"u", "user", TRUE},
{"U", "proxy-user", TRUE},
{"v", "verbose", FALSE},
{"V", "version", FALSE},
{"w", "write-out", TRUE},
1999-12-29 15:20:26 +01:00
{"x", "proxy", TRUE},
{"X", "request", TRUE},
{"X", "http-request", TRUE}, /* OBSOLETE VERSION */
{"Y", "speed-limit", TRUE},
{"y", "speed-time", TRUE},
1999-12-29 15:20:26 +01:00
{"z", "time-cond", TRUE},
{"Z", "max-redirs", TRUE},
1999-12-29 15:20:26 +01:00
{"#", "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);
2000-10-06 14:45:05 +02:00
int numhits=0;
1999-12-29 15:20:26 +01:00
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
if(strnequal(aliases[j].lname, word, fnam)) {
2000-08-24 19:56:20 +02:00
longopt = TRUE;
2000-10-06 14:45:05 +02:00
numhits++;
if(strequal(aliases[j].lname, word)) {
1999-12-29 15:20:26 +01:00
parse = aliases[j].letter;
hit = j;
2000-10-06 14:45:05 +02:00
numhits = 1; /* a single unique hit */
1999-12-29 15:20:26 +01:00
break;
}
parse = aliases[j].letter;
hit = j;
}
}
2000-10-06 14:45:05 +02:00
if(numhits>1) {
/* this is at least the second match! */
return PARAM_OPTION_AMBIGUOUS;
2000-10-06 14:45:05 +02:00
}
1999-12-29 15:20:26 +01:00
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
1999-12-29 15:20:26 +01:00
}
}
else {
flag++; /* prefixed with one dash, pass it */
1999-12-29 15:20:26 +01:00
hit=-1;
parse = flag;
}
do {
/* we can loop here if we have multiple single-letters */
2000-08-24 19:56:20 +02:00
if(!longopt)
letter = parse?*parse:'\0';
else {
letter = parse[0];
subletter = parse[1];
}
1999-12-29 15:20:26 +01:00
*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++) {
2000-08-24 19:56:20 +02:00
if(letter == aliases[j].letter[0]) {
1999-12-29 15:20:26 +01:00
hit = j;
break;
}
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
1999-12-29 15:20:26 +01:00
}
}
if(hit < 0) {
return PARAM_OPTION_UNKNOWN;
1999-12-29 15:20:26 +01:00
}
if(!longopt && aliases[hit].extraparam && parse[1]) {
nextarg=&parse[1]; /* this is the actual extra parameter */
singleopt=TRUE; /* don't loop anymore after this */
}
else if((!nextarg || !*nextarg) && aliases[hit].extraparam) {
return PARAM_REQUIRES_PARAMETER;
1999-12-29 15:20:26 +01:00
}
else if(nextarg && aliases[hit].extraparam)
*usedarg = TRUE; /* mark it as used */
switch(letter) {
case '9': /* there is no short letter for this */
/* LF -> CRLF conversinon? */
config->crlf = TRUE;
break;
case '8': /* there is no short letter for this */
if(strcmp(nextarg, "-"))
config->errors = fopen(nextarg, "wt");
else
config->errors = stdout;
break;
2000-09-18 23:54:46 +02:00
case '7': /* there is no short letter for this */
/* interface */
2000-09-21 10:52:08 +02:00
GetStr(&config->iface, nextarg);
break;
case '6': /* there is no short letter for this */
/* krb4 level string */
GetStr(&config->krb4level, nextarg);
break;
case '5':
/* the URL! */
2001-01-08 08:37:44 +01:00
{
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;
1999-12-29 15:20:26 +01:00
case '#': /* added 19990617 larsa */
config->progressmode ^= CURL_PROGRESS_BAR;
break;
case '2':
/* SSL version 2 */
config->ssl_version = 2;
break;
case '3':
/* SSL version 2 */
config->ssl_version = 3;
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, '=')) {
1999-12-29 15:20:26 +01:00
/* A cookie string must have a =-letter */
GetStr(&config->cookie, nextarg);
break;
1999-12-29 15:20:26 +01:00
}
/* We have a cookie file to read from! */
GetStr(&config->cookiefile, nextarg);
1999-12-29 15:20:26 +01:00
break;
case 'B':
/* use ASCII/text when transfering */
config->conf ^= CONF_GETTEXT;
1999-12-29 15:20:26 +01:00
break;
case 'c':
/* This makes us continue an ftp transfer */
config->use_resume^=TRUE;
2000-09-26 00:20:02 +02:00
fprintf(stderr, "-c is a deprecated switch, use '-C -' instead!\n");
1999-12-29 15:20:26 +01:00
break;
case 'C':
/* This makes us continue an ftp transfer at given position */
2000-09-26 00:20:02 +02:00
if(!strequal(nextarg, "-"))
config->resume_from= atoi(nextarg);
1999-12-29 15:20:26 +01:00
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(strequal("-", nextarg))
file = stdin;
else
file = fopen(nextarg, "r");
if(subletter == 'b') /* forced binary */
postdata = file2memory(file, &config->postfieldsize);
else
postdata = file2string(file);
if(file && (file != stdin))
fclose(stdin);
}
else {
GetStr(&postdata, nextarg);
}
2000-08-24 19:56:20 +02:00
if(config->postfields && *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);
}
2000-08-24 19:56:20 +02:00
else
config->postfields=postdata;
1999-12-29 15:20:26 +01:00
}
if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
return PARAM_BAD_USE;
1999-12-29 15:20:26 +01:00
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);
}
1999-12-29 15:20:26 +01:00
break;
case 'E':
2000-11-22 14:51:11 +01:00
if(subletter == 'a') {
/* CA info PEM file */
GetStr(&config->cacert, nextarg);
}
else {
1999-12-29 15:20:26 +01:00
char *ptr = strchr(nextarg, ':');
if(ptr) {
/* we have a password too */
*ptr=0;
ptr++;
GetStr(&config->cert_passwd, ptr);
}
GetStr(&config->cert, 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(curl_formparse(nextarg,
1999-12-29 15:20:26 +01:00
&config->httppost,
&config->last_post))
return PARAM_BAD_USE;
if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
return PARAM_BAD_USE;
1999-12-29 15:20:26 +01:00
break;
case 'g': /* g disables URLglobbing */
config->globoff ^= TRUE;
break;
1999-12-29 15:20:26 +01:00
case 'h': /* h for help */
help();
return PARAM_HELP_REQUESTED;
1999-12-29 15:20:26 +01:00
case 'H':
/* A custom header to append to a list */
config->headers = curl_slist_append(config->headers, nextarg);
1999-12-29 15:20:26 +01:00
break;
case 'i':
config->conf ^= CONF_HEADER; /* include the HTTP header as well */
break;
case 'I':
config->conf ^= CONF_HEADER; /* include the HTTP header in the output */
config->conf ^= CONF_NOBODY; /* don't fetch the body at all */
if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq))
return PARAM_BAD_USE;
1999-12-29 15:20:26 +01:00
break;
case 'K':
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 */
break;
case 'm':
/* specified max time */
config->timeout = atoi(nextarg);
break;
case 'M': /* M for manual, huge help */
hugehelp();
return PARAM_HELP_REQUESTED;
1999-12-29 15:20:26 +01:00
case 'n':
/* 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;
1999-12-29 15:20:26 +01:00
case 'o':
case 'O':
/* output file */
2001-01-08 08:37:44 +01:00
{
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;
}
}
1999-12-29 15:20:26 +01:00
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':
2000-09-15 08:11:46 +02:00
/* proxy tunnel for non-http protocols */
config->proxytunnel ^= TRUE;
1999-12-29 15:20:26 +01:00
break;
2000-09-15 08:11:46 +02:00
1999-12-29 15:20:26 +01:00
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 */
2000-01-11 00:36:14 +01:00
if(nextarg[0] == '-') {
/* prefixed with a dash makes it a POST TRANSFER one */
nextarg++;
config->postquote = curl_slist_append(config->postquote, nextarg);
}
else {
config->quote = curl_slist_append(config->quote, nextarg);
}
1999-12-29 15:20:26 +01:00
break;
case 'r':
/* byte range requested */
GetStr(&config->range, nextarg);
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 */
config->telnet_options = curl_slist_append(config->telnet_options, nextarg);
1999-12-29 15:20:26 +01:00
break;
case 'T':
/* we are uploading */
config->conf |= CONF_UPLOAD;
2000-09-26 00:20:02 +02:00
if(!strequal("-", nextarg))
/* make - equal stdin */
GetStr(&config->infile, nextarg);
1999-12-29 15:20:26 +01:00
break;
case 'u':
/* user:password */
GetStr(&config->userpwd, nextarg);
break;
case 'U':
/* Proxy user:password */
GetStr(&config->proxyuserpwd, nextarg);
break;
case 'v':
config->conf ^= CONF_VERBOSE; /* talk a lot */
break;
case 'V':
printf(CURL_ID "%s\n", curl_version());
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(strequal("-", nextarg))
file = stdin;
else
file = fopen(nextarg, "r");
config->writeout = file2string(file);
if(file && (file != stdin))
fclose(stdin);
}
else
GetStr(&config->writeout, nextarg);
break;
1999-12-29 15:20:26 +01:00
case 'x':
/* proxy */
2000-05-22 16:12:12 +02:00
GetStr(&config->proxy, nextarg);
1999-12-29 15:20:26 +01:00
break;
case 'X':
/* HTTP request */
GetStr(&config->customrequest, nextarg);
if(SetHTTPrequest(HTTPREQ_CUSTOM, &config->httpreq))
return PARAM_BAD_USE;
1999-12-29 15:20:26 +01:00
break;
case 'y':
1999-12-29 15:20:26 +01:00
/* low speed time */
config->low_speed_time = atoi(nextarg);
if(!config->low_speed_limit)
config->low_speed_limit = 1;
break;
case 'Y':
1999-12-29 15:20:26 +01:00
/* 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 = TIMECOND_IFMODSINCE;
break;
case '-':
/* If-Unmodified-Since: (section 14.24 in RFC2068) */
config->timecond = TIMECOND_IFUNMODSINCE;
nextarg++;
break;
case '=':
/* Last-Modified: (section 14.29 in RFC2068) */
config->timecond = TIMECOND_LASTMOD;
nextarg++;
break;
}
now=time(NULL);
config->condtime=curl_getdate(nextarg, &now);
if(-1 == 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 = TIMECOND_NONE;
}
else {
/* pull the time out from the file */
config->condtime = statbuf.st_mtime;
}
}
break;
case 'Z':
/* specified max no of redirects (http(s)) */
config->maxredirs = atoi(nextarg);
break;
1999-12-29 15:20:26 +01:00
default: /* unknown flag */
return PARAM_OPTION_UNKNOWN;
1999-12-29 15:20:26 +01:00
}
hit = -1;
} while(!singleopt && *++parse && !*usedarg);
1999-12-29 15:20:26 +01:00
return PARAM_OK;
1999-12-29 15:20:26 +01:00
}
static int parseconfig(char *filename,
struct Configurable *config)
{
int res;
FILE *file;
char filebuffer[256];
bool usedarg;
char *home=NULL;
1999-12-29 15:20:26 +01:00
if(!filename || !*filename) {
/* NULL or no file name attempts to load .curlrc from the homedir! */
#define CURLRC DOT_CHAR "curlrc"
home = curl_getenv("HOME"); /* portable environment reader */
1999-12-29 15:20:26 +01:00
if(!home)
return 0;
if(strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))) {
free(home);
return 0;
}
1999-12-29 15:20:26 +01:00
sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
filename = filebuffer;
}
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! */
2000-11-21 10:38:41 +01:00
while(isspace((int)*line))
line++;
switch(*line) {
case '#':
case '/':
case '\r':
case '\n':
case '*':
case '\0':
free(line);
continue;
}
1999-12-29 15:20:26 +01:00
/* the option keywords starts here */
option = line;
2000-11-21 10:38:41 +01:00
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) */
2000-11-21 10:38:41 +01:00
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 == '\\') {
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 */
1999-12-29 15:20:26 +01:00
}
else {
param=line; /* parameter starts here */
2000-11-21 10:38:41 +01:00
while(*line && !isspace((int)*line))
line++;
*line=0; /* zero terminate */
1999-12-29 15:20:26 +01:00
}
#ifdef DEBUG_CONFIG
fprintf(stderr, "PARAM: \"%s\"\n", param);
#endif
res = getparameter(option, param, &usedarg, config);
if(*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="<stdin>";
}
if(PARAM_HELP_REQUESTED != res) {
char *reason;
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";
break;
}
fprintf(stderr, "%s:%d: warning: '%s' %s\n",
filename, lineno, option, reason);
}
}
if(alloced_param)
free(param);
free(aline);
1999-12-29 15:20:26 +01:00
}
if(file != stdin)
fclose(file);
}
if(home)
free(home);
return 0;
1999-12-29 15:20:26 +01:00
}
struct OutStruct {
char *filename;
FILE *stream;
struct Configurable *config;
1999-12-29 15:20:26 +01:00
};
int my_fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream)
{
struct OutStruct *out=(struct OutStruct *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure */
if(out->config->nobuffer) {
/* disable output buffering */
#ifdef HAVE_SETVBUF
setvbuf(out->stream, NULL, _IONBF, 0);
#endif
}
1999-12-29 15:20:26 +01:00
}
return fwrite(buffer, size, nmemb, out->stream);
}
struct ProgressData {
size_t total;
size_t prev;
size_t point;
int width;
};
int myprogress (void *clientp,
size_t dltotal,
size_t dlnow,
size_t ultotal,
size_t ulnow)
{
/* The original progress-bar source code was written for curl by Lars Aas,
and this new edition inherites some of his concepts. */
char line[256];
char outline[256];
char format[40];
float frac;
float percent;
int barwidth;
int num;
int i;
struct ProgressData *bar = (struct ProgressData *)clientp;
size_t total = dltotal + ultotal;
bar->point = dlnow + ulnow; /* we've come this far */
if(0 == total) {
int prevblock = bar->prev / 1024;
int thisblock = bar->point / 1024;
while ( thisblock > prevblock ) {
fprintf( stderr, "#" );
prevblock++;
}
}
else {
frac = (float) bar->point / (float) total;
percent = frac * 100.0f;
barwidth = bar->width - 7;
num = (int) (((float)barwidth) * frac);
i = 0;
for ( i = 0; i < num; i++ ) {
line[i] = '#';
}
line[i] = '\0';
sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
sprintf( outline, format, line, percent );
fprintf( stderr, "\r%s", outline );
}
bar->prev = bar->point;
return 0;
}
void progressbarinit(struct ProgressData *bar)
{
#ifdef __EMX__
/* 20000318 mgs */
int scr_size [2];
#endif
char *colp;
memset(bar, 0, sizeof(struct ProgressData));
/* 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 = curl_getenv("COLUMNS");
if (colp != NULL) {
bar->width = atoi(colp);
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
}
1999-12-29 15:20:26 +01:00
2000-11-22 14:51:11 +01:00
void free_config_fields(struct Configurable *config)
{
2000-11-22 14:51:11 +01:00
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->infile)
free(config->infile);
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);
curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
curl_slist_free_all(config->postquote); /* */
curl_slist_free_all(config->headers); /* */
}
static int
operate(struct Configurable *config, int argc, char *argv[])
1999-12-29 15:20:26 +01:00
{
char errorbuffer[CURL_ERROR_SIZE];
2000-10-09 13:13:17 +02:00
char useragent[128]; /* buah, we don't want a larger default user agent */
struct ProgressData progressbar;
2001-01-08 08:37:44 +01:00
struct getout *urlnode;
struct getout *nextnode;
1999-12-29 15:20:26 +01:00
struct OutStruct outs;
struct OutStruct heads;
1999-12-29 15:20:26 +01:00
char *url = NULL;
URLGlob *urls=NULL;
1999-12-29 15:20:26 +01:00
int urlnum;
char *outfiles;
1999-12-29 15:20:26 +01:00
int separator = 0;
FILE *infd = stdin;
FILE *headerfilep = NULL;
char *urlbuffer=NULL;
int infilesize=-1; /* -1 means unknown */
bool stillflags=TRUE;
2000-11-17 15:03:58 +01:00
bool allocuseragent=FALSE;
2000-05-22 16:12:12 +02:00
CURL *curl;
int res;
1999-12-29 15:20:26 +01:00
int i;
2000-10-09 13:13:17 +02:00
#ifdef MALLOCDEBUG
/* this sends all memory debug messages to a logfile named memdump */
curl_memdebug("memdump");
#endif
2001-03-12 14:57:02 +01:00
main_init(); /* inits winsock crap for windows */
config->showerror=TRUE;
config->conf=CONF_DEFAULT;
1999-12-29 15:20:26 +01:00
if(argc>1 &&
(!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 {
res = parseconfig(NULL, config);
1999-12-29 15:20:26 +01:00
if(res)
return res;
}
2001-01-08 08:37:44 +01:00
if ((argc < 2) && !config->url_list) {
1999-12-29 15:20:26 +01:00
helpf(NULL);
2000-05-22 16:12:12 +02:00
return CURLE_FAILED_INIT;
1999-12-29 15:20:26 +01:00
}
/* Parse options */
for (i = 1; i < argc; i++) {
if(stillflags &&
('-' == argv[i][0])) {
char *nextarg;
bool passarg;
char *origopt=argv[i];
1999-12-29 15:20:26 +01:00
char *flag = argv[i];
1999-12-29 15:20:26 +01:00
if(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) {
switch(res) {
case PARAM_OPTION_AMBIGUOUS:
helpf("option %s is ambiguous\n", origopt);
break;
case PARAM_OPTION_UNKNOWN:
helpf("option %s is unknown\n", origopt);
break;
case PARAM_REQUIRES_PARAMETER:
helpf("option %s requires an extra argument!\n", origopt);
break;
case PARAM_BAD_USE:
helpf("option %s was wrongly used!\n", origopt);
break;
case PARAM_HELP_REQUESTED:
/* no text */
break;
}
clean_getout(config);
return CURLE_FAILED_INIT;
}
1999-12-29 15:20:26 +01:00
if(passarg) /* we're supposed to skip this */
i++;
}
}
else {
2001-01-08 08:37:44 +01:00
bool used;
/* just add the URL please */
res = getparameter("--url", argv[i], &used, config);
if(res)
return res;
1999-12-29 15:20:26 +01:00
}
}
2001-01-08 08:37:44 +01:00
if(!config->url_list) {
1999-12-29 15:20:26 +01:00
helpf("no URL specified!\n");
2000-05-22 16:12:12 +02:00
return CURLE_FAILED_INIT;
1999-12-29 15:20:26 +01:00
}
if(NULL == config->useragent) {
2000-10-09 13:13:17 +02:00
/* set non-zero default values: */
snprintf(useragent, sizeof(useragent),
CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
config->useragent= useragent;
2000-10-09 13:13:17 +02:00
}
2000-11-17 15:03:58 +01:00
else
allocuseragent = TRUE;
1999-12-29 15:20:26 +01:00
2001-03-12 14:57:02 +01:00
/*
* Get a curl handle to use for all forthcoming curl transfers. Cleanup
* when all transfers are done. This is supported with libcurl 7.7 and
* should not be attempted on previous versions.
*/
curl = curl_easy_init();
if(!curl)
return CURLE_FAILED_INIT;
2001-01-08 08:37:44 +01:00
urlnode = config->url_list;
2001-01-08 08:37:44 +01:00
/* loop through the list of given URLs */
while(urlnode) {
2000-11-17 15:03:58 +01:00
/* get the full URL (it might be NULL) */
2001-01-08 08:37:44 +01:00
url=urlnode->url;
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 */
}
2001-01-08 08:37:44 +01:00
/* default output stream is stdout */
outs.stream = stdout;
outs.config = config;
if(!config->globoff) {
/* Unless explicitly shut off, we expand '{...}' and '[...]' expressions
and return total number of URLs in pattern set */
res = glob_url(&urls, url, &urlnum);
if(res != CURLE_OK)
return res;
}
2001-01-08 08:37:44 +01:00
/* save outfile pattern before expansion */
outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;
if ((!outfiles || strequal(outfiles, "-")) && urlnum > 1) {
2001-01-08 08:37:44 +01:00
/* multiple files extracted to stdout, insert separators! */
separator = 1;
2000-11-17 15:03:58 +01:00
}
for(i = 0;
(url = urls?next_url(urls):(i?NULL:strdup(url)));
i++) {
2001-01-08 08:37:44 +01:00
char *outfile;
outfile = outfiles?strdup(outfiles):NULL;
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
if((urlnode->flags&GETOUT_USEREMOTE) ||
(outfile && !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.
*/
2000-11-17 15:03:58 +01:00
2001-01-08 08:37:44 +01:00
if(!outfile) {
/* Find and get the remote file name */
char * pc =strstr(url, "://");
if(pc)
pc+=3;
else
pc=url;
pc = strrchr(pc, '/');
outfile = (char *) NULL == pc ? NULL : strdup(pc+1) ;
if(!outfile) {
helpf("Remote file name has no length!\n");
return CURLE_WRITE_ERROR;
}
}
else if(urls) {
2001-01-08 08:37:44 +01:00
/* fill '#1' ... '#9' terms from URL pattern */
char *storefile = outfile;
outfile = match_url(storefile, urls);
free(storefile);
2000-11-17 15:03:58 +01:00
}
2001-01-08 08:37:44 +01:00
if((0 == config->resume_from) && config->use_resume) {
/* we're told to continue where we are now, then we get the size of
the file as it is now and open it for append instead */
2000-11-17 15:03:58 +01:00
2001-01-08 08:37:44 +01:00
struct stat fileinfo;
if(0 == stat(outfile, &fileinfo)) {
/* set offset to current file size: */
config->resume_from = fileinfo.st_size;
}
/* else let offset remain 0 */
2000-11-17 15:03:58 +01:00
}
2001-01-08 08:37:44 +01:00
if(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;
}
2000-11-17 15:03:58 +01:00
}
2001-01-08 08:37:44 +01:00
else {
outs.filename = outfile;
outs.stream = NULL; /* open when needed */
2000-11-17 15:03:58 +01:00
}
2001-01-08 08:37:44 +01:00
}
if(config->infile) {
/*
* We have specified a file to upload
*/
struct stat fileinfo;
/* If no file name part is given in the URL, we add this file name */
char *ptr=strstr(url, "://");
2000-11-17 15:03:58 +01:00
if(ptr)
2001-01-08 08:37:44 +01:00
ptr+=3;
2000-11-17 15:03:58 +01:00
else
2001-01-08 08:37:44 +01:00
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.*/
urlbuffer=(char *)malloc(strlen(url) + strlen(config->infile) + 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, config->infile);
else
/* thers is no trailing slash on the URL */
sprintf(urlbuffer, "%s/%s", url, config->infile);
url = urlbuffer; /* use our new URL instead! */
}
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
infd=(FILE *) fopen(config->infile, "rb");
if (!infd || stat(config->infile, &fileinfo)) {
helpf("Can't open '%s'!\n", config->infile);
return CURLE_READ_ERROR;
}
infilesize=fileinfo.st_size;
2000-11-17 15:03:58 +01:00
1999-12-29 15:20:26 +01:00
}
2001-01-08 08:37:44 +01:00
if((config->conf&CONF_UPLOAD) &&
config->use_resume &&
(0==config->resume_from)) {
config->resume_from = -1; /* -1 will then force get-it-yourself */
}
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;
}
2000-11-17 15:03:58 +01:00
2001-01-08 08:37:44 +01:00
if(outs.stream && isatty(fileno(outs.stream)) &&
!(config->conf&(CONF_UPLOAD|CONF_HTTPPOST)))
/* we send the output to a tty and it isn't an upload operation,
therefore we switch off the progress meter */
config->conf |= CONF_NOPROGRESS;
2000-11-17 15:03:58 +01:00
2001-01-08 08:37:44 +01:00
if (urlnum > 1) {
fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
i+1, urlnum, url, outfile ? outfile : "<stdout>");
if (separator)
printf("%s%s\n", CURLseparator, url);
2000-11-17 15:03:58 +01:00
}
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
if(!config->errors)
config->errors = stderr;
1999-12-29 15:20:26 +01:00
2001-01-24 15:44:05 +01:00
#if defined(WIN32) && !defined(__CYGWIN32__)
2001-01-08 08:37:44 +01:00
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( 1, O_BINARY );
}
2000-11-17 15:03:58 +01:00
#endif
2000-05-22 16:12:12 +02:00
2001-03-12 14:57:02 +01:00
curl_easy_setopt(curl, CURLOPT_FILE, (FILE *)&outs); /* where to store */
/* what call to write: */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
curl_easy_setopt(curl, CURLOPT_INFILE, infd); /* for uploads */
/* size of uploaded file: */
curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize);
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_VERBOSE, config->conf&CONF_VERBOSE);
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, config->conf&CONF_UPLOAD);
curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
config->conf&CONF_FTPLISTONLY);
curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);
curl_easy_setopt(curl, CURLOPT_NETRC, config->conf&CONF_NETRC);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
config->conf&CONF_FOLLOWLOCATION);
curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
curl_easy_setopt(curl, CURLOPT_MUTE, config->conf&CONF_MUTE);
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);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
2001-01-08 08:37:44 +01:00
2001-03-12 14:57:02 +01:00
/* new in libcurl 7.2: */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
2001-01-08 08:37:44 +01:00
2001-03-12 14:57:02 +01:00
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,
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_HTTPPOST, config->httppost);
curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, config->cert_passwd);
if(config->cacert) {
/* available from libcurl 7.5: */
curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
}
2001-03-12 14:57:02 +01:00
if(config->conf&(CONF_NOBODY|CONF_USEREMOTETIME)) {
/* no body or use remote time */
/* new in 7.5 */
curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
}
/* 7.5 news: */
if (config->maxredirs)
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
else
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS);
2001-03-12 14:57:02 +01:00
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_WRITEHEADER,
config->headerfile?&heads:NULL);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
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);
2000-11-17 15:03:58 +01:00
2001-03-12 14:57:02 +01:00
/* 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);
if((config->progressmode == CURL_PROGRESS_BAR) &&
!(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
/* we want the alternative style, then we have to implement it
ourselves! */
progressbarinit(&progressbar);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
}
2001-01-08 08:37:44 +01:00
2001-03-12 14:57:02 +01:00
/* new in libcurl 7.6.2: */
curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
res = curl_easy_perform(curl);
2001-01-08 08:37:44 +01:00
2001-03-12 14:57:02 +01:00
if(config->writeout) {
ourWriteOut(curl, config->writeout);
2000-11-17 15:03:58 +01:00
}
2001-03-12 14:57:02 +01:00
if((res!=CURLE_OK) && config->showerror)
fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
if((config->errors != stderr) &&
(config->errors != stdout))
/* it wasn't directed to stdout or stderr so close the file! */
fclose(config->errors);
if(config->headerfile && !headerfilep && heads.stream)
fclose(heads.stream);
if(urlbuffer)
free(urlbuffer);
if (outfile && !strequal(outfile, "-") && outs.stream)
fclose(outs.stream);
if (config->infile)
fclose(infd);
if(headerfilep)
fclose(headerfilep);
if(url)
free(url);
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
if(outfile)
free(outfile);
2000-11-17 15:03:58 +01:00
}
2001-01-08 08:37:44 +01:00
if(outfiles)
free(outfiles);
if(urls)
/* cleanup memory used for URL globbing patterns */
glob_cleanup(urls);
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
/* empty this urlnode struct */
if(urlnode->url)
free(urlnode->url);
if(urlnode->outfile)
free(urlnode->outfile);
2000-11-17 15:03:58 +01:00
2001-01-08 08:37:44 +01:00
/* move on to the next URL */
nextnode=urlnode->next;
free(urlnode); /* free the node */
urlnode = nextnode;
1999-12-29 15:20:26 +01:00
2001-01-08 08:37:44 +01:00
} /* while-loop through all URLs */
1999-12-29 15:20:26 +01:00
2000-11-17 15:03:58 +01:00
if(allocuseragent)
free(config->useragent);
2000-11-17 15:03:58 +01:00
2001-03-12 14:57:02 +01:00
/* cleanup the curl handle! */
curl_easy_cleanup(curl);
main_free(); /* cleanup the winsock stuff for windows */
return res;
}
int main(int argc, char *argv[])
{
int res;
struct Configurable config;
memset(&config, 0, sizeof(struct Configurable));
res = operate(&config, argc, argv);
free_config_fields(&config);
1999-12-29 15:20:26 +01:00
2000-11-17 15:03:58 +01:00
return res;
1999-12-29 15:20:26 +01:00
}
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;
}