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
67 static SANITIZEcode truncate_dryrun(const char *path,
68 const size_t truncate_pos);
69 #ifdef MSDOS
70 static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
71 int flags);
72 #endif
73 static 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 /*
80 Sanitize a file or path name.
81
82 All banned characters are replaced by underscores, for example:
83 f?*foo => f__foo
84 f:foo::$DATA => f_foo__$DATA
85 f:\foo:bar => f__foo_bar
86 f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH)
87
88 This function was implemented according to the guidelines in 'Naming Files,
89 Paths, and Namespaces' section 'Naming Conventions'.
90 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
91
92 Flags
93 -----
94 SANITIZE_ALLOW_COLONS: Allow colons.
95 Without this flag colons are sanitized.
96
97 SANITIZE_ALLOW_PATH: Allow path separators and colons.
98 Without this flag path separators and colons are sanitized.
99
100 SANITIZE_ALLOW_RESERVED: Allow reserved device names.
101 Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's
102 in a UNC prefixed path.
103
104 SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename.
105 Without this flag if the sanitized filename or path will be too long an error
106 occurs. With this flag the filename --and not any other parts of the path-- may
107 be truncated to at least a single character. A filename followed by an
108 alternate data stream (ADS) cannot be truncated in any case.
109
110 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
111 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
112 */
sanitize_file_name(char ** const sanitized,const char * file_name,int flags)113 SANITIZEcode 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 /*
239 Test if truncating a path to a file will leave at least a single character in
240 the filename. Filenames suffixed by an alternate data stream can't be
241 truncated. This performs a dry run, nothing is modified.
242
243 Good truncate_pos 9: C:\foo\bar => C:\foo\ba
244 Good truncate_pos 6: C:\foo => C:\foo
245 Good truncate_pos 5: C:\foo => C:\fo
246 Bad* truncate_pos 5: C:foo => C:foo
247 Bad truncate_pos 5: C:\foo:ads => C:\fo
248 Bad truncate_pos 9: C:\foo:ads => C:\foo:ad
249 Bad truncate_pos 5: C:\foo\bar => C:\fo
250 Bad truncate_pos 5: C:\foo\ => C:\fo
251 Bad truncate_pos 7: C:\foo\ => C:\foo\
252 Error truncate_pos 7: C:\foo => (pos out of range)
253 Bad 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
258 Returns
259 SANITIZE_ERR_OK: Good -- 'path' can be truncated
260 SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
261 != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
262 */
truncate_dryrun(const char * path,const size_t truncate_pos)263 SANITIZEcode 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 /*
300 Extra sanitization MSDOS for file_name.
301
302 This is a supporting function for sanitize_file_name.
303
304 Warning: This is an MSDOS legacy function and was purposely written in a way
305 that 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
307 sanitize_file_name.
308
309 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
310 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
311 */
312 #if defined(MSDOS) || defined(UNITTESTS)
msdosify(char ** const sanitized,const char * file_name,int flags)313 SANITIZEcode 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 /*
448 Rename file_name if it's a reserved dos device name.
449
450 This is a supporting function for sanitize_file_name.
451
452 Warning: This is an MSDOS legacy function and was purposely written in a way
453 that 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
455 sanitize_file_name.
456
457 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
458 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
459 */
rename_if_reserved_dos_device_name(char ** const sanitized,const char * file_name,int flags)460 SANITIZEcode 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 */
__crt0_glob_function(char * arg)592 char **__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
FindWin32CACert(struct OperationConfig * config,curl_sslbackend backend,const TCHAR * bundle_file)617 CURLcode FindWin32CACert(struct OperationConfig *config,
618 curl_sslbackend backend,
619 const TCHAR *bundle_file)
620 {
621 CURLcode result = CURLE_OK;
622
623 #ifdef CURL_WINDOWS_APP
624 (void)config;
625 (void)backend;
626 (void)bundle_file;
627 #else
628 /* Search and set cert file only if libcurl supports SSL.
629 *
630 * If Schannel is the selected SSL backend then these locations are
631 * ignored. We allow setting CA location for schannel only when explicitly
632 * specified by the user via CURLOPT_CAINFO / --cacert.
633 */
634 if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) {
635
636 DWORD res_len;
637 TCHAR buf[PATH_MAX];
638 TCHAR *ptr = NULL;
639
640 buf[0] = TEXT('\0');
641
642 res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
643 if(res_len > 0) {
644 char *mstr = curlx_convert_tchar_to_UTF8(buf);
645 Curl_safefree(config->cacert);
646 if(mstr)
647 config->cacert = strdup(mstr);
648 curlx_unicodefree(mstr);
649 if(!config->cacert)
650 result = CURLE_OUT_OF_MEMORY;
651 }
652 }
653 #endif
654
655 return result;
656 }
657
658
659 /* Get a list of all loaded modules with full paths.
660 * Returns slist on success or NULL on error.
661 */
GetLoadedModulePaths(void)662 struct curl_slist *GetLoadedModulePaths(void)
663 {
664 HANDLE hnd = INVALID_HANDLE_VALUE;
665 MODULEENTRY32 mod = {0};
666 struct curl_slist *slist = NULL;
667
668 mod.dwSize = sizeof(MODULEENTRY32);
669
670 do {
671 hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
672 } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
673
674 if(hnd == INVALID_HANDLE_VALUE)
675 goto error;
676
677 if(!Module32First(hnd, &mod))
678 goto error;
679
680 do {
681 char *path; /* points to stack allocated buffer */
682 struct curl_slist *temp;
683
684 #ifdef UNICODE
685 /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total
686 bytes of multibyte chars won't be more than twice that. */
687 char buffer[sizeof(mod.szExePath) * 2];
688 if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
689 buffer, sizeof(buffer), NULL, NULL))
690 goto error;
691 path = buffer;
692 #else
693 path = mod.szExePath;
694 #endif
695 temp = curl_slist_append(slist, path);
696 if(!temp)
697 goto error;
698 slist = temp;
699 } while(Module32Next(hnd, &mod));
700
701 goto cleanup;
702
703 error:
704 curl_slist_free_all(slist);
705 slist = NULL;
706 cleanup:
707 if(hnd != INVALID_HANDLE_VALUE)
708 CloseHandle(hnd);
709 return slist;
710 }
711
712 bool tool_term_has_bold;
713
714 #ifndef CURL_WINDOWS_APP
715 /* The terminal settings to restore on exit */
716 static struct TerminalSettings {
717 HANDLE hStdOut;
718 DWORD dwOutputMode;
719 LONG valid;
720 } TerminalSettings;
721
722 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
723 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
724 #endif
725
restore_terminal(void)726 static void restore_terminal(void)
727 {
728 if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE))
729 SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode);
730 }
731
732 /* This is the console signal handler.
733 * The system calls it in a separate thread.
734 */
signal_handler(DWORD type)735 static BOOL WINAPI signal_handler(DWORD type)
736 {
737 if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
738 restore_terminal();
739 return FALSE;
740 }
741
init_terminal(void)742 static void init_terminal(void)
743 {
744 TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
745
746 /*
747 * Enable VT (Virtual Terminal) output.
748 * Note: VT mode flag can be set on any version of Windows, but VT
749 * processing only performed on Win10 >= version 1709 (OS build 16299)
750 * Creator's Update. Also, ANSI bold on/off supported since then.
751 */
752 if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE ||
753 !GetConsoleMode(TerminalSettings.hStdOut,
754 &TerminalSettings.dwOutputMode) ||
755 !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT,
756 VERSION_GREATER_THAN_EQUAL))
757 return;
758
759 if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
760 tool_term_has_bold = true;
761 else {
762 /* The signal handler is set before attempting to change the console mode
763 because otherwise a signal would not be caught after the change but
764 before the handler was installed. */
765 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE);
766 if(SetConsoleCtrlHandler(signal_handler, TRUE)) {
767 if(SetConsoleMode(TerminalSettings.hStdOut,
768 (TerminalSettings.dwOutputMode |
769 ENABLE_VIRTUAL_TERMINAL_PROCESSING))) {
770 tool_term_has_bold = true;
771 atexit(restore_terminal);
772 }
773 else {
774 SetConsoleCtrlHandler(signal_handler, FALSE);
775 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE);
776 }
777 }
778 }
779 }
780 #endif
781
782 LARGE_INTEGER tool_freq;
783 bool tool_isVistaOrGreater;
784
win32_init(void)785 CURLcode win32_init(void)
786 {
787 /* curlx_verify_windows_version must be called during init at least once
788 because it has its own initialization routine. */
789 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
790 VERSION_GREATER_THAN_EQUAL))
791 tool_isVistaOrGreater = true;
792 else
793 tool_isVistaOrGreater = false;
794
795 QueryPerformanceFrequency(&tool_freq);
796
797 #ifndef CURL_WINDOWS_APP
798 init_terminal();
799 #endif
800
801 return CURLE_OK;
802 }
803
804 #endif /* _WIN32 */
805
806 #endif /* _WIN32 || MSDOS */
807