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