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
49 # define _use_lfn(f) (1) /* long filenames always available */
50 #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */
51 # define _use_lfn(f) (0) /* long filenames never available */
52 #elif defined(__DJGPP__)
53 # include <fcntl.h> /* _use_lfn(f) prototype */
54 #endif
55
56 #ifdef MSDOS
57
58 #ifndef S_ISCHR
59 # ifdef S_IFCHR
60 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
61 # else
62 # define S_ISCHR(m) (0) /* cannot tell if file is a device */
63 # endif
64 #endif
65
66 /* only used by msdosify() */
67 static SANITIZEcode truncate_dryrun(const char *path,
68 const size_t truncate_pos);
69 static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
70 int flags);
71 #endif
72 static SANITIZEcode rename_if_reserved_dos(char **const sanitized,
73 const char *file_name,
74 int flags);
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_PATH: Allow path separators and colons.
93 Without this flag path separators and colons are sanitized.
94
95 SANITIZE_ALLOW_RESERVED: Allow reserved device names.
96 Without this flag a reserved device name is renamed (COM1 => _COM1) unless it
97 is in a UNC prefixed path.
98
99 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
100 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
101 */
sanitize_file_name(char ** const sanitized,const char * file_name,int flags)102 SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
103 int flags)
104 {
105 char *p, *target;
106 size_t len;
107 SANITIZEcode sc;
108 size_t max_sanitized_len;
109
110 if(!sanitized)
111 return SANITIZE_ERR_BAD_ARGUMENT;
112
113 *sanitized = NULL;
114
115 if(!file_name)
116 return SANITIZE_ERR_BAD_ARGUMENT;
117
118 if(flags & SANITIZE_ALLOW_PATH) {
119 #ifndef MSDOS
120 if(file_name[0] == '\\' && file_name[1] == '\\')
121 /* UNC prefixed path \\ (eg \\?\C:\foo) */
122 max_sanitized_len = 32767-1;
123 else
124 #endif
125 max_sanitized_len = PATH_MAX-1;
126 }
127 else
128 /* The maximum length of a filename. FILENAME_MAX is often the same as
129 PATH_MAX, in other words it is 260 and does not discount the path
130 information therefore we should not use it. */
131 max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
132
133 len = strlen(file_name);
134 if(len > max_sanitized_len)
135 return SANITIZE_ERR_INVALID_PATH;
136
137 target = strdup(file_name);
138 if(!target)
139 return SANITIZE_ERR_OUT_OF_MEMORY;
140
141 #ifndef MSDOS
142 if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
143 /* Skip the literal path prefix \\?\ */
144 p = target + 4;
145 else
146 #endif
147 p = target;
148
149 /* replace control characters and other banned characters */
150 for(; *p; ++p) {
151 const char *banned;
152
153 if((1 <= *p && *p <= 31) ||
154 (!(flags & SANITIZE_ALLOW_PATH) && *p == ':') ||
155 (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
156 *p = '_';
157 continue;
158 }
159
160 for(banned = "|<>\"?*"; *banned; ++banned) {
161 if(*p == *banned) {
162 *p = '_';
163 break;
164 }
165 }
166 }
167
168 /* remove trailing spaces and periods if not allowing paths */
169 if(!(flags & SANITIZE_ALLOW_PATH) && len) {
170 char *clip = NULL;
171
172 p = &target[len];
173 do {
174 --p;
175 if(*p != ' ' && *p != '.')
176 break;
177 clip = p;
178 } while(p != target);
179
180 if(clip) {
181 *clip = '\0';
182 len = clip - target;
183 }
184 }
185
186 #ifdef MSDOS
187 sc = msdosify(&p, target, flags);
188 free(target);
189 if(sc)
190 return sc;
191 target = p;
192 len = strlen(target);
193
194 if(len > max_sanitized_len) {
195 free(target);
196 return SANITIZE_ERR_INVALID_PATH;
197 }
198 #endif
199
200 if(!(flags & SANITIZE_ALLOW_RESERVED)) {
201 sc = rename_if_reserved_dos(&p, target, flags);
202 free(target);
203 if(sc)
204 return sc;
205 target = p;
206 len = strlen(target);
207
208 if(len > max_sanitized_len) {
209 free(target);
210 return SANITIZE_ERR_INVALID_PATH;
211 }
212 }
213
214 *sanitized = target;
215 return SANITIZE_ERR_OK;
216 }
217
218 #if defined(MSDOS)
219 /*
220 Test if truncating a path to a file will leave at least a single character in
221 the filename. Filenames suffixed by an alternate data stream cannot be
222 truncated. This performs a dry run, nothing is modified.
223
224 Good truncate_pos 9: C:\foo\bar => C:\foo\ba
225 Good truncate_pos 6: C:\foo => C:\foo
226 Good truncate_pos 5: C:\foo => C:\fo
227 Bad* truncate_pos 5: C:foo => C:foo
228 Bad truncate_pos 5: C:\foo:ads => C:\fo
229 Bad truncate_pos 9: C:\foo:ads => C:\foo:ad
230 Bad truncate_pos 5: C:\foo\bar => C:\fo
231 Bad truncate_pos 5: C:\foo\ => C:\fo
232 Bad truncate_pos 7: C:\foo\ => C:\foo\
233 Error truncate_pos 7: C:\foo => (pos out of range)
234 Bad truncate_pos 1: C:\foo\ => C
235
236 * C:foo is ambiguous, C could end up being a drive or file therefore something
237 like C:superlongfilename cannot be truncated.
238
239 Returns
240 SANITIZE_ERR_OK: Good -- 'path' can be truncated
241 SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
242 != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
243 */
truncate_dryrun(const char * path,const size_t truncate_pos)244 static SANITIZEcode truncate_dryrun(const char *path,
245 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 static 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 #ifdef __DJGPP__
554 /*
555 * Disable program default argument globbing. We do it on our own.
556 */
__crt0_glob_function(char * arg)557 char **__crt0_glob_function(char *arg)
558 {
559 (void)arg;
560 return (char **)0;
561 }
562 #endif
563
564 #ifdef _WIN32
565
566 #if !defined(CURL_WINDOWS_UWP) && \
567 !defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE)
568 /* Search and set the CA cert file for Windows.
569 *
570 * Do not call this function if Schannel is the selected SSL backend. We allow
571 * setting CA location for Schannel only when explicitly specified by the user
572 * via CURLOPT_CAINFO / --cacert.
573 *
574 * Function to find CACert bundle on a Win32 platform using SearchPath.
575 * (SearchPath is already declared via inclusions done in setup header file)
576 * (Use the ASCII version instead of the Unicode one!)
577 * The order of the directories it searches is:
578 * 1. application's directory
579 * 2. current working directory
580 * 3. Windows System directory (e.g. C:\Windows\System32)
581 * 4. Windows Directory (e.g. C:\Windows)
582 * 5. all directories along %PATH%
583 *
584 * For WinXP and later search order actually depends on registry value:
585 * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
586 */
FindWin32CACert(struct OperationConfig * config,const TCHAR * bundle_file)587 CURLcode FindWin32CACert(struct OperationConfig *config,
588 const TCHAR *bundle_file)
589 {
590 CURLcode result = CURLE_OK;
591 DWORD res_len;
592 TCHAR buf[PATH_MAX];
593 TCHAR *ptr = NULL;
594
595 buf[0] = TEXT('\0');
596
597 res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
598 if(res_len > 0) {
599 char *mstr = curlx_convert_tchar_to_UTF8(buf);
600 Curl_safefree(config->cacert);
601 if(mstr)
602 config->cacert = strdup(mstr);
603 curlx_unicodefree(mstr);
604 if(!config->cacert)
605 result = CURLE_OUT_OF_MEMORY;
606 }
607
608 return result;
609 }
610 #endif
611
612 /* Get a list of all loaded modules with full paths.
613 * Returns slist on success or NULL on error.
614 */
GetLoadedModulePaths(void)615 struct curl_slist *GetLoadedModulePaths(void)
616 {
617 struct curl_slist *slist = NULL;
618 #if !defined(CURL_WINDOWS_UWP)
619 HANDLE hnd = INVALID_HANDLE_VALUE;
620 MODULEENTRY32 mod = {0};
621
622 mod.dwSize = sizeof(MODULEENTRY32);
623
624 do {
625 hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
626 } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
627
628 if(hnd == INVALID_HANDLE_VALUE)
629 goto error;
630
631 if(!Module32First(hnd, &mod))
632 goto error;
633
634 do {
635 char *path; /* points to stack allocated buffer */
636 struct curl_slist *temp;
637
638 #ifdef UNICODE
639 /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total
640 bytes of multibyte chars will not be more than twice that. */
641 char buffer[sizeof(mod.szExePath) * 2];
642 if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
643 buffer, sizeof(buffer), NULL, NULL))
644 goto error;
645 path = buffer;
646 #else
647 path = mod.szExePath;
648 #endif
649 temp = curl_slist_append(slist, path);
650 if(!temp)
651 goto error;
652 slist = temp;
653 } while(Module32Next(hnd, &mod));
654
655 goto cleanup;
656
657 error:
658 curl_slist_free_all(slist);
659 slist = NULL;
660 cleanup:
661 if(hnd != INVALID_HANDLE_VALUE)
662 CloseHandle(hnd);
663 #endif
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