1/*
2 * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/*
11 * This file is dual-licensed and is also available under the following
12 * terms:
13 *
14 * Copyright (c) 2004, Richard Levitte <richard@levitte.org>
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in the
24 *    documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <windows.h>
40#include <tchar.h>
41#include "internal/numbers.h"
42#ifndef LPDIR_H
43# include "LPdir.h"
44#endif
45
46/*
47 * We're most likely overcautious here, but let's reserve for broken WinCE
48 * headers and explicitly opt for UNICODE call. Keep in mind that our WinCE
49 * builds are compiled with -DUNICODE [as well as -D_UNICODE].
50 */
51#if defined(LP_SYS_WINCE) && !defined(FindFirstFile)
52# define FindFirstFile FindFirstFileW
53#endif
54#if defined(LP_SYS_WINCE) && !defined(FindNextFile)
55# define FindNextFile FindNextFileW
56#endif
57
58#ifndef NAME_MAX
59# define NAME_MAX 255
60#endif
61
62#ifdef CP_UTF8
63# define CP_DEFAULT CP_UTF8
64#else
65# define CP_DEFAULT CP_ACP
66#endif
67
68struct LP_dir_context_st {
69    WIN32_FIND_DATA ctx;
70    HANDLE handle;
71    char entry_name[NAME_MAX + 1];
72};
73
74const char *LP_find_file(LP_DIR_CTX **ctx, const char *directory)
75{
76    if (ctx == NULL || directory == NULL) {
77        errno = EINVAL;
78        return 0;
79    }
80
81    errno = 0;
82    if (*ctx == NULL) {
83        size_t dirlen = strlen(directory);
84
85        if (dirlen == 0 || dirlen > INT_MAX - 3) {
86            errno = ENOENT;
87            return 0;
88        }
89
90        *ctx = malloc(sizeof(**ctx));
91        if (*ctx == NULL) {
92            errno = ENOMEM;
93            return 0;
94        }
95        memset(*ctx, 0, sizeof(**ctx));
96
97        if (sizeof(TCHAR) != sizeof(char)) {
98            TCHAR *wdir = NULL;
99            /* len_0 denotes string length *with* trailing 0 */
100            size_t index = 0, len_0 = dirlen + 1;
101#ifdef LP_MULTIBYTE_AVAILABLE
102            int sz = 0;
103            UINT cp;
104
105            do {
106# ifdef CP_UTF8
107                if ((sz = MultiByteToWideChar((cp = CP_UTF8), 0,
108                                              directory, len_0,
109                                              NULL, 0)) > 0 ||
110                    GetLastError() != ERROR_NO_UNICODE_TRANSLATION)
111                    break;
112# endif
113                sz = MultiByteToWideChar((cp = CP_ACP), 0,
114                                         directory, len_0,
115                                         NULL, 0);
116            } while (0);
117
118            if (sz > 0) {
119                /*
120                 * allocate two additional characters in case we need to
121                 * concatenate asterisk, |sz| covers trailing '\0'!
122                 */
123                wdir = _alloca((sz + 2) * sizeof(TCHAR));
124                if (!MultiByteToWideChar(cp, 0, directory, len_0,
125                                         (WCHAR *)wdir, sz)) {
126                    free(*ctx);
127                    *ctx = NULL;
128                    errno = EINVAL;
129                    return 0;
130                }
131            } else
132#endif
133            {
134                sz = len_0;
135                /*
136                 * allocate two additional characters in case we need to
137                 * concatenate asterisk, |sz| covers trailing '\0'!
138                 */
139                wdir = _alloca((sz + 2) * sizeof(TCHAR));
140                for (index = 0; index < len_0; index++)
141                    wdir[index] = (TCHAR)directory[index];
142            }
143
144            sz--; /* wdir[sz] is trailing '\0' now */
145            if (wdir[sz - 1] != TEXT('*')) {
146                if (wdir[sz - 1] != TEXT('/') && wdir[sz - 1] != TEXT('\\'))
147                    _tcscpy(wdir + sz, TEXT("/*"));
148                else
149                    _tcscpy(wdir + sz, TEXT("*"));
150            }
151
152            (*ctx)->handle = FindFirstFile(wdir, &(*ctx)->ctx);
153        } else {
154            if (directory[dirlen - 1] != '*') {
155                char *buf = _alloca(dirlen + 3);
156
157                strcpy(buf, directory);
158                if (buf[dirlen - 1] != '/' && buf[dirlen - 1] != '\\')
159                    strcpy(buf + dirlen, "/*");
160                else
161                    strcpy(buf + dirlen, "*");
162
163                directory = buf;
164            }
165
166            (*ctx)->handle = FindFirstFile((TCHAR *)directory, &(*ctx)->ctx);
167        }
168
169        if ((*ctx)->handle == INVALID_HANDLE_VALUE) {
170            free(*ctx);
171            *ctx = NULL;
172            errno = EINVAL;
173            return 0;
174        }
175    } else {
176        if (FindNextFile((*ctx)->handle, &(*ctx)->ctx) == FALSE) {
177            return 0;
178        }
179    }
180    if (sizeof(TCHAR) != sizeof(char)) {
181        TCHAR *wdir = (*ctx)->ctx.cFileName;
182        size_t index, len_0 = 0;
183
184        while (wdir[len_0] && len_0 < (sizeof((*ctx)->entry_name) - 1))
185            len_0++;
186        len_0++;
187
188#ifdef LP_MULTIBYTE_AVAILABLE
189        if (!WideCharToMultiByte(CP_DEFAULT, 0, (WCHAR *)wdir, len_0,
190                                 (*ctx)->entry_name,
191                                 sizeof((*ctx)->entry_name), NULL, 0))
192#endif
193            for (index = 0; index < len_0; index++)
194                (*ctx)->entry_name[index] = (char)wdir[index];
195    } else
196        strncpy((*ctx)->entry_name, (const char *)(*ctx)->ctx.cFileName,
197                sizeof((*ctx)->entry_name) - 1);
198
199    (*ctx)->entry_name[sizeof((*ctx)->entry_name) - 1] = '\0';
200
201    return (*ctx)->entry_name;
202}
203
204int LP_find_file_end(LP_DIR_CTX **ctx)
205{
206    if (ctx != NULL && *ctx != NULL) {
207        FindClose((*ctx)->handle);
208        free(*ctx);
209        *ctx = NULL;
210        return 1;
211    }
212    errno = EINVAL;
213    return 0;
214}
215