1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "gears/base/common/permissions_db.h"
27 #include "gears/base/common/sqlite_wrapper.h"
28 #include "gears/base/common/thread_locals.h"
29 #include "gears/localserver/common/localserver_db.h"
31 static const char16
*kDatabaseName
= STRING16(L
"permissions.db");
32 static const char16
*kVersionTableName
= STRING16(L
"VersionInfo");
33 static const char16
*kVersionKeyName
= STRING16(L
"Version");
34 static const char16
*kAccessTableName
= STRING16(L
"Access");
35 static const int kCurrentVersion
= 4;
36 static const int kOldestUpgradeableVersion
= 1;
39 const std::string
PermissionsDB::kThreadLocalKey("base:permissions");
42 PermissionsDB
*PermissionsDB::GetDB() {
43 if (ThreadLocals::HasValue(kThreadLocalKey
)) {
44 return reinterpret_cast<PermissionsDB
*>(
45 ThreadLocals::GetValue(kThreadLocalKey
));
48 PermissionsDB
*db
= new PermissionsDB();
50 // If we can't initialize, we store NULL in the map so that we don't keep
51 // trying to Init() over and over.
57 ThreadLocals::SetValue(kThreadLocalKey
, db
, &DestroyDB
);
62 void PermissionsDB::DestroyDB(void *context
) {
63 PermissionsDB
*db
= reinterpret_cast<PermissionsDB
*>(context
);
70 PermissionsDB::PermissionsDB()
71 : version_table_(&db_
, kVersionTableName
),
72 access_table_(&db_
, kAccessTableName
),
73 shortcut_table_(&db_
) {
77 bool PermissionsDB::Init() {
78 // Initialize the database and tables
79 if (!db_
.Open(kDatabaseName
)) {
83 // Examine the contents of the database and determine if we have to
84 // instantiate or updgrade the schema.
86 version_table_
.GetInt(kVersionKeyName
, &version
);
88 // if its the version we're expecting, great
89 if (version
== kCurrentVersion
) {
93 // Doing this in a transaction effectively locks the database file and
94 // ensures that this is synchronized across all threads and processes
95 SQLTransaction
transaction(&db_
, "PermissionsDB::Init");
96 if (!transaction
.Begin()) {
100 // Fetch the version again in case someone else beat us to the
102 version_table_
.GetInt(kVersionKeyName
, &version
);
103 if (version
== kCurrentVersion
) {
108 // No database in place, create it.
110 // TODO(shess) Verify that this is true. Is it _no_ database, or
111 // is there a database which didn't have a version? The latter
112 // case would be masked by the CREATE IF NOT EXISTS statements
114 if (!CreateDatabase()) {
118 if (!UpgradeToVersion4()) {
123 // Double-check that we ended up with the right version.
124 version_table_
.GetInt(kVersionKeyName
, &version
);
125 if (version
!= kCurrentVersion
) {
129 return transaction
.Commit();
133 PermissionsDB::PermissionValue
PermissionsDB::GetCanAccessGears(
134 const SecurityOrigin
&origin
) {
135 int retval_int
= PERMISSION_DEFAULT
;
136 access_table_
.GetInt(origin
.url().c_str(), &retval_int
);
137 return static_cast<PermissionsDB::PermissionValue
>(retval_int
);
141 void PermissionsDB::SetCanAccessGears(const SecurityOrigin
&origin
,
142 PermissionsDB::PermissionValue value
) {
143 if (origin
.url().empty()) {
148 if (value
== PERMISSION_DEFAULT
) {
149 access_table_
.Clear(origin
.url().c_str());
150 } else if (value
== PERMISSION_ALLOWED
|| value
== PERMISSION_DENIED
) {
151 access_table_
.SetInt(origin
.url().c_str(), value
);
153 LOG(("PermissionsDB::SetCanAccessGears invalid value: %d", value
));
157 if (value
== PERMISSION_DENIED
|| value
== PERMISSION_DEFAULT
) {
158 WebCacheDB
*webcacheDB
= WebCacheDB::GetDB();
160 webcacheDB
->DeleteServersForOrigin(origin
);
166 bool PermissionsDB::GetOriginsByValue(PermissionsDB::PermissionValue value
,
167 std::vector
<SecurityOrigin
> *result
) {
168 if (PERMISSION_ALLOWED
!= value
&& PERMISSION_DENIED
!= value
) {
169 LOG(("Unexpected value: %d", value
));
173 // TODO(aa): Refactor into NameValueTable::FindNamesByIntValue().
174 std::string16
sql(STRING16(L
"SELECT Name FROM "));
175 sql
+= kAccessTableName
;
176 sql
+= STRING16(L
" WHERE Value = ? ORDER BY Name ASC");
178 SQLStatement statement
;
179 if (SQLITE_OK
!= statement
.prepare16(&db_
, sql
.c_str())) {
183 if (SQLITE_OK
!= statement
.bind_int(0, value
)) {
188 while (SQLITE_DONE
!= (rv
= statement
.step())) {
189 if (SQLITE_ROW
!= rv
) {
190 LOG(("PermissionsDB::ListGearsAccess: Could not iterate. Error was: %d",
191 db_
.GetErrorCode()));
195 SecurityOrigin origin
;
196 if (!origin
.InitFromUrl(statement
.column_text16_safe(0))) {
197 LOG(("PermissionsDB::ListGearsAccess: InitFromUrl() failed."));
198 // If we can't initialize a single URL, don't freak out. Try to do the
202 result
->push_back(origin
);
209 bool PermissionsDB::EnableGearsForWorker(const SecurityOrigin
&origin
) {
210 SQLTransaction
transaction(&db_
, "PermissionsDB::EnableGearsForWorker");
211 if (!transaction
.Begin()) {
215 switch (GetCanAccessGears(origin
)) {
216 case PERMISSION_ALLOWED
:
218 case PERMISSION_DENIED
:
220 case PERMISSION_DEFAULT
:
221 if (!access_table_
.SetInt(origin
.url().c_str(), PERMISSION_ALLOWED
)) {
224 return transaction
.Commit();
226 LOG(("Unexpected permission value"));
231 bool PermissionsDB::SetShortcut(const SecurityOrigin
&origin
,
232 const char16
*name
, const char16
*app_url
,
233 const std::vector
<std::string16
> &icon_urls
,
235 return shortcut_table_
.SetShortcut(origin
.url().c_str(), name
,
236 app_url
, icon_urls
, msg
);
239 bool PermissionsDB::GetOriginsWithShortcuts(
240 std::vector
<SecurityOrigin
> *result
) {
242 std::vector
<std::string16
> origin_urls
;
243 if (!shortcut_table_
.GetOriginsWithShortcuts(&origin_urls
)) {
247 for (size_t ii
= 0; ii
< origin_urls
.size(); ++ii
) {
248 SecurityOrigin origin
;
249 if (!origin
.InitFromUrl(origin_urls
[ii
].c_str())) {
250 LOG(("PermissionsDB::GetOriginsWithShortcuts: InitFromUrl() failed."));
251 // If we can't initialize a single URL, don't freak out. Try to do the
255 result
->push_back(origin
);
260 bool PermissionsDB::GetOriginShortcuts(const SecurityOrigin
&origin
,
261 std::vector
<std::string16
> *names
) {
262 return shortcut_table_
.GetOriginShortcuts(origin
.url().c_str(), names
);
265 bool PermissionsDB::GetShortcut(const SecurityOrigin
&origin
,
266 const char16
*name
, std::string16
*app_url
,
267 std::vector
<std::string16
> *icon_urls
,
268 std::string16
*msg
) {
269 return shortcut_table_
.GetShortcut(origin
.url().c_str(), name
,
270 app_url
, icon_urls
, msg
);
273 bool PermissionsDB::DeleteShortcut(const SecurityOrigin
&origin
,
274 const char16
*name
) {
275 return shortcut_table_
.DeleteShortcut(origin
.url().c_str(), name
);
278 bool PermissionsDB::DeleteShortcuts(const SecurityOrigin
&origin
) {
279 return shortcut_table_
.DeleteShortcuts(origin
.url().c_str());
282 bool PermissionsDB::CreateDatabase() {
283 ASSERT_SINGLE_THREAD();
285 SQLTransaction
transaction(&db_
, "PermissionsDB::CreateDatabase");
286 if (!transaction
.Begin()) {
290 if (!db_
.DropAllObjects()) {
294 if (!version_table_
.MaybeCreateTable() ||
295 !access_table_
.MaybeCreateTable() ||
296 !shortcut_table_
.MaybeCreateTable()) {
300 // set the current version
301 if (!version_table_
.SetInt(kVersionKeyName
, kCurrentVersion
)) {
305 return transaction
.Commit();
308 bool PermissionsDB::UpgradeToVersion2() {
309 SQLTransaction
transaction(&db_
, "PermissionsDB::UpgradeToVersion2");
310 if (!transaction
.Begin()) {
315 version_table_
.GetInt(kVersionKeyName
, &version
);
318 LOG(("PermissionsDB::UpgradeToVersion2 unexpected version: %d", version
));
322 // There was a bug in v1 of this db where we inserted some corrupt UTF-8
323 // characters into the db. This was pre-release, so it's not worth trying
324 // to clean it up. Instead just remove old permissions.
326 // TODO(shess) I'm inclined to say "DROP TABLE IF EXISTS
327 // ScourAccess". Or, since this was from a pre-release schema,
328 // "upgrade" version 1 by calling CreateDatabase(), which will drop
329 // all existing tables.
330 if (SQLITE_OK
!= db_
.Execute("DELETE FROM ScourAccess")) {
334 if (!version_table_
.SetInt(kVersionKeyName
, 2)) {
338 return transaction
.Commit();
341 bool PermissionsDB::UpgradeToVersion3() {
342 SQLTransaction
transaction(&db_
, "PermissionsDB::UpgradeToVersion3");
343 if (!transaction
.Begin()) {
348 version_table_
.GetInt(kVersionKeyName
, &version
);
351 if (!UpgradeToVersion2()) {
354 version_table_
.GetInt(kVersionKeyName
, &version
);
358 LOG(("PermissionsDB::UpgradeToVersion3 unexpected version: %d", version
));
362 if (!shortcut_table_
.UpgradeToVersion3()) {
366 if (!version_table_
.SetInt(kVersionKeyName
, 3)) {
370 return transaction
.Commit();
373 bool PermissionsDB::UpgradeToVersion4() {
374 SQLTransaction
transaction(&db_
, "PermissionsDB::UpgradeToVersion4");
375 if (!transaction
.Begin()) {
380 version_table_
.GetInt(kVersionKeyName
, &version
);
383 if (!UpgradeToVersion3()) {
386 version_table_
.GetInt(kVersionKeyName
, &version
);
390 LOG(("PermissionsDB::UpgradeToVersion4 unexpected version: %d", version
));
394 if (!shortcut_table_
.UpgradeFromVersion3ToVersion4()) {
398 if (!version_table_
.SetInt(kVersionKeyName
, 4)) {
402 return transaction
.Commit();