1/*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Collabora, Ltd. All rights reserved.
4 * Copyright (C) 2007-2009 Torch Mobile, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 *     its contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "FileSystem.h"
33
34#include "NotImplemented.h"
35#include "PlatformString.h"
36#include <wincrypt.h>
37#include <windows.h>
38#include <wtf/text/CString.h>
39
40namespace WebCore {
41
42static size_t reverseFindPathSeparator(const String& path, unsigned start = UINT_MAX)
43{
44    size_t positionSlash = path.reverseFind('/', start);
45    size_t positionBackslash = path.reverseFind('\\', start);
46
47    if (positionSlash == notFound)
48        return positionBackslash;
49
50    if (positionBackslash == notFound)
51        return positionSlash;
52
53    return std::max(positionSlash, positionBackslash);
54}
55
56static bool getFileInfo(const String& path, BY_HANDLE_FILE_INFORMATION& fileInfo)
57{
58    String filename = path;
59    HANDLE hFile = CreateFile(filename.charactersWithNullTermination(), GENERIC_READ, FILE_SHARE_READ, 0
60        , OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
61
62    if (hFile == INVALID_HANDLE_VALUE)
63        return false;
64
65    bool rtn = GetFileInformationByHandle(hFile, &fileInfo) ? true : false;
66
67    CloseHandle(hFile);
68    return rtn;
69}
70
71bool getFileSize(const String& path, long long& result)
72{
73    BY_HANDLE_FILE_INFORMATION fileInformation;
74    if (!getFileInfo(path, fileInformation))
75        return false;
76
77    ULARGE_INTEGER fileSize;
78    fileSize.LowPart = fileInformation.nFileSizeLow;
79    fileSize.HighPart = fileInformation.nFileSizeHigh;
80
81    result = fileSize.QuadPart;
82
83    return true;
84}
85
86bool getFileModificationTime(const String& path, time_t& result)
87{
88    BY_HANDLE_FILE_INFORMATION fileInformation;
89    if (!getFileInfo(path, fileInformation))
90        return false;
91
92    ULARGE_INTEGER t;
93    memcpy(&t, &fileInformation.ftLastWriteTime, sizeof(t));
94
95    result = t.QuadPart * 0.0000001 - 11644473600.0;
96
97    return true;
98}
99
100bool fileExists(const String& path)
101{
102    String filename = path;
103    HANDLE hFile = CreateFile(filename.charactersWithNullTermination(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE
104        , 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
105
106    CloseHandle(hFile);
107
108    return hFile != INVALID_HANDLE_VALUE;
109}
110
111bool deleteFile(const String& path)
112{
113    String filename = path;
114    return !!DeleteFileW(filename.charactersWithNullTermination());
115}
116
117
118bool deleteEmptyDirectory(const String& path)
119{
120    String filename = path;
121    return !!RemoveDirectoryW(filename.charactersWithNullTermination());
122}
123
124String pathByAppendingComponent(const String& path, const String& component)
125{
126    if (component.isEmpty())
127        return path;
128
129    Vector<UChar, MAX_PATH> buffer;
130
131    buffer.append(path.characters(), path.length());
132
133    if (buffer.last() != L'\\' && buffer.last() != L'/'
134        && component[0] != L'\\' && component[0] != L'/')
135        buffer.append(L'\\');
136
137    buffer.append(component.characters(), component.length());
138
139    return String(buffer.data(), buffer.size());
140}
141
142CString fileSystemRepresentation(const String&)
143{
144    return "";
145}
146
147bool makeAllDirectories(const String& path)
148{
149    size_t lastDivPos = reverseFindPathSeparator(path);
150    unsigned endPos = path.length();
151    if (lastDivPos == endPos - 1) {
152        --endPos;
153        lastDivPos = reverseFindPathSeparator(path, lastDivPos);
154    }
155
156    if (lastDivPos != notFound) {
157        if (!makeAllDirectories(path.substring(0, lastDivPos)))
158            return false;
159    }
160
161    String folder(path.substring(0, endPos));
162    CreateDirectory(folder.charactersWithNullTermination(), 0);
163
164    DWORD fileAttr = GetFileAttributes(folder.charactersWithNullTermination());
165    return fileAttr != 0xFFFFFFFF && (fileAttr & FILE_ATTRIBUTE_DIRECTORY);
166}
167
168String homeDirectoryPath()
169{
170    notImplemented();
171    return "";
172}
173
174String pathGetFileName(const String& path)
175{
176    size_t pos = reverseFindPathSeparator(path);
177    if (pos == notFound)
178        return path;
179    return path.substring(pos + 1);
180}
181
182String directoryName(const String& path)
183{
184    size_t pos = reverseFindPathSeparator(path);
185    if (pos == notFound)
186        return String();
187    return path.left(pos);
188}
189
190String openTemporaryFile(const String&, PlatformFileHandle& handle)
191{
192    handle = INVALID_HANDLE_VALUE;
193
194    wchar_t tempPath[MAX_PATH];
195    int tempPathLength = ::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath);
196    if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath))
197        return String();
198
199    HCRYPTPROV hCryptProv = 0;
200    if (!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
201        return String();
202
203    String proposedPath;
204    while (1) {
205
206        wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names)
207        const int randomPartLength = 8;
208        if (!CryptGenRandom(hCryptProv, randomPartLength * 2, reinterpret_cast<BYTE*>(tempFile)))
209            break;
210
211        // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation.
212        // don't include both upper and lowercase since Windows file systems are typically not case sensitive.
213        const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
214        for (int i = 0; i < randomPartLength; ++i)
215            tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)];
216
217        ASSERT(wcslen(tempFile) * 2 == sizeof(tempFile) - 2);
218
219        proposedPath = pathByAppendingComponent(String(tempPath), String(tempFile));
220
221        // use CREATE_NEW to avoid overwriting an existing file with the same name
222        handle = CreateFile(proposedPath.charactersWithNullTermination(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
223        if (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS)
224            continue;
225
226        break;
227    }
228
229    CryptReleaseContext(hCryptProv, 0);
230
231    if (!isHandleValid(handle))
232        return String();
233
234    return proposedPath;
235}
236
237PlatformFileHandle openFile(const String& path, FileOpenMode mode)
238{
239    DWORD desiredAccess = 0;
240    DWORD creationDisposition = 0;
241    switch (mode) {
242        case OpenForRead:
243            desiredAccess = GENERIC_READ;
244            creationDisposition = OPEN_EXISTING;
245        case OpenForWrite:
246            desiredAccess = GENERIC_WRITE;
247            creationDisposition = CREATE_ALWAYS;
248        default:
249            ASSERT_NOT_REACHED();
250    }
251
252    String destination = path;
253    return CreateFile(destination.charactersWithNullTermination(), desiredAccess, 0, 0, creationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
254}
255
256void closeFile(PlatformFileHandle& handle)
257{
258    if (isHandleValid(handle)) {
259        ::CloseHandle(handle);
260        handle = invalidPlatformFileHandle;
261    }
262}
263
264int writeToFile(PlatformFileHandle handle, const char* data, int length)
265{
266    if (!isHandleValid(handle))
267        return -1;
268
269    DWORD bytesWritten;
270    bool success = WriteFile(handle, data, length, &bytesWritten, 0);
271
272    if (!success)
273        return -1;
274    return static_cast<int>(bytesWritten);
275}
276
277bool unloadModule(PlatformModule module)
278{
279    return ::FreeLibrary(module);
280}
281
282String localUserSpecificStorageDirectory()
283{
284    return String(L"\\");
285}
286
287String roamingUserSpecificStorageDirectory()
288{
289    return String(L"\\");
290}
291
292Vector<String> listDirectory(const String& path, const String& filter)
293{
294    Vector<String> entries;
295
296    Vector<UChar, 256> pattern;
297    pattern.append(path.characters(), path.length());
298    if (pattern.last() != L'/' && pattern.last() != L'\\')
299        pattern.append(L'\\');
300
301    String root(pattern.data(), pattern.size());
302    pattern.append(filter.characters(), filter.length());
303    pattern.append(0);
304
305    WIN32_FIND_DATA findData;
306    HANDLE hFind = FindFirstFile(pattern.data(), &findData);
307    if (INVALID_HANDLE_VALUE != hFind) {
308        do {
309            // FIXEME: should we also add the folders? This function
310            // is so far only called by PluginDatabase.cpp to list
311            // all plugins in a folder, where it's not supposed to list sub-folders.
312            if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
313                entries.append(root + findData.cFileName);
314        } while (FindNextFile(hFind, &findData));
315        FindClose(hFind);
316    }
317
318    return entries;
319}
320
321} // namespace WebCore
322