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