aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaros Priputen <maros.priputen@student.tuke.sk>2019-10-30 09:43:14 +0100
committerDaniel Stenberg <daniel@haxx.se>2019-11-28 13:05:20 +0100
commit18e5cb77e986911063da8ab6bf254d632b2de6ea (patch)
treee86358f792c99440d2cb2eebe3a8518514816cc1 /src
parent1ff63fa69baf617eee856ea30db7ae23134e46fd (diff)
curl: two new command line options for etags
--etag-compare and --etag-save Suggested-by: Paul Hoffman Fixes #4277 Closes #4543
Diffstat (limited to 'src')
-rw-r--r--src/tool_cb_hdr.c54
-rw-r--r--src/tool_cb_hdr.h3
-rw-r--r--src/tool_cfgable.c2
-rw-r--r--src/tool_cfgable.h2
-rw-r--r--src/tool_getparam.c10
-rw-r--r--src/tool_help.c4
-rw-r--r--src/tool_operate.c101
-rw-r--r--src/tool_operate.h1
8 files changed, 176 insertions, 1 deletions
diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c
index b0880f186..77224adba 100644
--- a/src/tool_cb_hdr.c
+++ b/src/tool_cb_hdr.c
@@ -59,6 +59,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
struct HdrCbData *hdrcbdata = &per->hdrcbdata;
struct OutStruct *outs = &per->outs;
struct OutStruct *heads = &per->heads;
+ struct OutStruct *etag_save = &per->etag_save;
const char *str = ptr;
const size_t cb = size * nmemb;
const char *end = (char *)ptr + cb;
@@ -96,6 +97,59 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
}
/*
+ * Write etag to file when --etag-save option is given.
+ * etag string that we want is enveloped in double quotes
+ */
+ if(etag_save->config->etag_save_file && etag_save->stream) {
+ /* match only header that start with etag (case insensitive) */
+ if(curl_strnequal(str, "etag:", 5)) {
+ char *etag_h = NULL;
+ char *first = NULL;
+ char *last = NULL;
+ size_t etag_length = 0;
+
+ etag_h = ptr;
+ /* point to first occurence of double quote */
+ first = memchr(etag_h, '\"', cb);
+
+ /*
+ * if server side messed with the etag header and doesn't include
+ * double quotes around the etag, kindly exit with a warning
+ */
+
+ if(!first) {
+ warnf(
+ etag_save->config->global,
+ "\nReceived header etag is missing double quote/s\n");
+ return 1;
+ }
+ else {
+ /* discard first double quote */
+ first++;
+ }
+
+ /* point to last occurence of double quote */
+ last = memchr(first, '\"', cb);
+
+ if(!last) {
+ warnf(
+ etag_save->config->global,
+ "\nReceived header etag is missing double quote/s\n");
+ return 1;
+ }
+
+ /* get length of desired etag */
+ etag_length = (size_t)last - (size_t)first;
+
+ fwrite(first, size, etag_length, etag_save->stream);
+ /* terminate with new line */
+ fputc('\n', etag_save->stream);
+ }
+
+ (void)fflush(etag_save->stream);
+ }
+
+ /*
* This callback sets the filename where output shall be written when
* curl options --remote-name (-O) and --remote-header-name (-J) have
* been simultaneously given and additionally server returns an HTTP
diff --git a/src/tool_cb_hdr.h b/src/tool_cb_hdr.h
index cf544dfcb..ec5772f55 100644
--- a/src/tool_cb_hdr.h
+++ b/src/tool_cb_hdr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -43,6 +43,7 @@ struct HdrCbData {
struct OperationConfig *config;
struct OutStruct *outs;
struct OutStruct *heads;
+ struct OutStruct *etag_save;
bool honor_cd_filename;
};
diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c
index efa8c50b2..f802a5a31 100644
--- a/src/tool_cfgable.c
+++ b/src/tool_cfgable.c
@@ -128,6 +128,8 @@ static void free_config_fields(struct OperationConfig *config)
Curl_safefree(config->pubkey);
Curl_safefree(config->hostpubmd5);
Curl_safefree(config->engine);
+ Curl_safefree(config->etag_save_file);
+ Curl_safefree(config->etag_compare_file);
Curl_safefree(config->request_target);
Curl_safefree(config->customrequest);
Curl_safefree(config->krblevel);
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 4372cc6fc..32e811eaa 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -156,6 +156,8 @@ struct OperationConfig {
char *pubkey;
char *hostpubmd5;
char *engine;
+ char *etag_save_file;
+ char *etag_compare_file;
bool crlf;
char *customrequest;
char *krblevel;
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 75faff34d..3efc23e1e 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -268,6 +268,8 @@ static const struct LongShort aliases[]= {
{"E9", "proxy-tlsv1", ARG_NONE},
{"EA", "socks5-basic", ARG_BOOL},
{"EB", "socks5-gssapi", ARG_BOOL},
+ {"EC", "etag-save", ARG_FILENAME},
+ {"ED", "etag-compare", ARG_FILENAME},
{"f", "fail", ARG_BOOL},
{"fa", "fail-early", ARG_BOOL},
{"fb", "styled-output", ARG_BOOL},
@@ -1697,6 +1699,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->socks5_auth &= ~CURLAUTH_GSSAPI;
break;
+ case 'C':
+ GetStr(&config->etag_save_file, nextarg);
+ break;
+
+ case 'D':
+ GetStr(&config->etag_compare_file, nextarg);
+ break;
+
default: /* unknown flag */
return PARAM_OPTION_UNKNOWN;
}
diff --git a/src/tool_help.c b/src/tool_help.c
index 21900108b..8d3f34547 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -131,6 +131,10 @@ static const struct helptxt helptext[] = {
"EGD socket path for random data"},
{" --engine <name>",
"Crypto engine to use"},
+ {" --etag-save <file>",
+ "Get an ETag from response header and save it to a FILE"},
+ {" --etag-compare <file>",
+ "Get an ETag from a file and send a conditional request"},
{" --expect100-timeout <seconds>",
"How long to wait for 100-continue"},
{"-f, --fail",
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 4b2ffb55b..23971f112 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -644,6 +644,12 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
if(per->heads.alloc_filename)
Curl_safefree(per->heads.filename);
+ if(per->etag_save.fopened && per->etag_save.stream)
+ fclose(per->etag_save.stream);
+
+ if(per->etag_save.alloc_filename)
+ Curl_safefree(per->etag_save.filename);
+
curl_easy_cleanup(per->curl);
if(outs->alloc_filename)
free(outs->filename);
@@ -834,6 +840,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
struct OutStruct *outs;
struct InStruct *input;
struct OutStruct *heads;
+ struct OutStruct *etag_save;
struct HdrCbData *hdrcbdata = NULL;
CURL *curl = curl_easy_init();
result = add_per_transfer(&per);
@@ -882,6 +889,99 @@ static CURLcode single_transfer(struct GlobalConfig *global,
}
}
+ /* disallowing simultaneous use of --etag-save and --etag-compare */
+ if(config->etag_save_file && config->etag_compare_file) {
+ warnf(
+ config->global,
+ "Cannot use --etag-save and --etag-compare at the same time\n");
+
+ result = CURLE_UNKNOWN_OPTION;
+ break;
+ }
+
+ /* --etag-save */
+ etag_save = &per->etag_save;
+ etag_save->stream = stdout;
+ etag_save->config = config;
+ if(config->etag_save_file) {
+ /* open file for output: */
+ if(strcmp(config->etag_save_file, "-")) {
+ FILE *newfile = fopen(config->etag_save_file, "wb");
+ if(!newfile) {
+ warnf(
+ config->global,
+ "Failed to open %s\n", config->etag_save_file);
+
+ result = CURLE_WRITE_ERROR;
+ break;
+ }
+ 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 */
+ set_binmode(etag_save->stream);
+ }
+ }
+
+ /* --etag-compare */
+ if(config->etag_compare_file) {
+ char *etag_from_file = NULL;
+ char *header = NULL;
+ size_t file_size = 0;
+
+ /* open file for reading: */
+ FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
+ if(!file) {
+ warnf(
+ config->global,
+ "Failed to open %s\n", config->etag_compare_file);
+
+ result = CURLE_READ_ERROR;
+ break;
+ }
+
+ /* get file size */
+ fseek(file, 0, SEEK_END);
+ file_size = ftell(file);
+
+ /*
+ * check if file is empty, if it's not load etag
+ * else continue with empty etag
+ */
+ if(file_size != 0) {
+ fseek(file, 0, SEEK_SET);
+ file2string(&etag_from_file, file);
+
+ header = aprintf("If-None-Match: \"%s\"", etag_from_file);
+ }
+ else {
+ header = aprintf("If-None-Match: \"\"");
+ }
+
+ if(!header) {
+ warnf(
+ config->global,
+ "Failed to allocate memory for custom etag header\n");
+
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ /* add Etag from file to list of custom headers */
+ add2list(&config->headers, header);
+
+ Curl_safefree(header);
+ Curl_safefree(etag_from_file);
+
+ if(file) {
+ fclose(file);
+ }
+ }
hdrcbdata = &per->hdrcbdata;
@@ -1769,6 +1869,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
hdrcbdata->outs = outs;
hdrcbdata->heads = heads;
+ hdrcbdata->etag_save = etag_save;
hdrcbdata->global = global;
hdrcbdata->config = config;
diff --git a/src/tool_operate.h b/src/tool_operate.h
index 7223db767..39227c0f3 100644
--- a/src/tool_operate.h
+++ b/src/tool_operate.h
@@ -48,6 +48,7 @@ struct per_transfer {
struct ProgressData progressbar;
struct OutStruct outs;
struct OutStruct heads;
+ struct OutStruct etag_save;
struct InStruct input;
struct HdrCbData hdrcbdata;
char errorbuffer[CURL_ERROR_SIZE];