3c477053e5
sorted out the -y and -Y confusion in the help texts
1167 lines
32 KiB
C
1167 lines
32 KiB
C
/*****************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing rights and limitations
|
|
* under the License.
|
|
*
|
|
* The Original Code is Curl.
|
|
*
|
|
* The Initial Developer of the Original Code is Daniel Stenberg.
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1998.
|
|
* All Rights Reserved.
|
|
*
|
|
* ------------------------------------------------------------
|
|
* Main author:
|
|
* - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
|
|
*
|
|
* http://curl.haxx.nu
|
|
*
|
|
* $Source$
|
|
* $Revision$
|
|
* $Date$
|
|
* $Author$
|
|
* $State$
|
|
* $Locker$
|
|
*
|
|
* ------------------------------------------------------------
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
|
|
#include <curl/curl.h>
|
|
#include <curl/mprintf.h>
|
|
#include "../lib/getdate.h"
|
|
#ifdef GLOBURL
|
|
#include "urlglob.h"
|
|
#define CURLseparator "--_curl_--"
|
|
#define MIMEseparator "_curl_"
|
|
#endif
|
|
|
|
/* This is now designed to have its own local setup.h */
|
|
#include "setup.h"
|
|
|
|
#include "version.h"
|
|
|
|
#ifdef HAVE_IO_H /* typical win32 habit */
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
extern void hugehelp(void);
|
|
|
|
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");
|
|
}
|
|
|
|
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/--ftp-ascii Use ASCII transfer (F)\n"
|
|
" -c/--continue Resume a previous transfer where we left it\n"
|
|
" -C/--continue-at <offset> Specify absolute resume offset\n"
|
|
" -d/--data POST data (H)\n"
|
|
" -D/--dump-header <file> Write the headers to this file\n"
|
|
" -e/--referer Referer page (H)\n"
|
|
" -E/--cert <cert:passwd> Specifies your certificate file and password (HTTPS)\n"
|
|
" -f/--fail Fail silently (no output at all) on errors (H)\n"
|
|
" -F/--form <name=content> Specify HTTP POST data (H)\n"
|
|
|
|
" -h/--help This help text\n"
|
|
" -H/--header <line> Custom header to pass to server. (H)\n"
|
|
" -i/--include Include the HTTP-header in the output (H)\n"
|
|
" -I/--head Fetch document info only (HTTP HEAD/FTP SIZE)\n"
|
|
" -K/--config Specify which config file to read\n"
|
|
" -l/--list-only List only names of an FTP directory (F)\n"
|
|
" -L/--location Follow Location: hints (H)\n"
|
|
" -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"
|
|
" -o/--output <file> Write output to <file> instead of stdout\n"
|
|
" -O/--remote-name Write output to a file named as the remote file\n"
|
|
#if 0
|
|
" -p/--port <port> Use port other than default for current protocol.\n"
|
|
#endif
|
|
" -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)\n"
|
|
" -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server\n"
|
|
" -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/--upload Transfer/upload stdin to remote site\n"
|
|
" -T/--upload-file <file> Transfer/upload <file> to remote site\n"
|
|
" -u/--user <user:password> Specify user and password to use\n"
|
|
" -U/--proxy-user <user:password> Specify Proxy authentication\n"
|
|
" -v/--verbose Makes the operation more talkative\n"
|
|
" -V/--version Outputs version number then quits\n"
|
|
" -x/--proxy <host> Use proxy. (Default port is 1080)\n"
|
|
" -X/--request <command> Specific request command to use\n"
|
|
" -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"
|
|
" -z/--time-cond <time> Includes a time condition to the server (H)\n"
|
|
" -2/--sslv2 Force usage of SSLv2 (H)\n"
|
|
" -3/--sslv3 Force usage of SSLv3 (H)\n"
|
|
" -#/--progress-bar Display transfer progress as a progress bar\n"
|
|
" --crlf Convert LF to CRLF in upload. Useful for MVS (OS/390)\n"
|
|
" --stderr <file> Where to redirect stderr. - means stdout.\n",
|
|
curl_version()
|
|
);
|
|
}
|
|
|
|
struct LongShort {
|
|
char *letter;
|
|
char *lname;
|
|
bool extraparam;
|
|
};
|
|
|
|
struct Configurable {
|
|
char *useragent;
|
|
char *cookie;
|
|
bool use_resume;
|
|
int resume_from;
|
|
char *postfields;
|
|
char *referer;
|
|
long timeout;
|
|
char *outfile;
|
|
char *headerfile;
|
|
char remotefile;
|
|
char *ftpport;
|
|
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;
|
|
long conf;
|
|
char *url;
|
|
char *cert;
|
|
char *cert_passwd;
|
|
bool crlf;
|
|
char *cookiefile;
|
|
char *customrequest;
|
|
bool progressmode;
|
|
|
|
FILE *errors; /* if stderr redirect is requested */
|
|
|
|
struct curl_slist *quote;
|
|
struct curl_slist *postquote;
|
|
|
|
long ssl_version;
|
|
TimeCond timecond;
|
|
time_t condtime;
|
|
|
|
struct HttpHeader *headers;
|
|
struct HttpHeader *last_header;
|
|
|
|
struct HttpPost *httppost;
|
|
struct HttpPost *last_post;
|
|
};
|
|
|
|
static int parseconfig(char *filename,
|
|
struct Configurable *config);
|
|
|
|
static void GetStr(char **string,
|
|
char *value)
|
|
{
|
|
if(*string)
|
|
free(*string);
|
|
*string = strdup(value);
|
|
}
|
|
|
|
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 int getparameter(char *flag, /* f or -long-flag */
|
|
char *nextarg, /* NULL if unset */
|
|
bool *usedarg, /* set to TRUE if the arg has been
|
|
used */
|
|
struct Configurable *config)
|
|
{
|
|
char letter;
|
|
char *parse=NULL;
|
|
int res;
|
|
struct HttpHeader *head;
|
|
int j;
|
|
time_t now;
|
|
int hit=-1;
|
|
|
|
/* single-letter,
|
|
long-name,
|
|
boolean whether it takes an additional argument
|
|
*/
|
|
struct LongShort aliases[]= {
|
|
{"9", "crlf", FALSE},
|
|
{"8", "stderr", TRUE},
|
|
|
|
{"2", "sslv2", FALSE},
|
|
{"3", "sslv3", FALSE},
|
|
{"a", "append", FALSE},
|
|
{"A", "user-agent", TRUE},
|
|
{"b", "cookie", TRUE},
|
|
{"B", "ftp-ascii", FALSE},
|
|
{"c", "continue", FALSE},
|
|
{"C", "continue-at", TRUE},
|
|
{"d", "data", TRUE},
|
|
{"D", "dump-header", TRUE},
|
|
{"e", "referer", TRUE},
|
|
{"E", "cert", TRUE},
|
|
{"f", "fail", FALSE},
|
|
{"F", "form", TRUE},
|
|
|
|
{"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},
|
|
{"o", "output", TRUE},
|
|
{"O", "remote-name", FALSE},
|
|
#if 0
|
|
{"p", "port", TRUE},
|
|
#endif
|
|
{"P", "ftpport", TRUE},
|
|
{"q", "disable", FALSE},
|
|
{"Q", "quote", TRUE},
|
|
{"r", "range", TRUE},
|
|
{"s", "silent", FALSE},
|
|
{"S", "show-error", FALSE},
|
|
{"t", "upload", FALSE},
|
|
{"T", "upload-file", TRUE},
|
|
{"u", "user", TRUE},
|
|
{"U", "proxy-user", TRUE},
|
|
{"v", "verbose", FALSE},
|
|
{"V", "version", FALSE},
|
|
{"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]) {
|
|
/* try a long name */
|
|
int fnam=strlen(&flag[1]);
|
|
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
|
|
if(strnequal(aliases[j].lname, &flag[1], fnam)) {
|
|
if(strequal(aliases[j].lname, &flag[1])) {
|
|
parse = aliases[j].letter;
|
|
hit = j;
|
|
break;
|
|
}
|
|
if(parse) {
|
|
/* this is the second match, we can't continue! */
|
|
helpf("option --%s is ambiguous\n", &flag[1]);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
parse = aliases[j].letter;
|
|
hit = j;
|
|
}
|
|
}
|
|
if(hit < 0) {
|
|
helpf("unknown option -%s.\n", flag);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
}
|
|
else {
|
|
hit=-1;
|
|
parse = flag;
|
|
}
|
|
|
|
do {
|
|
/* we can loop here if we have multiple single-letters */
|
|
|
|
letter = parse?*parse:'\0';
|
|
*usedarg = FALSE; /* default is that we don't use the arg */
|
|
|
|
#if 0
|
|
fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
|
|
#endif
|
|
if(hit < 0) {
|
|
for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
|
|
if(letter == *aliases[j].letter) {
|
|
hit = j;
|
|
break;
|
|
}
|
|
}
|
|
if(hit < 0) {
|
|
helpf("unknown option -%c.\n", letter);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
}
|
|
if(hit < 0) {
|
|
helpf("unknown option -%c.\n", letter);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
if(!nextarg && aliases[hit].extraparam) {
|
|
helpf("option -%s/--%s requires an extra argument!\n",
|
|
aliases[hit].letter,
|
|
aliases[hit].lname);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
else if(nextarg && aliases[hit].extraparam)
|
|
*usedarg = TRUE; /* mark it as used */
|
|
|
|
switch(letter) {
|
|
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=get_date(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 '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;
|
|
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(strchr(nextarg, '=')) {
|
|
/* A cookie string must have a =-letter */
|
|
GetStr(&config->cookie, nextarg);
|
|
}
|
|
else {
|
|
/* We have a cookie file to read from! */
|
|
GetStr(&config->cookiefile, nextarg);
|
|
}
|
|
break;
|
|
case 'B':
|
|
/* use type ASCII when transfering ftp files */
|
|
config->conf ^= CONF_FTPASCII;
|
|
break;
|
|
case 'c':
|
|
/* This makes us continue an ftp transfer */
|
|
config->use_resume^=TRUE;
|
|
break;
|
|
case 'C':
|
|
/* This makes us continue an ftp transfer at given position */
|
|
config->resume_from= atoi(nextarg);
|
|
config->use_resume=TRUE;
|
|
break;
|
|
case 'd':
|
|
/* postfield data */
|
|
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->postfields = file2string(file);
|
|
if(file && (file != stdin))
|
|
fclose(stdin);
|
|
}
|
|
else {
|
|
GetStr(&config->postfields, nextarg);
|
|
}
|
|
if(config->postfields)
|
|
config->conf |= CONF_POST;
|
|
break;
|
|
case 'D':
|
|
/* dump-header to given file name */
|
|
GetStr(&config->headerfile, nextarg);
|
|
break;
|
|
case 'e':
|
|
GetStr(&config->referer, nextarg);
|
|
config->conf |= CONF_REFERER;
|
|
break;
|
|
case 'E':
|
|
{
|
|
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,
|
|
&config->httppost,
|
|
&config->last_post))
|
|
return URG_FAILED_INIT;
|
|
config->conf |= CONF_HTTPPOST; /* no toggle, OR! */
|
|
break;
|
|
|
|
case 'h': /* h for help */
|
|
help();
|
|
return URG_FAILED_INIT;
|
|
case 'H':
|
|
head = (struct HttpHeader *)malloc(sizeof(struct HttpHeader));
|
|
if(head) {
|
|
head->next = NULL;
|
|
head->header = NULL; /* first zero this */
|
|
GetStr(&head->header, nextarg); /* now get the header line */
|
|
|
|
/* point on our new one */
|
|
if(config->last_header)
|
|
config->last_header->next = head;
|
|
else {
|
|
config->headers = head;
|
|
}
|
|
|
|
config->last_header = head;
|
|
}
|
|
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 */
|
|
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 URG_FAILED_INIT;
|
|
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 'o':
|
|
/* output file */
|
|
GetStr(&config->outfile, nextarg); /* write to this file */
|
|
break;
|
|
case 'O':
|
|
/* output file */
|
|
config->remotefile ^= TRUE;
|
|
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!
|
|
*/
|
|
config->conf |= CONF_FTPPORT;
|
|
GetStr(&config->ftpport, nextarg);
|
|
break;
|
|
#if 0
|
|
case 'p':
|
|
/* specified port */
|
|
fputs("You've used the -p option, it will be removed in a future version\n",
|
|
stderr);
|
|
config->porttouse = atoi(nextarg);
|
|
config->conf |= CONF_PORT; /* changed port */
|
|
break;
|
|
#endif
|
|
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 */
|
|
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);
|
|
}
|
|
break;
|
|
case 'r':
|
|
/* byte range requested */
|
|
GetStr(&config->range, nextarg);
|
|
config->conf |= CONF_RANGE;
|
|
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':
|
|
/* we are uploading */
|
|
config->conf ^= CONF_UPLOAD;
|
|
break;
|
|
case 'T':
|
|
/* we are uploading */
|
|
config->conf |= CONF_UPLOAD;
|
|
GetStr(&config->infile, nextarg);
|
|
break;
|
|
case 'u':
|
|
/* user:password */
|
|
GetStr(&config->userpwd, nextarg);
|
|
config->conf |= CONF_USERPWD;
|
|
break;
|
|
case 'U':
|
|
/* Proxy user:password */
|
|
GetStr(&config->proxyuserpwd, nextarg);
|
|
config->conf |= CONF_PROXYUSERPWD;
|
|
break;
|
|
case 'v':
|
|
config->conf ^= CONF_VERBOSE; /* talk a lot */
|
|
break;
|
|
case 'V':
|
|
printf(CURL_ID "%s\n", curl_version());
|
|
return URG_FAILED_INIT;
|
|
case 'x':
|
|
/* proxy */
|
|
if(!*nextarg) {
|
|
/* disable proxy when no proxy is given */
|
|
config->conf &= ~CONF_PROXY;
|
|
}
|
|
else {
|
|
config->conf |= CONF_PROXY;
|
|
GetStr(&config->proxy, nextarg);
|
|
}
|
|
break;
|
|
case 'X':
|
|
/* HTTP request */
|
|
GetStr(&config->customrequest, nextarg);
|
|
break;
|
|
case 'y':
|
|
/* low speed time */
|
|
config->low_speed_time = atoi(nextarg);
|
|
if(!config->low_speed_limit)
|
|
config->low_speed_limit = 1;
|
|
break;
|
|
case 'Y':
|
|
/* low speed limit */
|
|
config->low_speed_limit = atoi(nextarg);
|
|
if(!config->low_speed_time)
|
|
config->low_speed_time=30;
|
|
break;
|
|
|
|
default: /* unknown flag */
|
|
if(letter)
|
|
helpf("Unknown option '%c'\n", letter);
|
|
else
|
|
helpf("Unknown option\n"); /* short help blurb */
|
|
return URG_FAILED_INIT;
|
|
}
|
|
hit = -1;
|
|
|
|
} while(*++parse && !*usedarg);
|
|
|
|
return URG_OK;
|
|
}
|
|
|
|
|
|
static int parseconfig(char *filename,
|
|
struct Configurable *config)
|
|
{
|
|
int res;
|
|
FILE *file;
|
|
char configbuffer[4096];
|
|
char filebuffer[256];
|
|
bool usedarg;
|
|
|
|
if(!filename || !*filename) {
|
|
/* NULL or no file name attempts to load .curlrc from the homedir! */
|
|
|
|
#define CURLRC DOT_CHAR "curlrc"
|
|
|
|
char *home = curl_GetEnv("HOME"); /* portable environment reader */
|
|
|
|
if(!home || (strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))))
|
|
return URG_OK;
|
|
|
|
sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
|
|
|
|
filename = filebuffer;
|
|
}
|
|
|
|
if(strcmp(filename,"-"))
|
|
file = fopen(filename, "r");
|
|
else
|
|
file = stdin;
|
|
|
|
if(file) {
|
|
char *tok;
|
|
char *tok2;
|
|
while(fgets(configbuffer, sizeof(configbuffer), file)) {
|
|
/* lines with # in the fist column is a comment! */
|
|
|
|
#if 0
|
|
fprintf(stderr, "%s", configbuffer);
|
|
#endif
|
|
if('#' == configbuffer[0])
|
|
continue;
|
|
tok = configbuffer;
|
|
|
|
while(*tok && isspace((int)*tok))
|
|
tok++;
|
|
/* tok=strtok(configbuffer, " \t\n"); */
|
|
#if 0
|
|
fprintf(stderr, "TOK: %s\n", tok);
|
|
#endif
|
|
if('-' != tok[0]) {
|
|
char *nl;
|
|
if(config->url)
|
|
free(config->url);
|
|
config->url = strdup(tok);
|
|
nl = strchr(config->url, '\n');
|
|
if(nl)
|
|
*nl=0;
|
|
}
|
|
while(('-' == tok[0])) {
|
|
/* this is a flag */
|
|
char *firsttok = strdup(tok);
|
|
char *nl;
|
|
|
|
/* remove newline from firsttok */
|
|
nl = strchr(firsttok, '\n');
|
|
if(nl)
|
|
*nl=0;
|
|
|
|
/* pass the -flag */
|
|
tok2=tok;
|
|
while(*tok2 && !isspace((int)*tok2))
|
|
tok2++;
|
|
|
|
/* pass the following white space */
|
|
while(*tok2 && isspace((int)*tok2))
|
|
tok2++;
|
|
|
|
while(!*tok2 &&
|
|
fgets(configbuffer, sizeof(configbuffer), file)) {
|
|
/* lines with # in the fist column is a comment! */
|
|
#if 0
|
|
fprintf(stderr, "%s", configbuffer);
|
|
#endif
|
|
if('#' == configbuffer[0])
|
|
continue;
|
|
tok2 = configbuffer;
|
|
/* tok2=strtok(configbuffer, " \t\n"); */
|
|
/* pass white space */
|
|
while(*tok2 && isspace((int)*tok2))
|
|
tok2++;
|
|
}
|
|
/* remove newline from tok2 */
|
|
nl = strchr(tok2, '\n');
|
|
if(nl)
|
|
*nl=0;
|
|
|
|
res = getparameter(firsttok+1,
|
|
*tok2?tok2:NULL,
|
|
&usedarg,
|
|
config);
|
|
free(firsttok);
|
|
#if 0
|
|
fprintf(stderr, "TOK %s TOK2: %s RES: %d\n",
|
|
firsttok, tok2?tok2:"NULL", res);
|
|
#endif
|
|
if(res)
|
|
return res;
|
|
if(!usedarg) {
|
|
/* tok2 is unused, */
|
|
tok = tok2;
|
|
}
|
|
else
|
|
break; /* we've used both our words */
|
|
}
|
|
}
|
|
if(file != stdin)
|
|
fclose(file);
|
|
}
|
|
return URG_OK;
|
|
}
|
|
|
|
struct OutStruct {
|
|
char *filename;
|
|
FILE *stream;
|
|
};
|
|
|
|
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 */
|
|
}
|
|
return fwrite(buffer, size, nmemb, out->stream);
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char errorbuffer[URLGET_ERROR_SIZE];
|
|
|
|
struct OutStruct outs;
|
|
|
|
char *url = NULL;
|
|
#ifdef GLOBURL
|
|
URLGlob *urls;
|
|
int urlnum;
|
|
char *outfiles = NULL;
|
|
int separator = 0;
|
|
#endif
|
|
|
|
FILE *infd = stdin;
|
|
FILE *headerfilep = NULL;
|
|
char *urlbuffer=NULL;
|
|
int infilesize=-1; /* -1 means unknown */
|
|
bool stillflags=TRUE;
|
|
|
|
int res=URG_OK;
|
|
int i;
|
|
struct Configurable config;
|
|
|
|
outs.stream = stdout;
|
|
|
|
memset(&config, 0, sizeof(struct Configurable));
|
|
|
|
/* set non-zero default values: */
|
|
config.useragent= maprintf(CURL_NAME "/" CURL_VERSION " (" OS ") "
|
|
"%s", curl_version());
|
|
config.showerror=TRUE;
|
|
config.conf=CONF_DEFAULT;
|
|
#if 0
|
|
config.crlf=FALSE;
|
|
config.quote=NULL;
|
|
#endif
|
|
|
|
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!
|
|
*/
|
|
#if 0
|
|
fprintf(stderr, "I TURNED OFF THE CRAP\n");
|
|
#endif
|
|
;
|
|
}
|
|
else {
|
|
res = parseconfig(NULL, &config);
|
|
if(res)
|
|
return res;
|
|
}
|
|
|
|
if ((argc < 2) && !config.url) {
|
|
helpf(NULL);
|
|
return URG_FAILED_INIT;
|
|
}
|
|
|
|
/* Parse options */
|
|
for (i = 1; i < argc; i++) {
|
|
if(stillflags &&
|
|
('-' == argv[i][0])) {
|
|
char *nextarg;
|
|
bool passarg;
|
|
|
|
char *flag = &argv[i][1];
|
|
|
|
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)
|
|
return res;
|
|
|
|
if(passarg) /* we're supposed to skip this */
|
|
i++;
|
|
}
|
|
}
|
|
else {
|
|
if(url) {
|
|
helpf("only one URL is supported!\n");
|
|
return URG_FAILED_INIT;
|
|
}
|
|
url = argv[i];
|
|
}
|
|
}
|
|
|
|
/* if no URL was specified and there was one in the config file, get that
|
|
one */
|
|
if(!url && config.url)
|
|
url = config.url;
|
|
|
|
if(!url) {
|
|
helpf("no URL specified!\n");
|
|
return URG_FAILED_INIT;
|
|
}
|
|
#if 0
|
|
fprintf(stderr, "URL: %s PROXY: %s\n", url, config.proxy?config.proxy:"none");
|
|
#endif
|
|
|
|
#ifdef GLOBURL
|
|
urlnum = glob_url(&urls, url); /* expand '{...}' and '[...]' expressions and return
|
|
total number of URLs in pattern set */
|
|
outfiles = config.outfile; /* save outfile pattern befor expansion */
|
|
if (!outfiles && !config.remotefile && urlnum > 1) {
|
|
#ifdef CURL_SEPARATORS
|
|
/* multiple files extracted to stdout, insert separators! */
|
|
separator = 1;
|
|
#endif
|
|
#ifdef MIME_SEPARATORS
|
|
/* multiple files extracted to stdout, insert MIME separators! */
|
|
separator = 1;
|
|
printf("MIME-Version: 1.0\n");
|
|
printf("Content-Type: multipart/mixed; boundary=%s\n\n", MIMEseparator);
|
|
#endif
|
|
}
|
|
for (i = 0; (url = next_url(urls)); ++i) {
|
|
if (outfiles)
|
|
config.outfile = strdup(outfiles);
|
|
#endif
|
|
|
|
if(config.outfile && config.infile) {
|
|
helpf("you can't both upload and download!\n");
|
|
return URG_FAILED_INIT;
|
|
}
|
|
|
|
if (config.outfile || config.remotefile) {
|
|
/*
|
|
* We have specified a file name to store the result in, or we have
|
|
* decided we want to use the remote file name.
|
|
*/
|
|
|
|
if(config.remotefile) {
|
|
/* Find and get the remote file name */
|
|
config.outfile=strstr(url, "://");
|
|
if(config.outfile)
|
|
config.outfile+=3;
|
|
else
|
|
config.outfile=url;
|
|
config.outfile = strrchr(config.outfile, '/');
|
|
if(!config.outfile || !strlen(++config.outfile)) {
|
|
helpf("Remote file name has no length!\n");
|
|
return URG_WRITE_ERROR;
|
|
}
|
|
}
|
|
#ifdef GLOBURL
|
|
else /* fill '#1' ... '#9' terms from URL pattern */
|
|
config.outfile = match_url(config.outfile, *urls);
|
|
#endif
|
|
|
|
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 */
|
|
struct stat fileinfo;
|
|
|
|
if(0 == stat(config.outfile, &fileinfo)) {
|
|
/* set offset to current file size: */
|
|
config.resume_from = fileinfo.st_size;
|
|
}
|
|
/* else let offset remain 0 */
|
|
}
|
|
|
|
if(config.resume_from) {
|
|
/* open file for output: */
|
|
outs.stream=(FILE *) fopen(config.outfile, config.resume_from?"ab":"wb");
|
|
if (!outs.stream) {
|
|
helpf("Can't open '%s'!\n", config.outfile);
|
|
return URG_WRITE_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
outs.filename = config.outfile;
|
|
outs.stream = NULL; /* open when needed */
|
|
}
|
|
}
|
|
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, "://");
|
|
if(ptr)
|
|
ptr+=3;
|
|
else
|
|
ptr=url;
|
|
ptr = strrchr(ptr, '/');
|
|
if(!ptr || !strlen(++ptr)) {
|
|
/* The URL has no file name part, add the local file name. In order
|
|
to be able to do so, we have to create a new URL in another buffer.*/
|
|
urlbuffer=(char *)malloc(strlen(url) + strlen(config.infile) + 3);
|
|
if(!urlbuffer) {
|
|
helpf("out of memory\n");
|
|
return URG_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! */
|
|
}
|
|
|
|
infd=(FILE *) fopen(config.infile, "rb");
|
|
if (!infd || stat(config.infile, &fileinfo)) {
|
|
helpf("Can't open '%s'!\n", config.infile);
|
|
return URG_READ_ERROR;
|
|
}
|
|
infilesize=fileinfo.st_size;
|
|
|
|
}
|
|
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,"-"))
|
|
{
|
|
headerfilep=(FILE *) fopen(config.headerfile, "wb");
|
|
if (!headerfilep) {
|
|
helpf("Can't open '%s'!\n", config.headerfile);
|
|
return URG_WRITE_ERROR;
|
|
}
|
|
}
|
|
else
|
|
headerfilep=stdout;
|
|
}
|
|
|
|
/* This was previously done in urlget, but that was wrong place to do it */
|
|
if(outs.stream && isatty(fileno(outs.stream)))
|
|
/* we send the output to a tty, and therefor we switch off the progress
|
|
meter right away */
|
|
config.conf |= CONF_NOPROGRESS;
|
|
|
|
#ifdef GLOBURL
|
|
if (urlnum > 1) {
|
|
fprintf(stderr, "\n[%d/%d]: %s --> %s\n", i+1, urlnum, url, config.outfile ? config.outfile : "<stdout>");
|
|
if (separator) {
|
|
#ifdef CURL_SEPARATORS
|
|
printf("%s%s\n", CURLseparator, url);
|
|
#endif
|
|
#ifdef MIME_SEPARATORS
|
|
printf("--%s\n", MIMEseparator);
|
|
printf("Content-ID: %s\n\n", url);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(!config.errors)
|
|
config.errors = stderr;
|
|
|
|
res = curl_urlget(URGTAG_FILE, (FILE *)&outs, /* where to store */
|
|
URGTAG_WRITEFUNCTION, my_fwrite, /* what call to write */
|
|
URGTAG_INFILE, infd, /* for uploads */
|
|
URGTAG_INFILESIZE, infilesize, /* size of uploaded file */
|
|
URGTAG_URL, url, /* what to fetch */
|
|
URGTAG_PROXY, config.proxy, /* proxy to use */
|
|
URGTAG_FLAGS, config.conf, /* flags */
|
|
URGTAG_USERPWD, config.userpwd, /* user + passwd */
|
|
URGTAG_PROXYUSERPWD, config.proxyuserpwd, /* Proxy user + passwd */
|
|
URGTAG_RANGE, config.range, /* range of document */
|
|
URGTAG_ERRORBUFFER, errorbuffer,
|
|
URGTAG_TIMEOUT, config.timeout,
|
|
URGTAG_POSTFIELDS, config.postfields,
|
|
URGTAG_REFERER, config.referer,
|
|
URGTAG_USERAGENT, config.useragent,
|
|
URGTAG_FTPPORT, config.ftpport,
|
|
URGTAG_LOW_SPEED_LIMIT, config.low_speed_limit,
|
|
URGTAG_LOW_SPEED_TIME, config.low_speed_time,
|
|
URGTAG_RESUME_FROM, config.use_resume?config.resume_from:0,
|
|
URGTAG_COOKIE, config.cookie,
|
|
URGTAG_HTTPHEADER, config.headers,
|
|
URGTAG_HTTPPOST, config.httppost,
|
|
URGTAG_SSLCERT, config.cert,
|
|
URGTAG_SSLCERTPASSWD, config.cert_passwd,
|
|
URGTAG_CRLF, config.crlf,
|
|
URGTAG_QUOTE, config.quote,
|
|
URGTAG_POSTQUOTE, config.postquote,
|
|
URGTAG_WRITEHEADER, headerfilep,
|
|
URGTAG_COOKIEFILE, config.cookiefile,
|
|
URGTAG_SSLVERSION, config.ssl_version,
|
|
URGTAG_TIMECONDITION, config.timecond,
|
|
URGTAG_TIMEVALUE, config.condtime,
|
|
URGTAG_CUSTOMREQUEST, config.customrequest,
|
|
URGTAG_STDERR, config.errors,
|
|
URGTAG_PROGRESSMODE, config.progressmode,
|
|
URGTAG_DONE); /* always terminate the list of tags */
|
|
if((res!=URG_OK) && config.showerror)
|
|
fprintf(config.errors, "curl: (%d) %s\n", res, errorbuffer);
|
|
|
|
if((config.errors != stderr) &&
|
|
(config.errors != stdout))
|
|
/* it wasn't directed to stdout or stderr so close the file! */
|
|
fclose(config.errors);
|
|
|
|
if(urlbuffer)
|
|
free(urlbuffer);
|
|
if (config.outfile && outs.stream)
|
|
fclose(outs.stream);
|
|
if (config.infile)
|
|
fclose(infd);
|
|
if(headerfilep)
|
|
fclose(headerfilep);
|
|
|
|
if(config.url)
|
|
free(config.url);
|
|
|
|
#ifdef GLOBURL
|
|
if(url)
|
|
free(url);
|
|
if(config.outfile && !config.remotefile)
|
|
free(config.outfile);
|
|
}
|
|
#ifdef MIME_SEPARATORS
|
|
if (separator)
|
|
printf("--%s--\n", MIMEseparator);
|
|
#endif
|
|
#endif
|
|
|
|
curl_slist_free_all(config.quote); /* the checks for config.quote == NULL */
|
|
|
|
return(res);
|
|
}
|