mirror of
https://github.com/curl/curl.git
synced 2025-09-07 21:05:09 +03:00
tool_operate: split up single_transfer
Complexity reduced from 124 to 83 Remove whitelisting of this function from the complexity script. Closes #17437
This commit is contained in:
parent
65e4444d67
commit
ed07f59841
|
@ -75,8 +75,7 @@ my @output=`$cmd`;
|
|||
|
||||
# these functions can have these scores, but not higher
|
||||
my %whitelist = (
|
||||
'getparameter' => 142,
|
||||
'single_transfer' => 124
|
||||
'getparameter' => 142
|
||||
);
|
||||
|
||||
# functions with complexity above this level causes the function to return error
|
||||
|
|
|
@ -796,6 +796,283 @@ static CURLcode append2query(struct GlobalConfig *global,
|
|||
return result;
|
||||
}
|
||||
|
||||
static CURLcode etag_compare(struct GlobalConfig *global,
|
||||
struct OperationConfig *config)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *etag_from_file = NULL;
|
||||
char *header = NULL;
|
||||
ParameterError pe;
|
||||
|
||||
/* open file for reading: */
|
||||
FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
|
||||
if(!file)
|
||||
warnf(global, "Failed to open %s: %s", config->etag_compare_file,
|
||||
strerror(errno));
|
||||
|
||||
if((PARAM_OK == file2string(&etag_from_file, file)) &&
|
||||
etag_from_file) {
|
||||
header = aprintf("If-None-Match: %s", etag_from_file);
|
||||
tool_safefree(etag_from_file);
|
||||
}
|
||||
else
|
||||
header = aprintf("If-None-Match: \"\"");
|
||||
|
||||
if(!header) {
|
||||
if(file)
|
||||
fclose(file);
|
||||
errorf(global,
|
||||
"Failed to allocate memory for custom etag header");
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* add Etag from file to list of custom headers */
|
||||
pe = add2list(&config->headers, header);
|
||||
tool_safefree(header);
|
||||
|
||||
if(file)
|
||||
fclose(file);
|
||||
if(pe != PARAM_OK)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode etag_store(struct GlobalConfig *global,
|
||||
struct OperationConfig *config,
|
||||
struct OutStruct *etag_save,
|
||||
bool *skip)
|
||||
{
|
||||
if(config->create_dirs) {
|
||||
CURLcode result = create_dir_hierarchy(config->etag_save_file, global);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* open file for output: */
|
||||
if(strcmp(config->etag_save_file, "-")) {
|
||||
FILE *newfile = fopen(config->etag_save_file, "ab");
|
||||
if(!newfile) {
|
||||
struct State *state = &config->state;
|
||||
warnf(global, "Failed creating file for saving etags: \"%s\". "
|
||||
"Skip this transfer", config->etag_save_file);
|
||||
tool_safefree(state->outfiles);
|
||||
glob_cleanup(&state->urls);
|
||||
*skip = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
else {
|
||||
etag_save->filename = config->etag_save_file;
|
||||
etag_save->s_isreg = TRUE;
|
||||
etag_save->fopened = TRUE;
|
||||
etag_save->stream = newfile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* always use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(etag_save->stream);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode setup_headerfile(struct GlobalConfig *global,
|
||||
struct OperationConfig *config,
|
||||
struct per_transfer *per,
|
||||
struct OutStruct *heads)
|
||||
{
|
||||
/* open file for output: */
|
||||
if(!strcmp(config->headerfile, "%")) {
|
||||
heads->stream = stderr;
|
||||
/* use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(heads->stream);
|
||||
}
|
||||
else if(strcmp(config->headerfile, "-")) {
|
||||
FILE *newfile;
|
||||
|
||||
/*
|
||||
* Since every transfer has its own file handle for dumping
|
||||
* the headers, we need to open it in append mode, since transfers
|
||||
* might finish in any order.
|
||||
* The first transfer just clears the file.
|
||||
*
|
||||
* Consider placing the file handle inside the OperationConfig, so
|
||||
* that it does not need to be opened/closed for every transfer.
|
||||
*/
|
||||
if(config->create_dirs) {
|
||||
CURLcode result = create_dir_hierarchy(config->headerfile, global);
|
||||
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
if(!per->prev || per->prev->config != config) {
|
||||
newfile = fopen(config->headerfile, "wb");
|
||||
if(newfile)
|
||||
fclose(newfile);
|
||||
}
|
||||
newfile = fopen(config->headerfile, "ab");
|
||||
|
||||
if(!newfile) {
|
||||
errorf(global, "Failed to open %s", config->headerfile);
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
else {
|
||||
heads->filename = config->headerfile;
|
||||
heads->s_isreg = TRUE;
|
||||
heads->fopened = TRUE;
|
||||
heads->stream = newfile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* always use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(heads->stream);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode setup_outfile(struct GlobalConfig *global,
|
||||
struct OperationConfig *config,
|
||||
struct per_transfer *per,
|
||||
struct OutStruct *outs,
|
||||
bool *skipped)
|
||||
{
|
||||
/*
|
||||
* We have specified a filename to store the result in, or we have
|
||||
* decided we want to use the remote filename.
|
||||
*/
|
||||
struct State *state = &config->state;
|
||||
|
||||
if(!per->outfile) {
|
||||
/* extract the filename from the URL */
|
||||
CURLcode result = get_url_file_name(global, &per->outfile, per->url);
|
||||
if(result) {
|
||||
errorf(global, "Failed to extract a filename"
|
||||
" from the URL to use for storage");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if(state->urls) {
|
||||
/* fill '#1' ... '#9' terms from URL pattern */
|
||||
char *storefile = per->outfile;
|
||||
CURLcode result = glob_match_url(&per->outfile, storefile, state->urls);
|
||||
tool_safefree(storefile);
|
||||
if(result) {
|
||||
/* bad globbing */
|
||||
warnf(global, "bad output glob");
|
||||
return result;
|
||||
}
|
||||
if(!*per->outfile) {
|
||||
warnf(global, "output glob produces empty string");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
}
|
||||
DEBUGASSERT(per->outfile);
|
||||
|
||||
if(config->output_dir && *config->output_dir) {
|
||||
char *d = aprintf("%s/%s", config->output_dir, per->outfile);
|
||||
if(!d)
|
||||
return CURLE_WRITE_ERROR;
|
||||
free(per->outfile);
|
||||
per->outfile = d;
|
||||
}
|
||||
/* Create the directory hierarchy, if not pre-existent to a multiple
|
||||
file output call */
|
||||
|
||||
if(config->create_dirs) {
|
||||
CURLcode result = create_dir_hierarchy(per->outfile, global);
|
||||
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(config->skip_existing) {
|
||||
struct_stat fileinfo;
|
||||
if(!stat(per->outfile, &fileinfo)) {
|
||||
/* file is present */
|
||||
notef(global, "skips transfer, \"%s\" exists locally",
|
||||
per->outfile);
|
||||
per->skip = TRUE;
|
||||
*skipped = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(config->resume_from_current) {
|
||||
/* We are told to continue from where we are now. Get the size
|
||||
of the file as it is now and open it for append instead */
|
||||
struct_stat fileinfo;
|
||||
/* VMS -- Danger, the filesize is only valid for stream files */
|
||||
if(0 == stat(per->outfile, &fileinfo))
|
||||
/* set offset to current file size: */
|
||||
config->resume_from = fileinfo.st_size;
|
||||
else
|
||||
/* let offset be 0 */
|
||||
config->resume_from = 0;
|
||||
}
|
||||
|
||||
if(config->resume_from) {
|
||||
#ifdef __VMS
|
||||
/* open file for output, forcing VMS output format into stream
|
||||
mode which is needed for stat() call above to always work. */
|
||||
FILE *file = fopen(outfile, "ab",
|
||||
"ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
|
||||
#else
|
||||
/* open file for output: */
|
||||
FILE *file = fopen(per->outfile, "ab");
|
||||
#endif
|
||||
if(!file) {
|
||||
errorf(global, "cannot open '%s'", per->outfile);
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
outs->fopened = TRUE;
|
||||
outs->stream = file;
|
||||
outs->init = config->resume_from;
|
||||
}
|
||||
else {
|
||||
outs->stream = NULL; /* open when needed */
|
||||
}
|
||||
outs->filename = per->outfile;
|
||||
outs->s_isreg = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void check_stdin_upload(struct GlobalConfig *global,
|
||||
struct OperationConfig *config,
|
||||
struct per_transfer *per)
|
||||
{
|
||||
/* count to see if there are more than one auth bit set
|
||||
in the authtype field */
|
||||
int authbits = 0;
|
||||
int bitcheck = 0;
|
||||
while(bitcheck < 32) {
|
||||
if(config->authtype & (1UL << bitcheck++)) {
|
||||
authbits++;
|
||||
if(authbits > 1) {
|
||||
/* more than one, we are done! */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user has also selected --anyauth or --proxy-anyauth
|
||||
* we should warn them.
|
||||
*/
|
||||
if(config->proxyanyauth || (authbits > 1)) {
|
||||
warnf(global,
|
||||
"Using --anyauth or --proxy-anyauth with upload from stdin"
|
||||
" involves a big risk of it not working. Use a temporary"
|
||||
" file or a fixed auth type instead");
|
||||
}
|
||||
|
||||
DEBUGASSERT(per->infdopen == FALSE);
|
||||
DEBUGASSERT(per->infd == STDIN_FILENO);
|
||||
|
||||
CURL_SET_BINMODE(stdin);
|
||||
if(!strcmp(per->uploadfile, ".")) {
|
||||
if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
|
||||
warnf(global,
|
||||
"fcntl failed on fd=%d: %s", per->infd, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* create the next (singular) transfer */
|
||||
static CURLcode single_transfer(struct GlobalConfig *global,
|
||||
struct OperationConfig *config,
|
||||
|
@ -931,73 +1208,16 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||
|
||||
/* --etag-compare */
|
||||
if(config->etag_compare_file) {
|
||||
char *etag_from_file = NULL;
|
||||
char *header = NULL;
|
||||
ParameterError pe;
|
||||
|
||||
/* open file for reading: */
|
||||
FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
|
||||
if(!file)
|
||||
warnf(global, "Failed to open %s: %s", config->etag_compare_file,
|
||||
strerror(errno));
|
||||
|
||||
if((PARAM_OK == file2string(&etag_from_file, file)) &&
|
||||
etag_from_file) {
|
||||
header = aprintf("If-None-Match: %s", etag_from_file);
|
||||
tool_safefree(etag_from_file);
|
||||
}
|
||||
else
|
||||
header = aprintf("If-None-Match: \"\"");
|
||||
|
||||
if(!header) {
|
||||
if(file)
|
||||
fclose(file);
|
||||
errorf(global,
|
||||
"Failed to allocate memory for custom etag header");
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
result = etag_compare(global, config);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
/* add Etag from file to list of custom headers */
|
||||
pe = add2list(&config->headers, header);
|
||||
tool_safefree(header);
|
||||
|
||||
if(file)
|
||||
fclose(file);
|
||||
if(pe != PARAM_OK) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(config->etag_save_file) {
|
||||
if(config->create_dirs) {
|
||||
result = create_dir_hierarchy(config->etag_save_file, global);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
/* open file for output: */
|
||||
if(strcmp(config->etag_save_file, "-")) {
|
||||
FILE *newfile = fopen(config->etag_save_file, "ab");
|
||||
if(!newfile) {
|
||||
warnf(global, "Failed creating file for saving etags: \"%s\". "
|
||||
"Skip this transfer", config->etag_save_file);
|
||||
tool_safefree(state->outfiles);
|
||||
glob_cleanup(&state->urls);
|
||||
return CURLE_OK;
|
||||
}
|
||||
else {
|
||||
etag_save->filename = config->etag_save_file;
|
||||
etag_save->s_isreg = TRUE;
|
||||
etag_save->fopened = TRUE;
|
||||
etag_save->stream = newfile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* always use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(etag_save->stream);
|
||||
}
|
||||
bool badetag = FALSE;
|
||||
result = etag_store(global, config, etag_save, &badetag);
|
||||
if(result || badetag)
|
||||
break;
|
||||
}
|
||||
|
||||
curl = curl_easy_init();
|
||||
|
@ -1037,55 +1257,10 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||
|
||||
/* Single header file for all URLs */
|
||||
if(config->headerfile) {
|
||||
/* open file for output: */
|
||||
if(!strcmp(config->headerfile, "%")) {
|
||||
heads->stream = stderr;
|
||||
/* use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(heads->stream);
|
||||
}
|
||||
else if(strcmp(config->headerfile, "-")) {
|
||||
FILE *newfile;
|
||||
|
||||
/*
|
||||
* Since every transfer has its own file handle for dumping
|
||||
* the headers, we need to open it in append mode, since transfers
|
||||
* might finish in any order.
|
||||
* The first transfer just clears the file.
|
||||
*
|
||||
* Consider placing the file handle inside the OperationConfig, so
|
||||
* that it does not need to be opened/closed for every transfer.
|
||||
*/
|
||||
if(config->create_dirs) {
|
||||
result = create_dir_hierarchy(config->headerfile, global);
|
||||
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
if(!per->prev || per->prev->config != config) {
|
||||
newfile = fopen(config->headerfile, "wb");
|
||||
if(newfile)
|
||||
fclose(newfile);
|
||||
}
|
||||
newfile = fopen(config->headerfile, "ab");
|
||||
|
||||
if(!newfile) {
|
||||
errorf(global, "Failed to open %s", config->headerfile);
|
||||
result = CURLE_WRITE_ERROR;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
heads->filename = config->headerfile;
|
||||
heads->s_isreg = TRUE;
|
||||
heads->fopened = TRUE;
|
||||
heads->stream = newfile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* always use binary mode for protocol header output */
|
||||
CURL_SET_BINMODE(heads->stream);
|
||||
}
|
||||
result = setup_headerfile(global, config, per, heads);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
hdrcbdata = &per->hdrcbdata;
|
||||
|
||||
outs = &per->outs;
|
||||
|
@ -1124,155 +1299,23 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||
|
||||
if((urlnode->useremote ||
|
||||
(per->outfile && strcmp("-", per->outfile)))) {
|
||||
|
||||
/*
|
||||
* We have specified a filename to store the result in, or we have
|
||||
* decided we want to use the remote filename.
|
||||
*/
|
||||
|
||||
if(!per->outfile) {
|
||||
/* extract the filename from the URL */
|
||||
result = get_url_file_name(global, &per->outfile, per->url);
|
||||
if(result) {
|
||||
errorf(global, "Failed to extract a filename"
|
||||
" from the URL to use for storage");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(state->urls) {
|
||||
/* fill '#1' ... '#9' terms from URL pattern */
|
||||
char *storefile = per->outfile;
|
||||
result = glob_match_url(&per->outfile, storefile, state->urls);
|
||||
tool_safefree(storefile);
|
||||
if(result) {
|
||||
/* bad globbing */
|
||||
warnf(global, "bad output glob");
|
||||
break;
|
||||
}
|
||||
if(!*per->outfile) {
|
||||
warnf(global, "output glob produces empty string");
|
||||
result = CURLE_WRITE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUGASSERT(per->outfile);
|
||||
|
||||
if(config->output_dir && *config->output_dir) {
|
||||
char *d = aprintf("%s/%s", config->output_dir, per->outfile);
|
||||
if(!d) {
|
||||
result = CURLE_WRITE_ERROR;
|
||||
break;
|
||||
}
|
||||
free(per->outfile);
|
||||
per->outfile = d;
|
||||
}
|
||||
/* Create the directory hierarchy, if not pre-existent to a multiple
|
||||
file output call */
|
||||
|
||||
if(config->create_dirs) {
|
||||
result = create_dir_hierarchy(per->outfile, global);
|
||||
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
if(config->skip_existing) {
|
||||
struct_stat fileinfo;
|
||||
if(!stat(per->outfile, &fileinfo)) {
|
||||
/* file is present */
|
||||
notef(global, "skips transfer, \"%s\" exists locally",
|
||||
per->outfile);
|
||||
per->skip = TRUE;
|
||||
*skipped = TRUE;
|
||||
}
|
||||
}
|
||||
if(urlnode->useremote && config->content_disposition) {
|
||||
/* Our header callback MIGHT set the filename */
|
||||
DEBUGASSERT(!outs->filename);
|
||||
}
|
||||
|
||||
if(config->resume_from_current) {
|
||||
/* We are told to continue from where we are now. Get the size
|
||||
of the file as it is now and open it for append instead */
|
||||
struct_stat fileinfo;
|
||||
/* VMS -- Danger, the filesize is only valid for stream files */
|
||||
if(0 == stat(per->outfile, &fileinfo))
|
||||
/* set offset to current file size: */
|
||||
config->resume_from = fileinfo.st_size;
|
||||
else
|
||||
/* let offset be 0 */
|
||||
config->resume_from = 0;
|
||||
}
|
||||
|
||||
if(config->resume_from) {
|
||||
#ifdef __VMS
|
||||
/* open file for output, forcing VMS output format into stream
|
||||
mode which is needed for stat() call above to always work. */
|
||||
FILE *file = fopen(outfile, "ab",
|
||||
"ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
|
||||
#else
|
||||
/* open file for output: */
|
||||
FILE *file = fopen(per->outfile, "ab");
|
||||
#endif
|
||||
if(!file) {
|
||||
errorf(global, "cannot open '%s'", per->outfile);
|
||||
result = CURLE_WRITE_ERROR;
|
||||
break;
|
||||
}
|
||||
outs->fopened = TRUE;
|
||||
outs->stream = file;
|
||||
outs->init = config->resume_from;
|
||||
}
|
||||
else {
|
||||
outs->stream = NULL; /* open when needed */
|
||||
}
|
||||
outs->filename = per->outfile;
|
||||
outs->s_isreg = TRUE;
|
||||
}
|
||||
|
||||
if(per->uploadfile && !stdin_upload(per->uploadfile)) {
|
||||
/*
|
||||
* We have specified a file to upload and it is not "-".
|
||||
*/
|
||||
result = add_file_name_to_url(per->curl, &per->url,
|
||||
per->uploadfile);
|
||||
result = setup_outfile(global, config, per, outs, skipped);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
else if(per->uploadfile && stdin_upload(per->uploadfile)) {
|
||||
/* count to see if there are more than one auth bit set
|
||||
in the authtype field */
|
||||
int authbits = 0;
|
||||
int bitcheck = 0;
|
||||
while(bitcheck < 32) {
|
||||
if(config->authtype & (1UL << bitcheck++)) {
|
||||
authbits++;
|
||||
if(authbits > 1) {
|
||||
/* more than one, we are done! */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user has also selected --anyauth or --proxy-anyauth
|
||||
* we should warn them.
|
||||
*/
|
||||
if(config->proxyanyauth || (authbits > 1)) {
|
||||
warnf(global,
|
||||
"Using --anyauth or --proxy-anyauth with upload from stdin"
|
||||
" involves a big risk of it not working. Use a temporary"
|
||||
" file or a fixed auth type instead");
|
||||
}
|
||||
if(per->uploadfile) {
|
||||
|
||||
DEBUGASSERT(per->infdopen == FALSE);
|
||||
DEBUGASSERT(per->infd == STDIN_FILENO);
|
||||
|
||||
CURL_SET_BINMODE(stdin);
|
||||
if(!strcmp(per->uploadfile, ".")) {
|
||||
if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
|
||||
warnf(global,
|
||||
"fcntl failed on fd=%d: %s", per->infd, strerror(errno));
|
||||
if(stdin_upload(per->uploadfile))
|
||||
check_stdin_upload(global, config, per);
|
||||
else {
|
||||
/*
|
||||
* We have specified a file to upload and it is not "-".
|
||||
*/
|
||||
result = add_file_name_to_url(per->curl, &per->url,
|
||||
per->uploadfile);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user