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(MSDOS) || defined(WIN32)
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 /* Search and set cert file only if libcurl supports SSL.
624 *
625 * If Schannel is the selected SSL backend then these locations are
626 * ignored. We allow setting CA location for schannel only when explicitly
627 * specified by the user via CURLOPT_CAINFO / --cacert.
628 */
629 if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) {
630
631 DWORD res_len;
632 TCHAR buf[PATH_MAX];
633 TCHAR *ptr = NULL;
634
635 buf[0] = TEXT('\0');
636
637 res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
638 if(res_len > 0) {
639 char *mstr = curlx_convert_tchar_to_UTF8(buf);
640 Curl_safefree(config->cacert);
641 if(mstr)
642 config->cacert = strdup(mstr);
643 curlx_unicodefree(mstr);
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 /* curlx_verify_windows_version must be called during init at least once
770 because it has its own initialization routine. */
771 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
772 VERSION_GREATER_THAN_EQUAL))
773 tool_isVistaOrGreater = true;
774 else
775 tool_isVistaOrGreater = false;
776
777 QueryPerformanceFrequency(&tool_freq);
778
779 init_terminal();
780
781 return CURLE_OK;
782 }
783
784 #endif /* WIN32 */
785
786 #endif /* MSDOS || WIN32 */
787