#include "tool_setup.h"
#if defined(_WIN32) || defined(MSDOS)
#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
# include <libgen.h>
#endif
#ifdef _WIN32
# include <stdlib.h>
# include <tlhelp32.h>
# include "tool_cfgable.h"
# include "tool_libinfo.h"
#endif
#include "tool_bname.h"
#include "tool_doswin.h"
#include "tool_msgs.h"
#include "memdebug.h"
#ifdef _WIN32
# undef PATH_MAX
# define PATH_MAX MAX_PATH
#elif !defined(__DJGPP__) || (__DJGPP__ < 2)
# define CURL_USE_LFN(f) 0
#elif defined(__DJGPP__)
# include <fcntl.h>
# define CURL_USE_LFN(f) _use_lfn(f)
#endif
#ifdef MSDOS
#ifndef S_ISCHR
# ifdef S_IFCHR
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
# else
# define S_ISCHR(m) (0)
# endif
#endif
static SANITIZEcode truncate_dryrun(const char *path,
const size_t truncate_pos);
static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
int flags);
#endif
static SANITIZEcode rename_if_reserved_dos(char **const sanitized,
const char *file_name,
int flags);
SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
int flags)
{
char *p, *target;
size_t len;
SANITIZEcode sc;
size_t max_sanitized_len;
if(!sanitized)
return SANITIZE_ERR_BAD_ARGUMENT;
*sanitized = NULL;
if(!file_name)
return SANITIZE_ERR_BAD_ARGUMENT;
if(flags & SANITIZE_ALLOW_PATH) {
#ifndef MSDOS
if(file_name[0] == '\\' && file_name[1] == '\\')
max_sanitized_len = 32767-1;
else
#endif
max_sanitized_len = PATH_MAX-1;
}
else
max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
len = strlen(file_name);
if(len > max_sanitized_len)
return SANITIZE_ERR_INVALID_PATH;
target = strdup(file_name);
if(!target)
return SANITIZE_ERR_OUT_OF_MEMORY;
#ifndef MSDOS
if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
p = target + 4;
else
#endif
p = target;
for(; *p; ++p) {
const char *banned;
if((1 <= *p && *p <= 31) ||
(!(flags & SANITIZE_ALLOW_PATH) && *p == ':') ||
(!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
*p = '_';
continue;
}
for(banned = "|<>\"?*"; *banned; ++banned) {
if(*p == *banned) {
*p = '_';
break;
}
}
}
if(!(flags & SANITIZE_ALLOW_PATH) && len) {
char *clip = NULL;
p = &target[len];
do {
--p;
if(*p != ' ' && *p != '.')
break;
clip = p;
} while(p != target);
if(clip) {
*clip = '\0';
}
}
#ifdef MSDOS
sc = msdosify(&p, target, flags);
free(target);
if(sc)
return sc;
target = p;
len = strlen(target);
if(len > max_sanitized_len) {
free(target);
return SANITIZE_ERR_INVALID_PATH;
}
#endif
if(!(flags & SANITIZE_ALLOW_RESERVED)) {
sc = rename_if_reserved_dos(&p, target, flags);
free(target);
if(sc)
return sc;
target = p;
len = strlen(target);
if(len > max_sanitized_len) {
free(target);
return SANITIZE_ERR_INVALID_PATH;
}
}
*sanitized = target;
return SANITIZE_ERR_OK;
}
#ifdef MSDOS
static SANITIZEcode truncate_dryrun(const char *path,
const size_t truncate_pos)
{
size_t len;
if(!path)
return SANITIZE_ERR_BAD_ARGUMENT;
len = strlen(path);
if(truncate_pos > len)
return SANITIZE_ERR_BAD_ARGUMENT;
if(!len || !truncate_pos)
return SANITIZE_ERR_INVALID_PATH;
if(strpbrk(&path[truncate_pos - 1], "\\/:"))
return SANITIZE_ERR_INVALID_PATH;
if(truncate_pos > 1) {
const char *p = &path[truncate_pos - 1];
do {
--p;
if(*p == ':')
return SANITIZE_ERR_INVALID_PATH;
} while(p != path && *p != '\\' && *p != '/');
}
return SANITIZE_ERR_OK;
}
static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
int flags)
{
char dos_name[PATH_MAX];
static const char illegal_chars_dos[] = ".+, ;=[]"
"|<>/\\\":?*";
static const char *illegal_chars_w95 = &illegal_chars_dos[8];
int idx, dot_idx;
const char *s = file_name;
char *d = dos_name;
const char *const dlimit = dos_name + sizeof(dos_name) - 1;
const char *illegal_aliens = illegal_chars_dos;
size_t len = sizeof(illegal_chars_dos) - 1;
if(!sanitized)
return SANITIZE_ERR_BAD_ARGUMENT;
*sanitized = NULL;
if(!file_name)
return SANITIZE_ERR_BAD_ARGUMENT;
if(strlen(file_name) > PATH_MAX-1)
return SANITIZE_ERR_INVALID_PATH;
if(CURL_USE_LFN(file_name)) {
illegal_aliens = illegal_chars_w95;
len -= (illegal_chars_w95 - illegal_chars_dos);
}
if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
*d++ = *s++;
*d = ((flags & SANITIZE_ALLOW_PATH)) ? ':' : '_';
++d; ++s;
}
for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
if(memchr(illegal_aliens, *s, len)) {
if((flags & SANITIZE_ALLOW_PATH) && *s == ':')
*d = ':';
else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
*d = *s;
else if(*s == '.') {
if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
(s[1] == '/' || s[1] == '\\' ||
(s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
*d++ = *s++;
if(d == dlimit)
break;
if(*s == '.') {
*d++ = *s++;
if(d == dlimit)
break;
}
*d = *s;
}
else if(idx == 0)
*d = '_';
else if(dot_idx >= 0) {
if(dot_idx < 5) {
d[dot_idx - idx] = '_';
*d = '.';
}
else
*d = '-';
}
else
*d = '.';
if(*s == '.')
dot_idx = idx;
}
else if(*s == '+' && s[1] == '+') {
if(idx - 2 == dot_idx) {
*d++ = 'x';
if(d == dlimit)
break;
*d = 'x';
}
else {
if(dlimit - d < 4) {
*d++ = 'x';
if(d == dlimit)
break;
*d = 'x';
}
else {
memcpy(d, "plus", 4);
d += 3;
}
}
s++;
idx++;
}
else
*d = '_';
}
else
*d = *s;
if(*s == '/' || *s == '\\') {
idx = 0;
dot_idx = -1;
}
else
idx++;
}
*d = '\0';
if(*s) {
if(strpbrk(s, "\\/:") || truncate_dryrun(dos_name, d - dos_name))
return SANITIZE_ERR_INVALID_PATH;
}
*sanitized = strdup(dos_name);
return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY;
}
#endif
static SANITIZEcode rename_if_reserved_dos(char **const sanitized,
const char *file_name,
int flags)
{
char *p, *base;
char fname[PATH_MAX];
#ifdef MSDOS
struct_stat st_buf;
#endif
size_t len;
if(!sanitized || !file_name)
return SANITIZE_ERR_BAD_ARGUMENT;
*sanitized = NULL;
len = strlen(file_name);
#ifndef MSDOS
if((flags & SANITIZE_ALLOW_PATH) &&
file_name[0] == '\\' && file_name[1] == '\\') {
*sanitized = strdup(file_name);
if(!*sanitized)
return SANITIZE_ERR_OUT_OF_MEMORY;
return SANITIZE_ERR_OK;
}
#endif
if(len > PATH_MAX-1)
return SANITIZE_ERR_INVALID_PATH;
memcpy(fname, file_name, len);
fname[len] = '\0';
base = basename(fname);
for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
size_t p_len;
int x = (curl_strnequal(p, "CON", 3) ||
curl_strnequal(p, "PRN", 3) ||
curl_strnequal(p, "AUX", 3) ||
curl_strnequal(p, "NUL", 3)) ? 3 :
(curl_strnequal(p, "CLOCK$", 6)) ? 6 :
(curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
(('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
if(!x)
continue;
for(; p[x] == ' '; ++x)
;
if(p[x] == '.') {
p[x] = '_';
continue;
}
else if(p[x] == ':') {
if(!(flags & SANITIZE_ALLOW_PATH)) {
p[x] = '_';
continue;
}
++x;
}
else if(p[x])
continue;
p_len = strlen(p);
if(strlen(fname) == PATH_MAX-1)
return SANITIZE_ERR_INVALID_PATH;
memmove(p + 1, p, p_len + 1);
p[0] = '_';
++p_len;
if(p == fname)
base = basename(fname);
}
#ifdef MSDOS
if(base && (curlx_stat(base, &st_buf) == 0) && S_ISCHR(st_buf.st_mode)) {
size_t blen = strlen(base);
if(blen) {
if(strlen(fname) >= PATH_MAX-1)
return SANITIZE_ERR_INVALID_PATH;
memmove(base + 1, base, blen + 1);
base[0] = '_';
}
}
#endif
*sanitized = strdup(fname);
return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY;
}
#ifdef __DJGPP__
char **__crt0_glob_function(char *arg)
{
(void)arg;
return (char **)0;
}
#endif
#ifdef _WIN32
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) && \
!defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE)
CURLcode FindWin32CACert(struct OperationConfig *config,
const TCHAR *bundle_file)
{
CURLcode result = CURLE_OK;
DWORD res_len;
TCHAR buf[PATH_MAX];
TCHAR *ptr = NULL;
buf[0] = TEXT('\0');
res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
if(res_len > 0) {
char *mstr = curlx_convert_tchar_to_UTF8(buf);
tool_safefree(config->cacert);
if(mstr)
config->cacert = strdup(mstr);
curlx_unicodefree(mstr);
if(!config->cacert)
result = CURLE_OUT_OF_MEMORY;
}
return result;
}
#endif
struct curl_slist *GetLoadedModulePaths(void)
{
struct curl_slist *slist = NULL;
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
HANDLE hnd = INVALID_HANDLE_VALUE;
MODULEENTRY32 mod = {0};
mod.dwSize = sizeof(MODULEENTRY32);
do {
hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
} while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
if(hnd == INVALID_HANDLE_VALUE)
goto error;
if(!Module32First(hnd, &mod))
goto error;
do {
char *path;
struct curl_slist *temp;
#ifdef UNICODE
char buffer[sizeof(mod.szExePath) * 2];
if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
buffer, sizeof(buffer), NULL, NULL))
goto error;
path = buffer;
#else
path = mod.szExePath;
#endif
temp = curl_slist_append(slist, path);
if(!temp)
goto error;
slist = temp;
} while(Module32Next(hnd, &mod));
goto cleanup;
error:
curl_slist_free_all(slist);
slist = NULL;
cleanup:
if(hnd != INVALID_HANDLE_VALUE)
CloseHandle(hnd);
#endif
return slist;
}
bool tool_term_has_bold;
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
static struct TerminalSettings {
HANDLE hStdOut;
DWORD dwOutputMode;
LONG valid;
} TerminalSettings;
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
static void restore_terminal(void)
{
if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE))
SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode);
}
static BOOL WINAPI signal_handler(DWORD type)
{
if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
restore_terminal();
return FALSE;
}
static void init_terminal(void)
{
TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE ||
!GetConsoleMode(TerminalSettings.hStdOut,
&TerminalSettings.dwOutputMode) ||
!curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL))
return;
if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
tool_term_has_bold = true;
else {
(void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE);
if(SetConsoleCtrlHandler(signal_handler, TRUE)) {
if(SetConsoleMode(TerminalSettings.hStdOut,
(TerminalSettings.dwOutputMode |
ENABLE_VIRTUAL_TERMINAL_PROCESSING))) {
tool_term_has_bold = true;
atexit(restore_terminal);
}
else {
SetConsoleCtrlHandler(signal_handler, FALSE);
(void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE);
}
}
}
}
#endif
CURLcode win32_init(void)
{
curlx_now_init();
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
init_terminal();
#endif
return CURLE_OK;
}
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
struct win_thread_data {
HANDLE stdin_handle;
curl_socket_t socket_l;
};
static DWORD WINAPI win_stdin_thread_func(void *thread_data)
{
struct win_thread_data *tdata = (struct win_thread_data *)thread_data;
DWORD n;
int nwritten;
char buffer[BUFSIZ];
BOOL r;
SOCKADDR_IN clientAddr;
int clientAddrLen = sizeof(clientAddr);
curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, (SOCKADDR*)&clientAddr,
&clientAddrLen);
if(socket_w == CURL_SOCKET_BAD) {
errorf("accept error: %d", SOCKERRNO);
goto ThreadCleanup;
}
sclose(tdata->socket_l);
tdata->socket_l = CURL_SOCKET_BAD;
if(shutdown(socket_w, SD_RECEIVE) == SOCKET_ERROR) {
errorf("shutdown error: %d", SOCKERRNO);
goto ThreadCleanup;
}
for(;;) {
r = ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL);
if(r == 0)
break;
if(n == 0)
break;
nwritten = CURL_SEND(socket_w, buffer, n, 0);
if(nwritten == SOCKET_ERROR)
break;
if((DWORD)nwritten != n)
break;
}
ThreadCleanup:
CloseHandle(tdata->stdin_handle);
tdata->stdin_handle = NULL;
if(tdata->socket_l != CURL_SOCKET_BAD) {
sclose(tdata->socket_l);
tdata->socket_l = CURL_SOCKET_BAD;
}
if(socket_w != CURL_SOCKET_BAD)
sclose(socket_w);
if(tdata) {
free(tdata);
}
return 0;
}
curl_socket_t win32_stdin_read_thread(void)
{
int result;
bool r;
int rc = 0, socksize = 0;
struct win_thread_data *tdata = NULL;
SOCKADDR_IN selfaddr;
static HANDLE stdin_thread = NULL;
static curl_socket_t socket_r = CURL_SOCKET_BAD;
if(socket_r != CURL_SOCKET_BAD) {
assert(stdin_thread != NULL);
return socket_r;
}
assert(stdin_thread == NULL);
do {
tdata = (struct win_thread_data*)calloc(1, sizeof(struct win_thread_data));
if(!tdata) {
errorf("calloc() error");
break;
}
tdata->socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(tdata->socket_l == CURL_SOCKET_BAD) {
errorf("socket() error: %d", SOCKERRNO);
break;
}
socksize = sizeof(selfaddr);
memset(&selfaddr, 0, socksize);
selfaddr.sin_family = AF_INET;
selfaddr.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK);
result = bind(tdata->socket_l, (SOCKADDR*)&selfaddr, socksize);
if(result == SOCKET_ERROR) {
errorf("bind error: %d", SOCKERRNO);
break;
}
result = getsockname(tdata->socket_l, (SOCKADDR*)&selfaddr, &socksize);
if(result == SOCKET_ERROR) {
errorf("getsockname error: %d", SOCKERRNO);
break;
}
result = listen(tdata->socket_l, 1);
if(result == SOCKET_ERROR) {
errorf("listen error: %d", SOCKERRNO);
break;
}
r = DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE),
GetCurrentProcess(), &tdata->stdin_handle,
0, FALSE, DUPLICATE_SAME_ACCESS);
if(!r) {
errorf("DuplicateHandle error: 0x%08lx", GetLastError());
break;
}
stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func,
tdata, 0, NULL);
if(!stdin_thread) {
errorf("CreateThread error: 0x%08lx", GetLastError());
break;
}
socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(socket_r == CURL_SOCKET_BAD) {
errorf("socket error: %d", SOCKERRNO);
break;
}
setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0);
if(connect(socket_r, (SOCKADDR*)&selfaddr, socksize) == SOCKET_ERROR) {
errorf("connect error: %d", SOCKERRNO);
break;
}
if(shutdown(socket_r, SD_SEND) == SOCKET_ERROR) {
errorf("shutdown error: %d", SOCKERRNO);
break;
}
if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) {
errorf("SetStdHandle error: 0x%08lx", GetLastError());
break;
}
rc = 1;
} while(0);
if(rc != 1) {
if(socket_r != CURL_SOCKET_BAD && tdata) {
if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r &&
tdata->stdin_handle) {
SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle);
tdata->stdin_handle = NULL;
}
sclose(socket_r);
socket_r = CURL_SOCKET_BAD;
}
if(stdin_thread) {
TerminateThread(stdin_thread, 1);
CloseHandle(stdin_thread);
stdin_thread = NULL;
}
if(tdata) {
if(tdata->stdin_handle)
CloseHandle(tdata->stdin_handle);
if(tdata->socket_l != CURL_SOCKET_BAD)
sclose(tdata->socket_l);
free(tdata);
}
return CURL_SOCKET_BAD;
}
assert(socket_r != CURL_SOCKET_BAD);
return socket_r;
}
#endif
#endif
#endif