1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24#include "tool_setup.h" 25 26#if defined(_WIN32) || defined(MSDOS) 27 28#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) 29# include <libgen.h> 30#endif 31 32#ifdef _WIN32 33# include <stdlib.h> 34# include <tlhelp32.h> 35# include "tool_cfgable.h" 36# include "tool_libinfo.h" 37#endif 38 39#include "tool_bname.h" 40#include "tool_doswin.h" 41 42#include "curlx.h" 43#include "memdebug.h" /* keep this as LAST include */ 44 45#ifdef _WIN32 46# undef PATH_MAX 47# define PATH_MAX MAX_PATH 48#endif 49 50#ifndef S_ISCHR 51# ifdef S_IFCHR 52# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 53# else 54# define S_ISCHR(m) (0) /* cannot tell if file is a device */ 55# endif 56#endif 57 58#ifdef _WIN32 59# define _use_lfn(f) (1) /* long file names always available */ 60#elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ 61# define _use_lfn(f) (0) /* long file names never available */ 62#elif defined(__DJGPP__) 63# include <fcntl.h> /* _use_lfn(f) prototype */ 64#endif 65 66#ifndef UNITTESTS 67static SANITIZEcode truncate_dryrun(const char *path, 68 const size_t truncate_pos); 69#ifdef MSDOS 70static SANITIZEcode msdosify(char **const sanitized, const char *file_name, 71 int flags); 72#endif 73static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized, 74 const char *file_name, 75 int flags); 76#endif /* !UNITTESTS (static declarations used if no unit tests) */ 77 78 79/* 80Sanitize a file or path name. 81 82All banned characters are replaced by underscores, for example: 83f?*foo => f__foo 84f:foo::$DATA => f_foo__$DATA 85f:\foo:bar => f__foo_bar 86f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) 87 88This function was implemented according to the guidelines in 'Naming Files, 89Paths, and Namespaces' section 'Naming Conventions'. 90https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx 91 92Flags 93----- 94SANITIZE_ALLOW_COLONS: Allow colons. 95Without this flag colons are sanitized. 96 97SANITIZE_ALLOW_PATH: Allow path separators and colons. 98Without this flag path separators and colons are sanitized. 99 100SANITIZE_ALLOW_RESERVED: Allow reserved device names. 101Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's 102in a UNC prefixed path. 103 104SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename. 105Without this flag if the sanitized filename or path will be too long an error 106occurs. With this flag the filename --and not any other parts of the path-- may 107be truncated to at least a single character. A filename followed by an 108alternate data stream (ADS) cannot be truncated in any case. 109 110Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 111Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 112*/ 113SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, 114 int flags) 115{ 116 char *p, *target; 117 size_t len; 118 SANITIZEcode sc; 119 size_t max_sanitized_len; 120 121 if(!sanitized) 122 return SANITIZE_ERR_BAD_ARGUMENT; 123 124 *sanitized = NULL; 125 126 if(!file_name) 127 return SANITIZE_ERR_BAD_ARGUMENT; 128 129 if((flags & SANITIZE_ALLOW_PATH)) { 130#ifndef MSDOS 131 if(file_name[0] == '\\' && file_name[1] == '\\') 132 /* UNC prefixed path \\ (eg \\?\C:\foo) */ 133 max_sanitized_len = 32767-1; 134 else 135#endif 136 max_sanitized_len = PATH_MAX-1; 137 } 138 else 139 /* The maximum length of a filename. 140 FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and 141 does not discount the path information therefore we shouldn't use it. */ 142 max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1; 143 144 len = strlen(file_name); 145 if(len > max_sanitized_len) { 146 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || 147 truncate_dryrun(file_name, max_sanitized_len)) 148 return SANITIZE_ERR_INVALID_PATH; 149 150 len = max_sanitized_len; 151 } 152 153 target = malloc(len + 1); 154 if(!target) 155 return SANITIZE_ERR_OUT_OF_MEMORY; 156 157 strncpy(target, file_name, len); 158 target[len] = '\0'; 159 160#ifndef MSDOS 161 if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) 162 /* Skip the literal path prefix \\?\ */ 163 p = target + 4; 164 else 165#endif 166 p = target; 167 168 /* replace control characters and other banned characters */ 169 for(; *p; ++p) { 170 const char *banned; 171 172 if((1 <= *p && *p <= 31) || 173 (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') || 174 (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) { 175 *p = '_'; 176 continue; 177 } 178 179 for(banned = "|<>\"?*"; *banned; ++banned) { 180 if(*p == *banned) { 181 *p = '_'; 182 break; 183 } 184 } 185 } 186 187 /* remove trailing spaces and periods if not allowing paths */ 188 if(!(flags & SANITIZE_ALLOW_PATH) && len) { 189 char *clip = NULL; 190 191 p = &target[len]; 192 do { 193 --p; 194 if(*p != ' ' && *p != '.') 195 break; 196 clip = p; 197 } while(p != target); 198 199 if(clip) { 200 *clip = '\0'; 201 len = clip - target; 202 } 203 } 204 205#ifdef MSDOS 206 sc = msdosify(&p, target, flags); 207 free(target); 208 if(sc) 209 return sc; 210 target = p; 211 len = strlen(target); 212 213 if(len > max_sanitized_len) { 214 free(target); 215 return SANITIZE_ERR_INVALID_PATH; 216 } 217#endif 218 219 if(!(flags & SANITIZE_ALLOW_RESERVED)) { 220 sc = rename_if_reserved_dos_device_name(&p, target, flags); 221 free(target); 222 if(sc) 223 return sc; 224 target = p; 225 len = strlen(target); 226 227 if(len > max_sanitized_len) { 228 free(target); 229 return SANITIZE_ERR_INVALID_PATH; 230 } 231 } 232 233 *sanitized = target; 234 return SANITIZE_ERR_OK; 235} 236 237 238/* 239Test if truncating a path to a file will leave at least a single character in 240the filename. Filenames suffixed by an alternate data stream can't be 241truncated. This performs a dry run, nothing is modified. 242 243Good truncate_pos 9: C:\foo\bar => C:\foo\ba 244Good truncate_pos 6: C:\foo => C:\foo 245Good truncate_pos 5: C:\foo => C:\fo 246Bad* truncate_pos 5: C:foo => C:foo 247Bad truncate_pos 5: C:\foo:ads => C:\fo 248Bad truncate_pos 9: C:\foo:ads => C:\foo:ad 249Bad truncate_pos 5: C:\foo\bar => C:\fo 250Bad truncate_pos 5: C:\foo\ => C:\fo 251Bad truncate_pos 7: C:\foo\ => C:\foo\ 252Error truncate_pos 7: C:\foo => (pos out of range) 253Bad truncate_pos 1: C:\foo\ => C 254 255* C:foo is ambiguous, C could end up being a drive or file therefore something 256 like C:superlongfilename can't be truncated. 257 258Returns 259SANITIZE_ERR_OK: Good -- 'path' can be truncated 260SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated 261!= SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error 262*/ 263SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos) 264{ 265 size_t len; 266 267 if(!path) 268 return SANITIZE_ERR_BAD_ARGUMENT; 269 270 len = strlen(path); 271 272 if(truncate_pos > len) 273 return SANITIZE_ERR_BAD_ARGUMENT; 274 275 if(!len || !truncate_pos) 276 return SANITIZE_ERR_INVALID_PATH; 277 278 if(strpbrk(&path[truncate_pos - 1], "\\/:")) 279 return SANITIZE_ERR_INVALID_PATH; 280 281 /* C:\foo can be truncated but C:\foo:ads can't */ 282 if(truncate_pos > 1) { 283 const char *p = &path[truncate_pos - 1]; 284 do { 285 --p; 286 if(*p == ':') 287 return SANITIZE_ERR_INVALID_PATH; 288 } while(p != path && *p != '\\' && *p != '/'); 289 } 290 291 return SANITIZE_ERR_OK; 292} 293 294/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function 295 * were taken with modification from the DJGPP port of tar 1.12. They use 296 * algorithms originally from DJTAR. 297 */ 298 299/* 300Extra sanitization MSDOS for file_name. 301 302This is a supporting function for sanitize_file_name. 303 304Warning: This is an MSDOS legacy function and was purposely written in a way 305that some path information may pass through. For example drive letter names 306(C:, D:, etc) are allowed to pass through. For sanitizing a filename use 307sanitize_file_name. 308 309Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 310Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 311*/ 312#if defined(MSDOS) || defined(UNITTESTS) 313SANITIZEcode msdosify(char **const sanitized, const char *file_name, 314 int flags) 315{ 316 char dos_name[PATH_MAX]; 317 static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ 318 "|<>/\\\":?*"; /* illegal in DOS & W95 */ 319 static const char *illegal_chars_w95 = &illegal_chars_dos[8]; 320 int idx, dot_idx; 321 const char *s = file_name; 322 char *d = dos_name; 323 const char *const dlimit = dos_name + sizeof(dos_name) - 1; 324 const char *illegal_aliens = illegal_chars_dos; 325 size_t len = sizeof(illegal_chars_dos) - 1; 326 327 if(!sanitized) 328 return SANITIZE_ERR_BAD_ARGUMENT; 329 330 *sanitized = NULL; 331 332 if(!file_name) 333 return SANITIZE_ERR_BAD_ARGUMENT; 334 335 if(strlen(file_name) > PATH_MAX-1 && 336 (!(flags & SANITIZE_ALLOW_TRUNCATE) || 337 truncate_dryrun(file_name, PATH_MAX-1))) 338 return SANITIZE_ERR_INVALID_PATH; 339 340 /* Support for Windows 9X VFAT systems, when available. */ 341 if(_use_lfn(file_name)) { 342 illegal_aliens = illegal_chars_w95; 343 len -= (illegal_chars_w95 - illegal_chars_dos); 344 } 345 346 /* Get past the drive letter, if any. */ 347 if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { 348 *d++ = *s++; 349 *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_'; 350 ++d; ++s; 351 } 352 353 for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { 354 if(memchr(illegal_aliens, *s, len)) { 355 356 if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':') 357 *d = ':'; 358 else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) 359 *d = *s; 360 /* Dots are special: DOS doesn't allow them as the leading character, 361 and a file name cannot have more than a single dot. We leave the 362 first non-leading dot alone, unless it comes too close to the 363 beginning of the name: we want sh.lex.c to become sh_lex.c, not 364 sh.lex-c. */ 365 else if(*s == '.') { 366 if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && 367 (s[1] == '/' || s[1] == '\\' || 368 (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { 369 /* Copy "./" and "../" verbatim. */ 370 *d++ = *s++; 371 if(d == dlimit) 372 break; 373 if(*s == '.') { 374 *d++ = *s++; 375 if(d == dlimit) 376 break; 377 } 378 *d = *s; 379 } 380 else if(idx == 0) 381 *d = '_'; 382 else if(dot_idx >= 0) { 383 if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ 384 d[dot_idx - idx] = '_'; /* replace previous dot */ 385 *d = '.'; 386 } 387 else 388 *d = '-'; 389 } 390 else 391 *d = '.'; 392 393 if(*s == '.') 394 dot_idx = idx; 395 } 396 else if(*s == '+' && s[1] == '+') { 397 if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ 398 *d++ = 'x'; 399 if(d == dlimit) 400 break; 401 *d = 'x'; 402 } 403 else { 404 /* libg++ etc. */ 405 if(dlimit - d < 4) { 406 *d++ = 'x'; 407 if(d == dlimit) 408 break; 409 *d = 'x'; 410 } 411 else { 412 memcpy(d, "plus", 4); 413 d += 3; 414 } 415 } 416 s++; 417 idx++; 418 } 419 else 420 *d = '_'; 421 } 422 else 423 *d = *s; 424 if(*s == '/' || *s == '\\') { 425 idx = 0; 426 dot_idx = -1; 427 } 428 else 429 idx++; 430 } 431 *d = '\0'; 432 433 if(*s) { 434 /* dos_name is truncated, check that truncation requirements are met, 435 specifically truncating a filename suffixed by an alternate data stream 436 or truncating the entire filename is not allowed. */ 437 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") || 438 truncate_dryrun(dos_name, d - dos_name)) 439 return SANITIZE_ERR_INVALID_PATH; 440 } 441 442 *sanitized = strdup(dos_name); 443 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY); 444} 445#endif /* MSDOS || UNITTESTS */ 446 447/* 448Rename file_name if it's a reserved dos device name. 449 450This is a supporting function for sanitize_file_name. 451 452Warning: This is an MSDOS legacy function and was purposely written in a way 453that some path information may pass through. For example drive letter names 454(C:, D:, etc) are allowed to pass through. For sanitizing a filename use 455sanitize_file_name. 456 457Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 458Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 459*/ 460SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized, 461 const char *file_name, 462 int flags) 463{ 464 /* We could have a file whose name is a device on MS-DOS. Trying to 465 * retrieve such a file would fail at best and wedge us at worst. We need 466 * to rename such files. */ 467 char *p, *base; 468 char fname[PATH_MAX]; 469#ifdef MSDOS 470 struct_stat st_buf; 471#endif 472 473 if(!sanitized) 474 return SANITIZE_ERR_BAD_ARGUMENT; 475 476 *sanitized = NULL; 477 478 if(!file_name) 479 return SANITIZE_ERR_BAD_ARGUMENT; 480 481 /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */ 482#ifndef MSDOS 483 if((flags & SANITIZE_ALLOW_PATH) && 484 file_name[0] == '\\' && file_name[1] == '\\') { 485 size_t len = strlen(file_name); 486 *sanitized = malloc(len + 1); 487 if(!*sanitized) 488 return SANITIZE_ERR_OUT_OF_MEMORY; 489 strncpy(*sanitized, file_name, len + 1); 490 return SANITIZE_ERR_OK; 491 } 492#endif 493 494 if(strlen(file_name) > PATH_MAX-1 && 495 (!(flags & SANITIZE_ALLOW_TRUNCATE) || 496 truncate_dryrun(file_name, PATH_MAX-1))) 497 return SANITIZE_ERR_INVALID_PATH; 498 499 strncpy(fname, file_name, PATH_MAX-1); 500 fname[PATH_MAX-1] = '\0'; 501 base = basename(fname); 502 503 /* Rename reserved device names that are known to be accessible without \\.\ 504 Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS 505 https://support.microsoft.com/en-us/kb/74496 506 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx 507 */ 508 for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { 509 size_t p_len; 510 int x = (curl_strnequal(p, "CON", 3) || 511 curl_strnequal(p, "PRN", 3) || 512 curl_strnequal(p, "AUX", 3) || 513 curl_strnequal(p, "NUL", 3)) ? 3 : 514 (curl_strnequal(p, "CLOCK$", 6)) ? 6 : 515 (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? 516 (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; 517 518 if(!x) 519 continue; 520 521 /* the devices may be accessible with an extension or ADS, for 522 example CON.AIR and 'CON . AIR' and CON:AIR access console */ 523 524 for(; p[x] == ' '; ++x) 525 ; 526 527 if(p[x] == '.') { 528 p[x] = '_'; 529 continue; 530 } 531 else if(p[x] == ':') { 532 if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) { 533 p[x] = '_'; 534 continue; 535 } 536 ++x; 537 } 538 else if(p[x]) /* no match */ 539 continue; 540 541 /* p points to 'CON' or 'CON ' or 'CON:', etc */ 542 p_len = strlen(p); 543 544 /* Prepend a '_' */ 545 if(strlen(fname) == PATH_MAX-1) { 546 --p_len; 547 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len)) 548 return SANITIZE_ERR_INVALID_PATH; 549 p[p_len] = '\0'; 550 } 551 memmove(p + 1, p, p_len + 1); 552 p[0] = '_'; 553 ++p_len; 554 555 /* if fname was just modified then the basename pointer must be updated */ 556 if(p == fname) 557 base = basename(fname); 558 } 559 560 /* This is the legacy portion from rename_if_dos_device_name that checks for 561 reserved device names. It only works on MSDOS. On Windows XP the stat 562 check errors with EINVAL if the device name is reserved. On Windows 563 Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN 564 stat doc the latter behavior is correct, but that doesn't help us identify 565 whether it's a reserved device name and not a regular file name. */ 566#ifdef MSDOS 567 if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { 568 /* Prepend a '_' */ 569 size_t blen = strlen(base); 570 if(blen) { 571 if(strlen(fname) == PATH_MAX-1) { 572 --blen; 573 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen)) 574 return SANITIZE_ERR_INVALID_PATH; 575 base[blen] = '\0'; 576 } 577 memmove(base + 1, base, blen + 1); 578 base[0] = '_'; 579 } 580 } 581#endif 582 583 *sanitized = strdup(fname); 584 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY); 585} 586 587#if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__)) 588 589/* 590 * Disable program default argument globbing. We do it on our own. 591 */ 592char **__crt0_glob_function(char *arg) 593{ 594 (void)arg; 595 return (char **)0; 596} 597 598#endif /* MSDOS && (__DJGPP__ || __GO32__) */ 599 600#ifdef _WIN32 601 602/* 603 * Function to find CACert bundle on a Win32 platform using SearchPath. 604 * (SearchPath is already declared via inclusions done in setup header file) 605 * (Use the ASCII version instead of the unicode one!) 606 * The order of the directories it searches is: 607 * 1. application's directory 608 * 2. current working directory 609 * 3. Windows System directory (e.g. C:\windows\system32) 610 * 4. Windows Directory (e.g. C:\windows) 611 * 5. all directories along %PATH% 612 * 613 * For WinXP and later search order actually depends on registry value: 614 * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode 615 */ 616 617CURLcode FindWin32CACert(struct OperationConfig *config, 618 curl_sslbackend backend, 619 const TCHAR *bundle_file) 620{ 621 CURLcode result = CURLE_OK; 622 623 /* Search and set cert file only if libcurl supports SSL. 624 * 625 * If Schannel is the selected SSL backend then these locations are 626 * ignored. We allow setting CA location for schannel only when explicitly 627 * specified by the user via CURLOPT_CAINFO / --cacert. 628 */ 629 if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) { 630 631 DWORD res_len; 632 TCHAR buf[PATH_MAX]; 633 TCHAR *ptr = NULL; 634 635 buf[0] = TEXT('\0'); 636 637 res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr); 638 if(res_len > 0) { 639 char *mstr = curlx_convert_tchar_to_UTF8(buf); 640 Curl_safefree(config->cacert); 641 if(mstr) 642 config->cacert = strdup(mstr); 643 curlx_unicodefree(mstr); 644 if(!config->cacert) 645 result = CURLE_OUT_OF_MEMORY; 646 } 647 } 648 649 return result; 650} 651 652 653/* Get a list of all loaded modules with full paths. 654 * Returns slist on success or NULL on error. 655 */ 656struct curl_slist *GetLoadedModulePaths(void) 657{ 658 HANDLE hnd = INVALID_HANDLE_VALUE; 659 MODULEENTRY32 mod = {0}; 660 struct curl_slist *slist = NULL; 661 662 mod.dwSize = sizeof(MODULEENTRY32); 663 664 do { 665 hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); 666 } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH); 667 668 if(hnd == INVALID_HANDLE_VALUE) 669 goto error; 670 671 if(!Module32First(hnd, &mod)) 672 goto error; 673 674 do { 675 char *path; /* points to stack allocated buffer */ 676 struct curl_slist *temp; 677 678#ifdef UNICODE 679 /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total 680 bytes of multibyte chars won't be more than twice that. */ 681 char buffer[sizeof(mod.szExePath) * 2]; 682 if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1, 683 buffer, sizeof(buffer), NULL, NULL)) 684 goto error; 685 path = buffer; 686#else 687 path = mod.szExePath; 688#endif 689 temp = curl_slist_append(slist, path); 690 if(!temp) 691 goto error; 692 slist = temp; 693 } while(Module32Next(hnd, &mod)); 694 695 goto cleanup; 696 697error: 698 curl_slist_free_all(slist); 699 slist = NULL; 700cleanup: 701 if(hnd != INVALID_HANDLE_VALUE) 702 CloseHandle(hnd); 703 return slist; 704} 705 706/* The terminal settings to restore on exit */ 707static struct TerminalSettings { 708 HANDLE hStdOut; 709 DWORD dwOutputMode; 710 LONG valid; 711} TerminalSettings; 712 713#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 714#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 715#endif 716 717bool tool_term_has_bold; 718 719static void restore_terminal(void) 720{ 721 if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE)) 722 SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); 723} 724 725/* This is the console signal handler. 726 * The system calls it in a separate thread. 727 */ 728static BOOL WINAPI signal_handler(DWORD type) 729{ 730 if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) 731 restore_terminal(); 732 return FALSE; 733} 734 735static void init_terminal(void) 736{ 737 TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 738 739 /* 740 * Enable VT (Virtual Terminal) output. 741 * Note: VT mode flag can be set on any version of Windows, but VT 742 * processing only performed on Win10 >= version 1709 (OS build 16299) 743 * Creator's Update. Also, ANSI bold on/off supported since then. 744 */ 745 if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE || 746 !GetConsoleMode(TerminalSettings.hStdOut, 747 &TerminalSettings.dwOutputMode) || 748 !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, 749 VERSION_GREATER_THAN_EQUAL)) 750 return; 751 752 if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) 753 tool_term_has_bold = true; 754 else { 755 /* The signal handler is set before attempting to change the console mode 756 because otherwise a signal would not be caught after the change but 757 before the handler was installed. */ 758 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE); 759 if(SetConsoleCtrlHandler(signal_handler, TRUE)) { 760 if(SetConsoleMode(TerminalSettings.hStdOut, 761 (TerminalSettings.dwOutputMode | 762 ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { 763 tool_term_has_bold = true; 764 atexit(restore_terminal); 765 } 766 else { 767 SetConsoleCtrlHandler(signal_handler, FALSE); 768 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE); 769 } 770 } 771 } 772} 773 774LARGE_INTEGER tool_freq; 775bool tool_isVistaOrGreater; 776 777CURLcode win32_init(void) 778{ 779 /* curlx_verify_windows_version must be called during init at least once 780 because it has its own initialization routine. */ 781 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, 782 VERSION_GREATER_THAN_EQUAL)) 783 tool_isVistaOrGreater = true; 784 else 785 tool_isVistaOrGreater = false; 786 787 QueryPerformanceFrequency(&tool_freq); 788 789 init_terminal(); 790 791 return CURLE_OK; 792} 793 794#endif /* _WIN32 */ 795 796#endif /* _WIN32 || MSDOS */ 797