tool: remove protocol count limitation

Replace bit mask protocol sets by null-terminated arrays of protocol
tokens. These are the addresses of the protocol names returned by
curl_version_info().

Protocol names are sorted case-insensitively before output to satisfy CI
tests matches consistency.

The protocol list returned by curl_version_info() is augmented with all
RTMP protocol variants.

Test 1401 adjusted for new alpha ordered output.

Closes #9546
This commit is contained in:
Patrick Monnerat 2022-09-21 10:41:22 +02:00 committed by Daniel Stenberg
parent fb11e45f9c
commit 677266c769
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
12 changed files with 224 additions and 240 deletions

View File

@ -338,6 +338,11 @@ static const char * const protocols[] = {
#endif #endif
#ifdef USE_LIBRTMP #ifdef USE_LIBRTMP
"rtmp", "rtmp",
"rtmpe",
"rtmps",
"rtmpt",
"rtmpte",
"rtmpts",
#endif #endif
#ifndef CURL_DISABLE_RTSP #ifndef CURL_DISABLE_RTSP
"rtsp", "rtsp",

View File

@ -75,8 +75,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
const char *str = ptr; const char *str = ptr;
const size_t cb = size * nmemb; const size_t cb = size * nmemb;
const char *end = (char *)ptr + cb; const char *end = (char *)ptr + cb;
char *scheme; const char *scheme = NULL;
proto_t protocol = proto_last;
/* /*
* Once that libcurl has called back tool_header_cb() the returned value * Once that libcurl has called back tool_header_cb() the returned value
@ -142,11 +141,10 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
*/ */
curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme); curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme);
if(scheme) scheme = proto_token(scheme);
protocol = scheme2protocol(scheme);
if(hdrcbdata->honor_cd_filename && if(hdrcbdata->honor_cd_filename &&
(cb > 20) && checkprefix("Content-disposition:", str) && (cb > 20) && checkprefix("Content-disposition:", str) &&
(protocol == proto_https || protocol == proto_http)) { (scheme == proto_http || scheme == proto_https)) {
const char *p = str + 20; const char *p = str + 20;
/* look for the 'filename=' parameter /* look for the 'filename=' parameter
@ -206,8 +204,8 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
per->was_last_header_empty = TRUE; per->was_last_header_empty = TRUE;
} }
if(hdrcbdata->config->show_headers && if(hdrcbdata->config->show_headers &&
(protocol == proto_http || protocol == proto_https || (scheme == proto_http || scheme == proto_https ||
protocol == proto_rtsp || protocol == proto_file)) { scheme == proto_rtsp || scheme == proto_file)) {
/* bold headers only for selected protocols */ /* bold headers only for selected protocols */
char *value = NULL; char *value = NULL;

View File

@ -578,6 +578,15 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
#ifdef HAVE_WRITABLE_ARGV #ifdef HAVE_WRITABLE_ARGV
argv_item_t clearthis = NULL; argv_item_t clearthis = NULL;
#endif #endif
static const char *redir_protos[] = {
"http",
"https",
"ftp",
"ftps",
NULL
};
*usedarg = FALSE; /* default is that we don't use the arg */ *usedarg = FALSE; /* default is that we don't use the arg */
if(('-' != flag[0]) || ('-' == flag[1])) { if(('-' != flag[0]) || ('-' == flag[1])) {
@ -1209,15 +1218,13 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
break; break;
case 'D': /* --proto */ case 'D': /* --proto */
config->proto_present = TRUE; config->proto_present = TRUE;
err = proto2num(config, PROTO_ALL, &config->proto_str, nextarg); err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
if(err) if(err)
return err; return err;
break; break;
case 'E': /* --proto-redir */ case 'E': /* --proto-redir */
config->proto_redir_present = TRUE; config->proto_redir_present = TRUE;
if(proto2num(config, PROTO_BIT(proto_http) | PROTO_BIT(proto_https) | if(proto2num(config, redir_protos, &config->proto_redir_str, nextarg))
PROTO_BIT(proto_ftp) | PROTO_BIT(proto_ftps),
&config->proto_redir_str, nextarg))
return PARAM_BAD_USE; return PARAM_BAD_USE;
break; break;
case 'F': /* --resolve */ case 'F': /* --resolve */

View File

@ -22,9 +22,6 @@
* *
***************************************************************************/ ***************************************************************************/
#include "tool_setup.h" #include "tool_setup.h"
#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H)
#include <strings.h>
#endif
#define ENABLE_CURLX_PRINTF #define ENABLE_CURLX_PRINTF
/* use our own printf() functions */ /* use our own printf() functions */
#include "curlx.h" #include "curlx.h"
@ -32,6 +29,7 @@
#include "tool_panykey.h" #include "tool_panykey.h"
#include "tool_help.h" #include "tool_help.h"
#include "tool_libinfo.h" #include "tool_libinfo.h"
#include "tool_util.h"
#include "tool_version.h" #include "tool_version.h"
#include "memdebug.h" /* keep this as LAST include */ #include "memdebug.h" /* keep this as LAST include */
@ -190,22 +188,6 @@ void tool_help(char *category)
free(category); free(category);
} }
static int
featcomp(const void *p1, const void *p2)
{
/* The arguments to this function are "pointers to pointers to char", but
the comparison arguments are "pointers to char", hence the following cast
plus dereference */
#ifdef HAVE_STRCASECMP
return strcasecmp(* (char * const *) p1, * (char * const *) p2);
#elif defined(HAVE_STRCMPI)
return strcmpi(* (char * const *) p1, * (char * const *) p2);
#elif defined(HAVE_STRICMP)
return stricmp(* (char * const *) p1, * (char * const *) p2);
#else
return strcmp(* (char * const *) p1, * (char * const *) p2);
#endif
}
void tool_version_info(void) void tool_version_info(void)
{ {
@ -237,7 +219,7 @@ void tool_version_info(void)
if(curlinfo->features & feats[i].bitmask) if(curlinfo->features & feats[i].bitmask)
featp[numfeat++] = (char *)feats[i].name; featp[numfeat++] = (char *)feats[i].name;
} }
qsort(&featp[0], numfeat, sizeof(char *), featcomp); qsort(&featp[0], numfeat, sizeof(char *), struplocompare4sort);
for(i = 0; i< numfeat; i++) for(i = 0; i< numfeat; i++)
printf(" %s", featp[i]); printf(" %s", featp[i]);
puts(""); /* newline */ puts(""); /* newline */

View File

@ -35,121 +35,43 @@
/* global variable definitions, for libcurl run-time info */ /* global variable definitions, for libcurl run-time info */
#define MAX_PROTOS 64 /* Maximum number of supported protocols. */ static const char *no_protos = NULL;
curl_version_info_data *curlinfo = NULL; curl_version_info_data *curlinfo = NULL;
const char * const *built_in_protos = &no_protos;
proto_t proto_last = 0; size_t proto_count = 0;
proto_t proto_ftp = PROTO_NONE; const char *proto_file = NULL;
proto_t proto_ftps = PROTO_NONE; const char *proto_ftp = NULL;
proto_t proto_http = PROTO_NONE; const char *proto_ftps = NULL;
proto_t proto_https = PROTO_NONE; const char *proto_http = NULL;
proto_t proto_file = PROTO_NONE; const char *proto_https = NULL;
proto_t proto_rtsp = PROTO_NONE; const char *proto_rtsp = NULL;
proto_t proto_scp = PROTO_NONE; const char *proto_scp = NULL;
proto_t proto_sftp = PROTO_NONE; const char *proto_sftp = NULL;
proto_t proto_tftp = PROTO_NONE; const char *proto_tftp = NULL;
static struct proto_name_nump { static struct proto_name_tokenp {
const char *proto_name; const char *proto_name;
proto_t *proto_nump; const char **proto_tokenp;
} const possibly_built_in[] = { } const possibly_built_in[] = {
/* Keep entries in CURLPROTO_* order for sorting purpose. */ { "file", &proto_file },
{ "http", &proto_http },
{ "https", &proto_https },
{ "ftp", &proto_ftp }, { "ftp", &proto_ftp },
{ "ftps", &proto_ftps }, { "ftps", &proto_ftps },
{ "http", &proto_http },
{ "https", &proto_https },
{ "rtsp", &proto_rtsp },
{ "scp", &proto_scp }, { "scp", &proto_scp },
{ "sftp", &proto_sftp }, { "sftp", &proto_sftp },
{ "telnet", NULL },
{ "ldap", NULL },
{ "ldaps", NULL },
{ "dict", NULL },
{ "file", &proto_file },
{ "tftp", &proto_tftp }, { "tftp", &proto_tftp },
{ "imap", NULL },
{ "imaps", NULL },
{ "pop3", NULL },
{ "pop3s", NULL },
{ "smtp", NULL },
{ "smtps", NULL },
{ "rtsp", &proto_rtsp },
{ "rtmp", NULL },
{ "rtmpt", NULL },
{ "rtmpe", NULL },
{ "rtmpte", NULL },
{ "rtmps", NULL },
{ "rtmpts", NULL },
{ "gopher", NULL },
{ "smb", NULL },
{ "smbs", NULL },
{ "mqtt", NULL },
{ "gophers", NULL },
{ "ws", NULL },
{ "wss", NULL },
{ NULL, NULL } { NULL, NULL }
}; };
static const char *built_in_protos[MAX_PROTOS + 1] = {NULL};
/*
* scheme2protocol() returns the protocol number for the specified URL scheme
*/
proto_t scheme2protocol(const char *scheme)
{
proto_t p;
for(p = 0; built_in_protos[p]; p++)
if(curl_strequal(scheme, built_in_protos[p]))
return p;
return PROTO_NONE;
}
/*
* protocol2scheme() returns the name of the specified protocol.
*/
const char *protocol2scheme(proto_t proto)
{
return proto < proto_last? built_in_protos[proto]: NULL;
}
/* Enter a prototype in the built-in prototype table. */
static CURLcode enter_proto(const char *proto)
{
if(scheme2protocol(proto) == PROTO_NONE) {
if(proto_last >= MAX_PROTOS)
return CURLE_OUT_OF_MEMORY;
built_in_protos[proto_last] = proto;
built_in_protos[++proto_last] = NULL;
}
return CURLE_OK;
}
/* qsort helper functions for prototype array. */
static int sortkey(const void *arg)
{
const char *proto = *(const char **) arg;
const struct proto_name_nump *p;
for(p = possibly_built_in; p->proto_name; p++)
if(curl_strequal(p->proto_name, proto))
break;
return (int) (p - possibly_built_in);
}
static int protocmp(const void *p1, const void *p2)
{
return sortkey(p1) - sortkey(p2);
}
/* /*
* libcurl_info_init: retrieves run-time information about libcurl, * libcurl_info_init: retrieves run-time information about libcurl,
* setting a global pointer 'curlinfo' to libcurl's run-time info * setting a global pointer 'curlinfo' to libcurl's run-time info
* struct, Assigning numbers to specific protocols and identifying protocols * struct, count protocols and flag those we are interested in.
* we are interested in.
*/ */
CURLcode get_libcurl_info(void) CURLcode get_libcurl_info(void)
@ -163,40 +85,40 @@ CURLcode get_libcurl_info(void)
if(curlinfo->protocols) { if(curlinfo->protocols) {
const char *const *builtin; const char *const *builtin;
const struct proto_name_nump *p; const struct proto_name_tokenp *p;
/* Copy protocols to local table. */ built_in_protos = curlinfo->protocols;
for(builtin = curlinfo->protocols; !result && *builtin; builtin++)
result = enter_proto(*builtin);
/* Special case: if RTMP is present, also include RTMPE, RTMPS, RTMPT,
RTMPTE and RTMPTS. */
if(scheme2protocol("rtmp") != PROTO_NONE) {
if(!result)
result = enter_proto("rtmpe");
if(!result)
result = enter_proto("rtmps");
if(!result)
result = enter_proto("rtmpt");
if(!result)
result = enter_proto("rtmpte");
if(!result)
result = enter_proto("rtmpts");
}
if(result)
return result;
/* Sort the protocols to be sure the primary ones are always accessible
* and to retain their list order for testing purposes. */
qsort((char *)built_in_protos, proto_last,
sizeof(built_in_protos[0]), protocmp);
for(builtin = built_in_protos; !result && *builtin; builtin++) {
/* Identify protocols we are interested in. */ /* Identify protocols we are interested in. */
for(p = possibly_built_in; p->proto_name; p++) for(p = possibly_built_in; p->proto_name; p++)
if(p->proto_nump) if(curl_strequal(p->proto_name, *builtin)) {
*p->proto_nump = scheme2protocol(p->proto_name); *p->proto_tokenp = *builtin;
break;
}
}
proto_count = builtin - built_in_protos;
} }
return CURLE_OK; return CURLE_OK;
} }
/* Tokenize a protocol name.
* Return the address of the protocol name listed by the library, or NULL if
* not found.
* Although this may seem useless, this always returns the same address for
* a given protocol and thus allows comparing pointers rather than strings.
* In addition, the returned pointer is not deallocated until the program ends.
*/
const char *proto_token(const char *proto)
{
const char * const *builtin;
if(!proto)
return NULL;
for(builtin = built_in_protos; *builtin; builtin++)
if(curl_strequal(*builtin, proto))
break;
return *builtin;
}

View File

@ -27,38 +27,22 @@
/* global variable declarations, for libcurl run-time info */ /* global variable declarations, for libcurl run-time info */
typedef unsigned int proto_t; /* A protocol number.*/
#define PROTO_NONE ((proto_t) -1)
/* Protocol numbers set type. This should have enough bits for all
* enabled protocols.
*/
typedef unsigned int proto_set_t;
#define PROTO_MAX ((proto_t) (8 * sizeof(proto_set_t)))
#define PROTO_BIT(p) ((p) < PROTO_MAX? (proto_set_t) 1 << (p): \
(proto_set_t) 0)
#define PROTO_ALL (PROTO_BIT(proto_last) - (proto_set_t) 1)
extern curl_version_info_data *curlinfo; extern curl_version_info_data *curlinfo;
extern proto_t proto_last; extern const char * const *built_in_protos;
extern size_t proto_count;
extern proto_t proto_ftp; extern const char *proto_file;
extern proto_t proto_ftps; extern const char *proto_ftp;
extern proto_t proto_http; extern const char *proto_ftps;
extern proto_t proto_https; extern const char *proto_http;
extern proto_t proto_file; extern const char *proto_https;
extern proto_t proto_rtsp; extern const char *proto_rtsp;
extern proto_t proto_scp; extern const char *proto_scp;
extern proto_t proto_sftp; extern const char *proto_sftp;
extern proto_t proto_tftp; extern const char *proto_tftp;
CURLcode get_libcurl_info(void); CURLcode get_libcurl_info(void);
proto_t scheme2protocol(const char *scheme); const char *proto_token(const char *proto);
const char *protocol2scheme(proto_t proto);
#endif /* HEADER_CURL_TOOL_LIBINFO_H */ #endif /* HEADER_CURL_TOOL_LIBINFO_H */

View File

@ -465,12 +465,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
/* If it returned OK. _or_ failonerror was enabled and it /* If it returned OK. _or_ failonerror was enabled and it
returned due to such an error, check for HTTP transient returned due to such an error, check for HTTP transient
errors to retry on. */ errors to retry on. */
char *scheme; const char *scheme;
proto_t protocol = proto_last;
curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
if(scheme) scheme = proto_token(scheme);
protocol = scheme2protocol(scheme); if(scheme == proto_http || scheme == proto_https) {
if(protocol == proto_http || protocol == proto_https) {
/* This was HTTP(S) */ /* This was HTTP(S) */
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
@ -497,17 +495,13 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
} }
} /* if CURLE_OK */ } /* if CURLE_OK */
else if(result) { else if(result) {
char *scheme; const char *scheme;
proto_t protocol = proto_last;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
scheme = proto_token(scheme);
if(scheme) if((scheme == proto_ftp || scheme == proto_ftps) && response / 100 == 4)
protocol = scheme2protocol(scheme);
if((protocol == proto_ftp || protocol == proto_ftps) &&
response / 100 == 4)
/* /*
* This is typically when the FTP server only allows a certain * This is typically when the FTP server only allows a certain
* amount of users and we are not one of them. All 4xx codes * amount of users and we are not one of them. All 4xx codes
@ -693,12 +687,12 @@ static void single_transfer_cleanup(struct OperationConfig *config)
} }
/* /*
* Return the proto bit for the scheme used in the given URL * Return the protocol token for the scheme used in the given URL
*/ */
static proto_t url_proto(char *url) static const char *url_proto(char *url)
{ {
CURLU *uh = curl_url(); CURLU *uh = curl_url();
proto_t proto = PROTO_NONE; const char *proto = NULL;
if(uh) { if(uh) {
if(url) { if(url) {
@ -708,14 +702,14 @@ static proto_t url_proto(char *url)
if(!curl_url_get(uh, CURLUPART_SCHEME, &schemep, if(!curl_url_get(uh, CURLUPART_SCHEME, &schemep,
CURLU_DEFAULT_SCHEME) && CURLU_DEFAULT_SCHEME) &&
schemep) { schemep) {
proto = scheme2protocol(schemep); proto = proto_token(schemep);
curl_free(schemep); curl_free(schemep);
} }
} }
} }
curl_url_cleanup(uh); curl_url_cleanup(uh);
} }
return proto; return proto? proto: "???"; /* Never match if not found. */
} }
/* create the next (singular) transfer */ /* create the next (singular) transfer */
@ -858,7 +852,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
struct OutStruct *etag_save; struct OutStruct *etag_save;
struct HdrCbData *hdrcbdata = NULL; struct HdrCbData *hdrcbdata = NULL;
struct OutStruct etag_first; struct OutStruct etag_first;
proto_t use_proto; const char *use_proto;
CURL *curl; CURL *curl;
/* --etag-save */ /* --etag-save */
@ -1257,10 +1251,8 @@ static CURLcode single_transfer(struct GlobalConfig *global,
/* here */ /* here */
use_proto = url_proto(per->this_url); use_proto = url_proto(per->this_url);
if(use_proto == PROTO_NONE)
use_proto = proto_last; /* Do not match any identified protocol. */
#if 0 #if 0
if(use_proto >= proto_last) { if(!use_proto) {
warnf(global, "URL is '%s' but no support for the scheme\n", warnf(global, "URL is '%s' but no support for the scheme\n",
per->this_url); per->this_url);
} }
@ -1416,12 +1408,12 @@ static CURLcode single_transfer(struct GlobalConfig *global,
my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers); my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
if(proto_http < proto_last || proto_rtsp < proto_last) { if(proto_http || proto_rtsp) {
my_setopt_str(curl, CURLOPT_REFERER, config->referer); my_setopt_str(curl, CURLOPT_REFERER, config->referer);
my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent); my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
} }
if(proto_http < proto_last) { if(proto_http) {
long postRedir = 0; long postRedir = 0;
my_setopt(curl, CURLOPT_FOLLOWLOCATION, my_setopt(curl, CURLOPT_FOLLOWLOCATION,
@ -1471,9 +1463,9 @@ static CURLcode single_transfer(struct GlobalConfig *global,
return result; return result;
} }
} /* (proto_http < proto_last) */ } /* (proto_http) */
if(proto_ftp < proto_last) if(proto_ftp)
my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
config->low_speed_limit); config->low_speed_limit);
@ -1972,7 +1964,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L); my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L);
/* curl 7.15.1 */ /* curl 7.15.1 */
if(proto_ftp < proto_last) if(proto_ftp)
my_setopt(curl, CURLOPT_FTP_FILEMETHOD, my_setopt(curl, CURLOPT_FTP_FILEMETHOD,
(long)config->ftp_filemethod); (long)config->ftp_filemethod);
@ -2009,7 +2001,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L); my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
/* curl 7.20.0 */ /* curl 7.20.0 */
if(config->tftp_blksize && proto_tftp < proto_last) if(config->tftp_blksize && proto_tftp)
my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize); my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
if(config->mail_from) if(config->mail_from)
@ -2122,7 +2114,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
(long)(config->expect100timeout*1000)); (long)(config->expect100timeout*1000));
/* new in 7.48.0 */ /* new in 7.48.0 */
if(config->tftp_no_options && proto_tftp < proto_last) if(config->tftp_no_options && proto_tftp)
my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L); my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L);
/* new in 7.59.0 */ /* new in 7.59.0 */

View File

@ -35,6 +35,7 @@
#include "tool_msgs.h" #include "tool_msgs.h"
#include "tool_paramhlp.h" #include "tool_paramhlp.h"
#include "tool_libinfo.h" #include "tool_libinfo.h"
#include "tool_util.h"
#include "tool_version.h" #include "tool_version.h"
#include "dynbuf.h" #include "dynbuf.h"
@ -260,6 +261,51 @@ ParameterError str2udouble(double *valp, const char *str, long max)
return PARAM_OK; return PARAM_OK;
} }
/*
* Implement protocol sets in null-terminated array of protocol name pointers.
*/
/* Return index of prototype token in set, card(set) if not found.
Can be called with proto == NULL to get card(set). */
static size_t protoset_index(const char * const *protoset, const char *proto)
{
const char * const *p = protoset;
for(; *p; p++)
if(proto == *p)
break;
return p - protoset;
}
/* Include protocol token in set. */
static void protoset_set(const char **protoset, const char *proto)
{
if(proto) {
size_t n = protoset_index(protoset, proto);
if(!protoset[n]) {
DEBUGASSERT(n < proto_count);
protoset[n] = proto;
protoset[n + 1] = NULL;
}
}
}
/* Exclude protocol token from set. */
static void protoset_clear(const char **protoset, const char *proto)
{
if(proto) {
size_t n = protoset_index(protoset, proto);
if(protoset[n]) {
size_t m = protoset_index(protoset, NULL) - 1;
protoset[n] = protoset[m];
protoset[m] = NULL;
}
}
}
/* /*
* Parse the string and provide an allocated libcurl compatible protocol * Parse the string and provide an allocated libcurl compatible protocol
* string output. Return non-zero on failure, zero on success. * string output. Return non-zero on failure, zero on success.
@ -274,13 +320,14 @@ ParameterError str2udouble(double *valp, const char *str, long max)
#define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */ #define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */
ParameterError proto2num(struct OperationConfig *config, ParameterError proto2num(struct OperationConfig *config,
proto_set_t val, char **ostr, const char *str) const char * const *val, char **ostr, const char *str)
{ {
char *buffer; char *buffer;
const char *sep = ","; const char *sep = ",";
char *token; char *token;
const char **protoset;
struct curlx_dynbuf obuf; struct curlx_dynbuf obuf;
proto_t proto; size_t proto;
CURLcode result; CURLcode result;
curlx_dyn_init(&obuf, MAX_PROTOSTRING); curlx_dyn_init(&obuf, MAX_PROTOSTRING);
@ -292,6 +339,21 @@ ParameterError proto2num(struct OperationConfig *config,
if(!buffer) if(!buffer)
return PARAM_NO_MEM; return PARAM_NO_MEM;
protoset = malloc((proto_count + 1) * sizeof(*protoset));
if(!protoset) {
free(buffer);
return PARAM_NO_MEM;
}
/* Preset protocol set with default values. */
protoset[0] = NULL;
for(; *val; val++) {
const char *p = proto_token(*val);
if(p)
protoset_set(protoset, p);
}
/* Allow strtok() here since this isn't used threaded */ /* Allow strtok() here since this isn't used threaded */
/* !checksrc! disable BANNEDFUNC 2 */ /* !checksrc! disable BANNEDFUNC 2 */
for(token = strtok(buffer, sep); for(token = strtok(buffer, sep);
@ -312,7 +374,8 @@ ParameterError proto2num(struct OperationConfig *config,
action = allow; action = allow;
break; break;
default: /* Includes case of terminating NULL */ default: /* Includes case of terminating NULL */
Curl_safefree(buffer); free(buffer);
free((char *) protoset);
return PARAM_BAD_USE; return PARAM_BAD_USE;
} }
} }
@ -320,47 +383,49 @@ ParameterError proto2num(struct OperationConfig *config,
if(curl_strequal(token, "all")) { if(curl_strequal(token, "all")) {
switch(action) { switch(action) {
case deny: case deny:
val = 0; protoset[0] = NULL;
break; break;
case allow: case allow:
case set: case set:
val = PROTO_ALL; memcpy((char *) protoset,
built_in_protos, (proto_count + 1) * sizeof(*protoset));
break; break;
} }
} }
else { else {
proto = scheme2protocol(token); const char *p = proto_token(token);
if(proto != PROTO_NONE) {
if(p)
switch(action) { switch(action) {
case deny: case deny:
val &= ~PROTO_BIT(proto); protoset_clear(protoset, p);
break; break;
case set: case set:
val = 0; protoset[0] = NULL;
/* FALLTHROUGH */ /* FALLTHROUGH */
case allow: case allow:
if(proto >= PROTO_MAX) protoset_set(protoset, p);
warnf(config->global, "protocol '%s' enabled but not accessible\n",
token);
val |= PROTO_BIT(proto);
break; break;
} }
}
else { /* unknown protocol */ else { /* unknown protocol */
/* If they have specified only this protocol, we say treat it as /* If they have specified only this protocol, we say treat it as
if no protocols are allowed */ if no protocols are allowed */
if(action == set) if(action == set)
val = 0; protoset[0] = NULL;
warnf(config->global, "unrecognized protocol '%s'\n", token); warnf(config->global, "unrecognized protocol '%s'\n", token);
} }
} }
} }
Curl_safefree(buffer); free(buffer);
/* We need the protocols in alphabetic order for CI tests requirements. */
qsort((char *) protoset, protoset_index(protoset, NULL), sizeof(*protoset),
struplocompare4sort);
result = curlx_dyn_addn(&obuf, "", 0); result = curlx_dyn_addn(&obuf, "", 0);
for(proto = 0; proto < proto_last && proto < PROTO_MAX && !result; proto++) for(proto = 0; protoset[proto] && !result; proto++)
if(val & PROTO_BIT(proto)) result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]);
result = curlx_dyn_addf(&obuf, "%s,", protocol2scheme(proto)); free((char *) protoset);
curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1); curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1);
*ostr = curlx_dyn_ptr(&obuf); *ostr = curlx_dyn_ptr(&obuf);
@ -377,13 +442,10 @@ ParameterError proto2num(struct OperationConfig *config,
*/ */
ParameterError check_protocol(const char *str) ParameterError check_protocol(const char *str)
{ {
proto_t proto;
if(!str) if(!str)
return PARAM_REQUIRES_PARAMETER; return PARAM_REQUIRES_PARAMETER;
proto = scheme2protocol(str); if(proto_token(str))
if(proto < proto_last)
return PARAM_OK; return PARAM_OK;
return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL; return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
} }

View File

@ -39,7 +39,7 @@ ParameterError str2unummax(long *val, const char *str, long max);
ParameterError str2udouble(double *val, const char *str, long max); ParameterError str2udouble(double *val, const char *str, long max);
ParameterError proto2num(struct OperationConfig *config, ParameterError proto2num(struct OperationConfig *config,
proto_set_t val, char **obuf, const char * const *val, char **obuf,
const char *str); const char *str);
ParameterError check_protocol(const char *str); ParameterError check_protocol(const char *str);

View File

@ -23,6 +23,10 @@
***************************************************************************/ ***************************************************************************/
#include "tool_setup.h" #include "tool_setup.h"
#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H)
#include <strings.h>
#endif
#include "tool_util.h" #include "tool_util.h"
#include "memdebug.h" /* keep this as LAST include */ #include "memdebug.h" /* keep this as LAST include */
@ -135,3 +139,27 @@ long tvdiff(struct timeval newer, struct timeval older)
return (long)(newer.tv_sec-older.tv_sec)*1000+ return (long)(newer.tv_sec-older.tv_sec)*1000+
(long)(newer.tv_usec-older.tv_usec)/1000; (long)(newer.tv_usec-older.tv_usec)/1000;
} }
/* Case insensitive compare. Accept NULL pointers. */
int struplocompare(const char *p1, const char *p2)
{
if(!p1)
return p2? -1: 0;
if(!p2)
return 1;
#ifdef HAVE_STRCASECMP
return strcasecmp(p1, p2);
#elif defined(HAVE_STRCMPI)
return strcmpi(p1, p2);
#elif defined(HAVE_STRICMP)
return stricmp(p1, p2);
#else
return strcmp(p1, p2);
#endif
}
/* Indirect version to use as qsort callback. */
int struplocompare4sort(const void *p1, const void *p2)
{
return struplocompare(* (char * const *) p1, * (char * const *) p2);
}

View File

@ -35,4 +35,8 @@ struct timeval tvnow(void);
*/ */
long tvdiff(struct timeval t1, struct timeval t2); long tvdiff(struct timeval t1, struct timeval t2);
/* Case insensitive comparison support. */
int struplocompare(const char *p1, const char *p2);
int struplocompare4sort(const void *p1, const void *p2);
#endif /* HEADER_CURL_TOOL_UTIL_H */ #endif /* HEADER_CURL_TOOL_UTIL_H */

View File

@ -96,7 +96,7 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L); curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
%endif %endif
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(hnd, CURLOPT_PROTOCOLS_STR, "http,ftp,file"); curl_easy_setopt(hnd, CURLOPT_PROTOCOLS_STR, "file,ftp,http");
/* Here is a list of options the curl code used that cannot get generated /* Here is a list of options the curl code used that cannot get generated
as source easily. You may choose to either not use them or implement as source easily. You may choose to either not use them or implement