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