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