mirror of
https://github.com/curl/curl
synced 2026-08-25 12:32:35 -04:00
tool_xattr: add support for Windows alternate data stream
Requires a supported filesystem, i.e. NTFS. Example: ```console > curl.exe https://curl.se/index.html --output test.txt --xattr --referer https://curl.se/ > cat < test.txt:Zone.Identifier [ZoneTransfer] HostUrl=https://curl.se/index.html ReferrerUrl=https://curl.se/ ``` Where newlines are CLRF, `ReferrerUrl` is set when using `--referer` option. Also: - test688: make it test `--referer`. - document `user.xdg.referrer.url` in `--xattr` man page. Refs: https://en.wikipedia.org/wiki/Mark_of_the_Web https://en.wikipedia.org/wiki/NTFS#Alternate_data_stream_(ADS) https://learn.microsoft.com/en-us/windows/win32/fileio/file-streams https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c54dec26-1551-4d3a-a0ea-4fa40f848eb3 Ref: #22345 Closes #22354
This commit is contained in:
parent
057b251358
commit
6bff85be68
8 changed files with 107 additions and 22 deletions
1
.github/scripts/pyspelling.words
vendored
1
.github/scripts/pyspelling.words
vendored
|
|
@ -392,6 +392,7 @@ IMAPS
|
|||
imaps
|
||||
impacket
|
||||
implementers
|
||||
INI
|
||||
init
|
||||
initializer
|
||||
inlined
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ Store metadata in the extended file attributes.
|
|||
|
||||
When saving output to a file, tell curl to store file metadata in extended
|
||||
file attributes. Currently, `curl` is stored in the `creator` attribute,
|
||||
the URL is stored in the `xdg.origin.url` attribute and, for HTTP, the content
|
||||
type is stored in the `mime_type` attribute. If the file system does not
|
||||
support extended attributes, a warning is issued.
|
||||
the URL is stored in the `xdg.origin.url` attribute, for HTTP, the content
|
||||
type is stored in the `mime_type` attribute, and if set, the referrer URL in
|
||||
`user.xdg.referrer.url`. If the file system does not support extended
|
||||
attributes, a warning is issued.
|
||||
|
||||
Since curl 8.22.0 this option is also supported on Windows, where it creates
|
||||
an Alternate Data Stream named `Zone.Identifier`. It contains an INI formatted
|
||||
`ZoneTransfer` section, with values: `HostUrl`, `ReferrerUrl` (if set).
|
||||
|
|
|
|||
|
|
@ -650,7 +650,7 @@ static CURLcode post_output_handling(struct per_transfer *per,
|
|||
|
||||
/* Set file extended attributes */
|
||||
if(!result && config->xattr && outs->fopened && outs->stream) {
|
||||
rc = fwrite_xattr(curl, per->url, fileno(outs->stream));
|
||||
rc = fwrite_xattr(curl, per->url, fileno(outs->stream), outs->filename);
|
||||
if(rc) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
warnf("Error setting extended attributes on '%s': %s", outs->filename,
|
||||
|
|
|
|||
|
|
@ -27,19 +27,6 @@
|
|||
|
||||
#ifdef USE_XATTR
|
||||
|
||||
/* mapping table of curl metadata to extended attribute names */
|
||||
static const struct xattr_mapping {
|
||||
const char *attr; /* name of the xattr */
|
||||
CURLINFO info;
|
||||
} mappings[] = {
|
||||
/* mappings proposed by
|
||||
* https://freedesktop.org/wiki/CommonExtendedAttributes/
|
||||
*/
|
||||
{ "user.xdg.referrer.url", CURLINFO_REFERER },
|
||||
{ "user.mime_type", CURLINFO_CONTENT_TYPE },
|
||||
{ NULL, CURLINFO_NONE } /* last element, abort here */
|
||||
};
|
||||
|
||||
/* returns a new URL that needs to be freed */
|
||||
/* @unittest: 1621 */
|
||||
UNITTEST char *stripcredentials(const char *url)
|
||||
|
|
@ -74,6 +61,20 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* mapping table of curl metadata to extended attribute names */
|
||||
static const struct xattr_mapping {
|
||||
const char *attr; /* name of the xattr */
|
||||
CURLINFO info;
|
||||
} mappings[] = {
|
||||
/* mappings proposed by
|
||||
* https://freedesktop.org/wiki/CommonExtendedAttributes/
|
||||
*/
|
||||
{ "user.xdg.referrer.url", CURLINFO_REFERER },
|
||||
{ "user.mime_type", CURLINFO_CONTENT_TYPE },
|
||||
{ NULL, CURLINFO_NONE } /* last element, abort here */
|
||||
};
|
||||
|
||||
static int xattr(int fd,
|
||||
const char *attr, /* name of the xattr */
|
||||
const char *value)
|
||||
|
|
@ -102,13 +103,70 @@ static int xattr(int fd,
|
|||
}
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
static int win32_file_stream(CURL *curl, FILE *fs, const char *url)
|
||||
{
|
||||
int err = 1;
|
||||
char *value = NULL;
|
||||
char *nurl = stripcredentials(url);
|
||||
CURLcode result = curl_easy_getinfo(curl, CURLINFO_REFERER, &value);
|
||||
|
||||
if(nurl && !result) {
|
||||
err = 0;
|
||||
err |= (fputs("[ZoneTransfer]\n", fs) == EOF);
|
||||
if(value) {
|
||||
err |= (fputs("ReferrerUrl=", fs) == EOF);
|
||||
err |= (fputs(value, fs) == EOF);
|
||||
err |= (fputs("\n", fs) == EOF);
|
||||
}
|
||||
err |= (fputs("HostUrl=", fs) == EOF);
|
||||
err |= (fputs(nurl, fs) == EOF);
|
||||
err |= (fputs("\n", fs) == EOF);
|
||||
}
|
||||
curl_free(nurl);
|
||||
return err;
|
||||
}
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
/* store metadata from the curl request alongside the downloaded
|
||||
* file using extended attributes
|
||||
*/
|
||||
int fwrite_xattr(CURL *curl, const char *url, int fd)
|
||||
int fwrite_xattr(CURL *curl, const char *url, int fd, const char *filename)
|
||||
{
|
||||
int err;
|
||||
#ifdef _WIN32
|
||||
char *fn_abs, *fn_stream;
|
||||
FILE *fs;
|
||||
(void)fd;
|
||||
|
||||
/* convert to absolute path to prevent Windows interpreting a 'X:<stream>'
|
||||
filename as 'drive-letter:<filename>'. */
|
||||
fn_abs = _fullpath(NULL, filename, 0);
|
||||
if(!fn_abs)
|
||||
return 1;
|
||||
|
||||
fn_stream = curl_maprintf("%s:%s", fn_abs, "Zone.Identifier");
|
||||
/* !checksrc! disable BANNEDFUNC 1 */
|
||||
free(fn_abs); /* allocated by CRT, use system free() */
|
||||
if(!fn_stream)
|
||||
return 1;
|
||||
|
||||
fs = curlx_fopen(fn_stream, FOPEN_WRITETEXT);
|
||||
curl_free(fn_stream);
|
||||
if(!fs)
|
||||
return 1;
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
if(getenv("CURL_FAKE_XATTR"))
|
||||
win32_file_stream(curl, stdout, url);
|
||||
#endif
|
||||
err = win32_file_stream(curl, fs, url);
|
||||
curlx_fclose(fs);
|
||||
#else
|
||||
int i = 0;
|
||||
int err = xattr(fd, "user.creator", "curl");
|
||||
(void)filename;
|
||||
|
||||
err = xattr(fd, "user.creator", "curl");
|
||||
|
||||
/* loop through all xattr-curlinfo pairs and abort on a set error */
|
||||
while(!err && mappings[i].attr) {
|
||||
|
|
@ -125,6 +183,7 @@ int fwrite_xattr(CURL *curl, const char *url, int fd)
|
|||
err = xattr(fd, "user.xdg.origin.url", nurl);
|
||||
curl_free(nurl);
|
||||
}
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -33,17 +33,19 @@
|
|||
# include <sys/types.h>
|
||||
# include <sys/extattr.h>
|
||||
# define USE_XATTR
|
||||
#elif defined(_WIN32)
|
||||
# define USE_XATTR
|
||||
#endif
|
||||
|
||||
#ifdef USE_XATTR
|
||||
int fwrite_xattr(CURL *curl, const char *url, int fd);
|
||||
int fwrite_xattr(CURL *curl, const char *url, int fd, const char *filename);
|
||||
|
||||
#ifdef UNITTESTS
|
||||
UNITTEST char *stripcredentials(const char *url);
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define fwrite_xattr(a, b, c) 0
|
||||
#define fwrite_xattr(a, b, c, d) 0
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_TOOL_XATTR_H */
|
||||
|
|
|
|||
|
|
@ -76,9 +76,14 @@ Accept: */*
|
|||
|
||||
</protocol>
|
||||
<stdout mode="text">
|
||||
%if win32
|
||||
[ZoneTransfer]
|
||||
HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%else
|
||||
user.creator => curl
|
||||
user.mime_type => text/html
|
||||
user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%endif
|
||||
</stdout>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
|
|
|||
|
|
@ -53,9 +53,14 @@ Accept: */*
|
|||
|
||||
</protocol>
|
||||
<stdout mode="text">
|
||||
%if win32
|
||||
[ZoneTransfer]
|
||||
HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%else
|
||||
user.creator => curl
|
||||
user.mime_type => fake/data
|
||||
user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%endif
|
||||
</stdout>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ CURL_FAKE_XATTR=1
|
|||
basic --xattr with (uppercase) -O
|
||||
</name>
|
||||
<command>
|
||||
--xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
--xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER --referer https://referrer.invalid/
|
||||
</command>
|
||||
</client>
|
||||
|
||||
|
|
@ -50,12 +50,20 @@ GET /%TESTNUMBER HTTP/1.1
|
|||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
Referer: https://referrer.invalid/
|
||||
|
||||
</protocol>
|
||||
<stdout mode="text">
|
||||
%if win32
|
||||
[ZoneTransfer]
|
||||
ReferrerUrl=https://referrer.invalid/
|
||||
HostUrl=http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%else
|
||||
user.creator => curl
|
||||
user.xdg.referrer.url => https://referrer.invalid/
|
||||
user.mime_type => fake/data
|
||||
user.xdg.origin.url => http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
%endif
|
||||
</stdout>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue