• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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