771f315b4bdf32c3422385f1d97bc1943652a829
[WebKit-https.git] / WebCore / storage / DatabaseTracker.cpp
1 /*
2  * Copyright (C) 2007 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include "config.h"
29 #include "DatabaseTracker.h"
30
31 #include "Database.h"
32 #include "FileSystem.h"
33 #include "NotImplemented.h"
34 #include "SecurityOriginData.h"
35 #include "SQLiteStatement.h"
36
37 namespace WebCore {
38
39 // HTML5 SQL Storage spec suggests 5MB as the default quota per origin
40 static const unsigned DefaultOriginQuota = 5242880;
41
42 struct SecurityOriginDataHash {
43     static unsigned hash(const SecurityOriginData& data)
44     {
45         unsigned hashCodes[3] = {
46             data.protocol().impl() ? data.protocol().impl()->hash() : 0,
47             data.host().impl() ? data.host().impl()->hash() : 0,
48             data.port()
49         };
50         return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 3 * sizeof(unsigned) / sizeof(UChar));
51     }
52          
53     static bool equal(const SecurityOriginData& a, const SecurityOriginData& b)
54     {
55         return a == b;
56     }
57
58     static const bool safeToCompareToEmptyOrDeleted = true;
59 };
60
61 struct SecurityOriginDataTraits : WTF::GenericHashTraits<SecurityOriginData> {
62     static const SecurityOriginData& deletedValue()
63     {
64         // Okay deleted value because file: protocols should always have port 0
65         static SecurityOriginData key("file", "", 1);
66         return key;
67     }
68     static const SecurityOriginData& emptyValue()
69     {
70         // Okay empty value because file: protocols should always have port 0
71         static SecurityOriginData key("file", "", 2);
72         return key;
73     }
74 };
75
76 DatabaseTracker& DatabaseTracker::tracker()
77 {
78     static DatabaseTracker tracker;
79
80     return tracker;
81 }
82
83 DatabaseTracker::DatabaseTracker()
84     : m_defaultQuota(DefaultOriginQuota)
85     , m_client(0)
86 {
87 }
88
89 void DatabaseTracker::setDatabasePath(const String& path)
90 {        
91     m_databasePath = path;
92     openTrackerDatabase();
93 }
94
95 const String& DatabaseTracker::databasePath()
96 {
97     return m_databasePath;
98 }
99
100 void DatabaseTracker::openTrackerDatabase()
101 {
102     ASSERT(!m_database.isOpen());
103     
104     makeAllDirectories(m_databasePath);
105     String databasePath = pathByAppendingComponent(m_databasePath, "Databases.db");
106
107     if (!m_database.open(databasePath)) {
108         // FIXME: What do do here?
109         return;
110     }
111     if (!m_database.tableExists("Origins")) {
112         if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, creationPolicy INTEGER NOT NULL ON CONFLICT FAIL, sizePolicy INTEGER NOT NULL ON CONFLICT FAIL);")) {
113             // FIXME: and here
114         }
115     }
116
117     if (!m_database.tableExists("Databases")) {
118         if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT UNIQUE ON CONFLICT REPLACE, name TEXT UNIQUE ON CONFLICT REPLACE, path TEXT NOT NULL ON CONFLICT FAIL);")) {
119             // FIXME: and here
120         }
121     }
122 }
123     
124 String DatabaseTracker::fullPathForDatabase(const SecurityOriginData& origin, const String& name)
125 {
126     String originIdentifier = origin.stringIdentifier();
127     String originPath = pathByAppendingComponent(m_databasePath, originIdentifier);
128     
129     // Make sure the path for this SecurityOrigin exists
130     if (!makeAllDirectories(originPath))
131         return "";
132     
133     // See if we have a path for this database yet
134     SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
135
136     if (statement.prepare() != SQLResultOk)
137         return "";
138
139     statement.bindText(1, originIdentifier);
140     statement.bindText(2, name);
141
142     int result = statement.step();
143
144     if (result == SQLResultRow)
145         return pathByAppendingComponent(originPath, statement.getColumnText16(0));
146     if (result != SQLResultDone) {
147         LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin.stringIdentifier().ascii().data(), name.ascii().data());
148         return "";
149     }
150     statement.finalize();
151     
152     SQLiteStatement sequenceStatement(m_database, "SELECT seq FROM sqlite_sequence WHERE name='Databases';");
153
154     // FIXME: More informative error handling here, even though these steps should never fail
155     if (sequenceStatement.prepare() != SQLResultOk)
156         return "";
157     result = sequenceStatement.step();
158
159     // This has a range of 2^63 and starts at 0 for every time a user resets Safari -
160     // I can't imagine it'd over overflow
161     int64_t seq = 0;
162     if (result == SQLResultRow) {
163         seq = sequenceStatement.getColumnInt64(0);
164     } else if (result != SQLResultDone)
165         return "";
166     sequenceStatement.finalize();
167
168     String filename;
169     do {
170         ++seq;
171         filename = pathByAppendingComponent(originPath, String::format("%016llx.db", seq));
172     } while (fileExists(filename));
173
174     if (!addDatabase(origin, name, String::format("%016llx.db", seq)))
175         return "";
176
177     return filename;
178 }
179
180 void DatabaseTracker::populateOrigins()
181 {
182     if (m_origins)
183         return;
184
185     m_origins.set(new HashSet<SecurityOriginData, SecurityOriginDataHash, SecurityOriginDataTraits>);
186
187     if (!m_database.isOpen())
188         return;
189
190     SQLiteStatement statement(m_database, "SELECT DISTINCT origin FROM Databases;");
191
192     if (statement.prepare() != SQLResultOk)
193         return;
194
195     int result;
196     while ((result = statement.step()) == SQLResultRow)
197         m_origins->add(statement.getColumnText16(0));
198
199     if (result != SQLResultDone)
200         LOG_ERROR("Failed to read in all origins from the database");
201
202     return;
203 }
204
205 void DatabaseTracker::origins(Vector<SecurityOriginData>& result)
206 {
207     if (!m_origins)
208         populateOrigins();
209
210     copyToVector(*(m_origins.get()), result);
211 }
212
213 bool DatabaseTracker::databaseNamesForOrigin(const SecurityOriginData& origin, Vector<String>& resultVector)
214 {
215     if (!m_database.isOpen())
216         return false;
217
218     SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
219
220     if (statement.prepare() != SQLResultOk)
221         return false;
222
223     statement.bindText(1, origin.stringIdentifier());
224
225     int result;
226     while ((result = statement.step()) == SQLResultRow)
227         resultVector.append(statement.getColumnText16(0));
228
229     if (result != SQLResultDone) {
230         LOG_ERROR("Failed to retrieve all database names for origin %s", origin.stringIdentifier().ascii().data());
231         return false;
232     }
233
234     return true;
235 }
236
237 DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, const SecurityOriginData& origin)
238 {
239     notImplemented();
240     return DatabaseDetails();
241 }
242
243
244 unsigned long long DatabaseTracker::usageForOrigin(const SecurityOriginData& origin)
245 {
246     notImplemented();
247     return 0;
248 }
249
250 unsigned long long DatabaseTracker::quotaForOrigin(const SecurityOriginData& origin)
251 {
252     notImplemented();
253     return 5 * 1024 * 1024;
254 }
255
256 void DatabaseTracker::setQuota(const SecurityOriginData& origin, unsigned long long quota)
257 {
258     notImplemented();
259 }
260     
261 bool DatabaseTracker::addDatabase(const SecurityOriginData& origin, const String& name, const String& path)
262 {
263     if (!m_database.isOpen())
264         return false;
265
266     SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
267
268     if (statement.prepare() != SQLResultOk)
269         return false;
270
271     statement.bindText(1, origin.stringIdentifier());
272     statement.bindText(2, name);
273     statement.bindText(3, path);
274
275     if (!statement.executeCommand()) {
276         LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin.stringIdentifier().ascii().data(), statement.lastErrorMsg());
277         return false;
278     }
279
280     populateOrigins();
281     m_origins->add(origin);
282     return true;
283 }
284
285 void DatabaseTracker::deleteAllDatabases()
286 {
287     notImplemented();
288 }
289
290 void DatabaseTracker::deleteDatabasesWithOrigin(const SecurityOriginData& origin)
291 {
292     notImplemented();
293 }
294
295 void DatabaseTracker::deleteDatabase(const SecurityOriginData& origin, const String& name)
296 {
297     notImplemented();
298 }
299
300 void DatabaseTracker::setClient(DatabaseTrackerClient* client)
301 {
302     m_client = client;
303 }
304
305 void DatabaseTracker::setDefaultOriginQuota(unsigned long long quota)
306 {
307     m_defaultQuota = quota;
308 }
309
310 unsigned long long DatabaseTracker::defaultOriginQuota() const
311 {
312     return m_defaultQuota;
313 }
314
315 } // namespace WebCore