1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "File.h"
28
29#include "FileMetadata.h"
30#include "FileSystem.h"
31#include "MIMETypeRegistry.h"
32#include <wtf/CurrentTime.h>
33#include <wtf/DateMath.h>
34#include <wtf/text/WTFString.h>
35
36namespace WebCore {
37
38static String getContentTypeFromFileName(const String& name, File::ContentTypeLookupPolicy policy)
39{
40    String type;
41    int index = name.reverseFind('.');
42    if (index != -1) {
43        if (policy == File::WellKnownContentTypes)
44            type = MIMETypeRegistry::getWellKnownMIMETypeForExtension(name.substring(index + 1));
45        else {
46            ASSERT(policy == File::AllContentTypes);
47            type = MIMETypeRegistry::getMIMETypeForExtension(name.substring(index + 1));
48        }
49    }
50    return type;
51}
52
53static PassOwnPtr<BlobData> createBlobDataForFileWithType(const String& path, const String& contentType)
54{
55    OwnPtr<BlobData> blobData = BlobData::create();
56    ASSERT(Blob::isNormalizedContentType(contentType));
57    blobData->setContentType(contentType);
58    blobData->appendFile(path);
59    return blobData.release();
60}
61
62static PassOwnPtr<BlobData> createBlobDataForFile(const String& path, File::ContentTypeLookupPolicy policy)
63{
64    return createBlobDataForFileWithType(path, getContentTypeFromFileName(path, policy));
65}
66
67static PassOwnPtr<BlobData> createBlobDataForFileWithName(const String& path, const String& fileSystemName, File::ContentTypeLookupPolicy policy)
68{
69    return createBlobDataForFileWithType(path, getContentTypeFromFileName(fileSystemName, policy));
70}
71
72#if ENABLE(FILE_SYSTEM)
73static PassOwnPtr<BlobData> createBlobDataForFileWithMetadata(const String& fileSystemName, const FileMetadata& metadata)
74{
75    OwnPtr<BlobData> blobData = BlobData::create();
76    blobData->setContentType(getContentTypeFromFileName(fileSystemName, File::WellKnownContentTypes));
77    blobData->appendFile(metadata.platformPath, 0, metadata.length, metadata.modificationTime);
78    return blobData.release();
79}
80
81static PassOwnPtr<BlobData> createBlobDataForFileSystemURL(const KURL& fileSystemURL, const FileMetadata& metadata)
82{
83    OwnPtr<BlobData> blobData = BlobData::create();
84    blobData->setContentType(getContentTypeFromFileName(fileSystemURL.path(), File::WellKnownContentTypes));
85    blobData->appendURL(fileSystemURL, 0, metadata.length, metadata.modificationTime);
86    return blobData.release();
87}
88#endif
89
90#if ENABLE(DIRECTORY_UPLOAD)
91PassRefPtr<File> File::createWithRelativePath(const String& path, const String& relativePath)
92{
93    RefPtr<File> file = adoptRef(new File(path, AllContentTypes));
94    file->m_relativePath = relativePath;
95    return file.release();
96}
97#endif
98
99File::File(const String& path, ContentTypeLookupPolicy policy)
100    : Blob(createBlobDataForFile(path, policy), -1)
101    , m_path(path)
102    , m_name(pathGetFileName(path))
103#if ENABLE(FILE_SYSTEM)
104    , m_snapshotSize(-1)
105    , m_snapshotModificationTime(invalidFileTime())
106#endif
107{
108}
109
110File::File(const String& path, const KURL& url, const String& type)
111    : Blob(url, type, -1)
112    , m_path(path)
113#if ENABLE(FILE_SYSTEM)
114    , m_snapshotSize(-1)
115    , m_snapshotModificationTime(invalidFileTime())
116#endif
117{
118    m_name = pathGetFileName(path);
119    // FIXME: File object serialization/deserialization does not include
120    // newer file object data members: m_name and m_relativePath.
121    // See SerializedScriptValue.cpp
122}
123
124File::File(const String& path, const String& name, ContentTypeLookupPolicy policy)
125    : Blob(createBlobDataForFileWithName(path, name, policy), -1)
126    , m_path(path)
127    , m_name(name)
128#if ENABLE(FILE_SYSTEM)
129    , m_snapshotSize(-1)
130    , m_snapshotModificationTime(invalidFileTime())
131#endif
132{
133}
134
135#if ENABLE(FILE_SYSTEM)
136File::File(const String& name, const FileMetadata& metadata)
137    : Blob(createBlobDataForFileWithMetadata(name, metadata), metadata.length)
138    , m_path(metadata.platformPath)
139    , m_name(name)
140    , m_snapshotSize(metadata.length)
141    , m_snapshotModificationTime(metadata.modificationTime)
142{
143}
144
145File::File(const KURL& fileSystemURL, const FileMetadata& metadata)
146    : Blob(createBlobDataForFileSystemURL(fileSystemURL, metadata), metadata.length)
147    , m_fileSystemURL(fileSystemURL)
148    , m_snapshotSize(metadata.length)
149    , m_snapshotModificationTime(metadata.modificationTime)
150{
151}
152#endif
153
154double File::lastModifiedDate() const
155{
156#if ENABLE(FILE_SYSTEM)
157    if (hasValidSnapshotMetadata() && isValidFileTime(m_snapshotModificationTime))
158        return m_snapshotModificationTime * msPerSecond;
159#endif
160
161    time_t modificationTime;
162    if (getFileModificationTime(m_path, modificationTime) && isValidFileTime(modificationTime))
163        return modificationTime * msPerSecond;
164
165    return currentTime() * msPerSecond;
166}
167
168unsigned long long File::size() const
169{
170#if ENABLE(FILE_SYSTEM)
171    if (hasValidSnapshotMetadata())
172        return m_snapshotSize;
173#endif
174
175    // FIXME: JavaScript cannot represent sizes as large as unsigned long long, we need to
176    // come up with an exception to throw if file size is not representable.
177    long long size;
178    if (!getFileSize(m_path, size))
179        return 0;
180    return static_cast<unsigned long long>(size);
181}
182
183void File::captureSnapshot(long long& snapshotSize, double& snapshotModificationTime) const
184{
185#if ENABLE(FILE_SYSTEM)
186    if (hasValidSnapshotMetadata()) {
187        snapshotSize = m_snapshotSize;
188        snapshotModificationTime = m_snapshotModificationTime;
189        return;
190    }
191#endif
192
193    // Obtains a snapshot of the file by capturing its current size and modification time. This is used when we slice a file for the first time.
194    // If we fail to retrieve the size or modification time, probably due to that the file has been deleted, 0 size is returned.
195    FileMetadata metadata;
196    if (!getFileMetadata(m_path, metadata)) {
197        snapshotSize = 0;
198        snapshotModificationTime = invalidFileTime();
199        return;
200    }
201
202    snapshotSize = metadata.length;
203    snapshotModificationTime = metadata.modificationTime;
204}
205
206} // namespace WebCore
207