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