This source file includes following definitions.
- InitDB
- special_storage_policy_
- SetValidityPeriodForTesting
- identity
- SqlLiteStorage
- sql_lite_storage_
- FindIdentity
- AddIdentity
- Close
- DeleteBetween
- SetValidityPeriodForTesting
- OnLoaded
- Load
- Close
- AddIdentity
- DeleteIdentity
- DeleteBetween
- OnDatabaseError
- BatchOperation
- Commit
#include "content/browser/media/webrtc_identity_store_backend.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "sql/error_delegate_util.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "url/gurl.h"
#include "webkit/browser/quota/special_storage_policy.h"
namespace content {
static const char* kWebRTCIdentityStoreDBName = "webrtc_identity_store";
static const base::FilePath::CharType kWebRTCIdentityStoreDirectory[] =
FILE_PATH_LITERAL("WebRTCIdentityStore");
static bool InitDB(sql::Connection* db) {
if (db->DoesTableExist(kWebRTCIdentityStoreDBName)) {
if (db->DoesColumnExist(kWebRTCIdentityStoreDBName, "origin") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "identity_name") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "common_name") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "certificate") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "private_key") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "creation_time"))
return true;
if (!db->Execute("DROP TABLE webrtc_identity_store"))
return false;
}
return db->Execute(
"CREATE TABLE webrtc_identity_store"
" ("
"origin TEXT NOT NULL,"
"identity_name TEXT NOT NULL,"
"common_name TEXT NOT NULL,"
"certificate BLOB NOT NULL,"
"private_key BLOB NOT NULL,"
"creation_time INTEGER)");
}
struct WebRTCIdentityStoreBackend::IdentityKey {
IdentityKey(const GURL& origin, const std::string& identity_name)
: origin(origin), identity_name(identity_name) {}
bool operator<(const IdentityKey& other) const {
return origin < other.origin ||
(origin == other.origin && identity_name < other.identity_name);
}
GURL origin;
std::string identity_name;
};
struct WebRTCIdentityStoreBackend::Identity {
Identity(const std::string& common_name,
const std::string& certificate,
const std::string& private_key)
: common_name(common_name),
certificate(certificate),
private_key(private_key),
creation_time(base::Time::Now().ToInternalValue()) {}
Identity(const std::string& common_name,
const std::string& certificate,
const std::string& private_key,
int64 creation_time)
: common_name(common_name),
certificate(certificate),
private_key(private_key),
creation_time(creation_time) {}
std::string common_name;
std::string certificate;
std::string private_key;
int64 creation_time;
};
struct WebRTCIdentityStoreBackend::PendingFindRequest {
PendingFindRequest(const GURL& origin,
const std::string& identity_name,
const std::string& common_name,
const FindIdentityCallback& callback)
: origin(origin),
identity_name(identity_name),
common_name(common_name),
callback(callback) {}
~PendingFindRequest() {}
GURL origin;
std::string identity_name;
std::string common_name;
FindIdentityCallback callback;
};
class WebRTCIdentityStoreBackend::SqlLiteStorage
: public base::RefCountedThreadSafe<SqlLiteStorage> {
public:
SqlLiteStorage(base::TimeDelta validity_period,
const base::FilePath& path,
quota::SpecialStoragePolicy* policy)
: validity_period_(validity_period), special_storage_policy_(policy) {
if (!path.empty())
path_ = path.Append(kWebRTCIdentityStoreDirectory);
}
void Load(IdentityMap* out_map);
void Close();
void AddIdentity(const GURL& origin,
const std::string& identity_name,
const Identity& identity);
void DeleteIdentity(const GURL& origin,
const std::string& identity_name,
const Identity& identity);
void DeleteBetween(base::Time delete_begin, base::Time delete_end);
void SetValidityPeriodForTesting(base::TimeDelta validity_period) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
DCHECK(!db_.get());
validity_period_ = validity_period;
}
private:
friend class base::RefCountedThreadSafe<SqlLiteStorage>;
enum OperationType {
ADD_IDENTITY,
DELETE_IDENTITY
};
struct PendingOperation {
PendingOperation(OperationType type,
const GURL& origin,
const std::string& identity_name,
const Identity& identity)
: type(type),
origin(origin),
identity_name(identity_name),
identity(identity) {}
OperationType type;
GURL origin;
std::string identity_name;
Identity identity;
};
typedef std::vector<PendingOperation*> PendingOperationList;
virtual ~SqlLiteStorage() {}
void OnDatabaseError(int error, sql::Statement* stmt);
void BatchOperation(OperationType type,
const GURL& origin,
const std::string& identity_name,
const Identity& identity);
void Commit();
base::TimeDelta validity_period_;
base::FilePath path_;
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
scoped_ptr<sql::Connection> db_;
PendingOperationList pending_operations_;
DISALLOW_COPY_AND_ASSIGN(SqlLiteStorage);
};
WebRTCIdentityStoreBackend::WebRTCIdentityStoreBackend(
const base::FilePath& path,
quota::SpecialStoragePolicy* policy,
base::TimeDelta validity_period)
: validity_period_(validity_period),
state_(NOT_STARTED),
sql_lite_storage_(new SqlLiteStorage(validity_period, path, policy)) {}
bool WebRTCIdentityStoreBackend::FindIdentity(
const GURL& origin,
const std::string& identity_name,
const std::string& common_name,
const FindIdentityCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_ == CLOSED)
return false;
if (state_ != LOADED) {
pending_find_requests_.push_back(
new PendingFindRequest(origin, identity_name, common_name, callback));
if (state_ == LOADING)
return true;
DCHECK_EQ(state_, NOT_STARTED);
scoped_ptr<IdentityMap> out_map(new IdentityMap());
base::Closure task(
base::Bind(&SqlLiteStorage::Load, sql_lite_storage_, out_map.get()));
if (BrowserThread::PostTaskAndReply(
BrowserThread::DB,
FROM_HERE,
task,
base::Bind(&WebRTCIdentityStoreBackend::OnLoaded,
this,
base::Passed(&out_map)))) {
state_ = LOADING;
return true;
}
}
IdentityKey key(origin, identity_name);
IdentityMap::iterator iter = identities_.find(key);
if (iter != identities_.end() && iter->second.common_name == common_name) {
base::TimeDelta age = base::Time::Now() - base::Time::FromInternalValue(
iter->second.creation_time);
if (age < validity_period_) {
return BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(callback,
net::OK,
iter->second.certificate,
iter->second.private_key));
}
identities_.erase(iter);
}
return BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(callback, net::ERR_FILE_NOT_FOUND, "", ""));
}
void WebRTCIdentityStoreBackend::AddIdentity(const GURL& origin,
const std::string& identity_name,
const std::string& common_name,
const std::string& certificate,
const std::string& private_key) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_ == CLOSED)
return;
IdentityKey key(origin, identity_name);
Identity identity(common_name, certificate, private_key);
if (identities_.find(key) != identities_.end()) {
if (!BrowserThread::PostTask(BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::DeleteIdentity,
sql_lite_storage_,
origin,
identity_name,
identities_.find(key)->second)))
return;
}
identities_.insert(std::pair<IdentityKey, Identity>(key, identity));
BrowserThread::PostTask(BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::AddIdentity,
sql_lite_storage_,
origin,
identity_name,
identity));
}
void WebRTCIdentityStoreBackend::Close() {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&WebRTCIdentityStoreBackend::Close, this));
return;
}
if (state_ == CLOSED)
return;
state_ = CLOSED;
BrowserThread::PostTask(
BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::Close, sql_lite_storage_));
}
void WebRTCIdentityStoreBackend::DeleteBetween(base::Time delete_begin,
base::Time delete_end,
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_ == CLOSED)
return;
IdentityMap::iterator it = identities_.begin();
while (it != identities_.end()) {
if (it->second.creation_time >= delete_begin.ToInternalValue() &&
it->second.creation_time <= delete_end.ToInternalValue()) {
identities_.erase(it++);
} else {
++it;
}
}
BrowserThread::PostTaskAndReply(BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::DeleteBetween,
sql_lite_storage_,
delete_begin,
delete_end),
callback);
}
void WebRTCIdentityStoreBackend::SetValidityPeriodForTesting(
base::TimeDelta validity_period) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
validity_period_ = validity_period;
BrowserThread::PostTask(
BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::SetValidityPeriodForTesting,
sql_lite_storage_,
validity_period));
}
WebRTCIdentityStoreBackend::~WebRTCIdentityStoreBackend() {}
void WebRTCIdentityStoreBackend::OnLoaded(scoped_ptr<IdentityMap> out_map) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_ != LOADING)
return;
DVLOG(2) << "WebRTC identity store has loaded.";
state_ = LOADED;
identities_.swap(*out_map);
for (size_t i = 0; i < pending_find_requests_.size(); ++i) {
FindIdentity(pending_find_requests_[i]->origin,
pending_find_requests_[i]->identity_name,
pending_find_requests_[i]->common_name,
pending_find_requests_[i]->callback);
delete pending_find_requests_[i];
}
pending_find_requests_.clear();
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
DCHECK(!db_.get());
const base::FilePath dir = path_.DirName();
if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
DLOG(ERROR) << "Unable to open DB file path.";
return;
}
db_.reset(new sql::Connection());
db_->set_error_callback(base::Bind(&SqlLiteStorage::OnDatabaseError, this));
if (!db_->Open(path_)) {
DLOG(ERROR) << "Unable to open DB.";
db_.reset();
return;
}
if (!InitDB(db_.get())) {
DLOG(ERROR) << "Unable to init DB.";
db_.reset();
return;
}
db_->Preload();
DeleteBetween(base::Time(), base::Time::Now() - validity_period_);
sql::Statement stmt(db_->GetUniqueStatement(
"SELECT origin, identity_name, common_name, "
"certificate, private_key, creation_time "
"FROM webrtc_identity_store"));
CHECK(stmt.is_valid());
while (stmt.Step()) {
IdentityKey key(GURL(stmt.ColumnString(0)), stmt.ColumnString(1));
std::string common_name(stmt.ColumnString(2));
std::string cert, private_key;
stmt.ColumnBlobAsString(3, &cert);
stmt.ColumnBlobAsString(4, &private_key);
int64 creation_time = stmt.ColumnInt64(5);
std::pair<IdentityMap::iterator, bool> result =
out_map->insert(std::pair<IdentityKey, Identity>(
key, Identity(common_name, cert, private_key, creation_time)));
DCHECK(result.second);
}
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::Close() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
Commit();
db_.reset();
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::AddIdentity(
const GURL& origin,
const std::string& identity_name,
const Identity& identity) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!db_.get())
return;
if (special_storage_policy_.get() &&
!special_storage_policy_->IsStorageProtected(origin) &&
special_storage_policy_->IsStorageSessionOnly(origin)) {
return;
}
BatchOperation(ADD_IDENTITY, origin, identity_name, identity);
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteIdentity(
const GURL& origin,
const std::string& identity_name,
const Identity& identity) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!db_.get())
return;
BatchOperation(DELETE_IDENTITY, origin, identity_name, identity);
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteBetween(
base::Time delete_begin,
base::Time delete_end) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!db_.get())
return;
Commit();
sql::Statement del_stmt(db_->GetCachedStatement(
SQL_FROM_HERE,
"DELETE FROM webrtc_identity_store"
" WHERE creation_time >= ? AND creation_time <= ?"));
CHECK(del_stmt.is_valid());
del_stmt.BindInt64(0, delete_begin.ToInternalValue());
del_stmt.BindInt64(1, delete_end.ToInternalValue());
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
DLOG(ERROR) << "Failed to begin the transaction.";
return;
}
CHECK(del_stmt.Run());
transaction.Commit();
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::OnDatabaseError(
int error,
sql::Statement* stmt) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!sql::IsErrorCatastrophic(error))
return;
db_->RazeAndClose();
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::BatchOperation(
OperationType type,
const GURL& origin,
const std::string& identity_name,
const Identity& identity) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
static const base::TimeDelta kCommitInterval(
base::TimeDelta::FromSeconds(30));
static const size_t kCommitAfterBatchSize = 512;
scoped_ptr<PendingOperation> operation(
new PendingOperation(type, origin, identity_name, identity));
pending_operations_.push_back(operation.release());
if (pending_operations_.size() == 1) {
BrowserThread::PostDelayedTask(BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::Commit, this),
kCommitInterval);
} else if (pending_operations_.size() >= kCommitAfterBatchSize) {
BrowserThread::PostTask(BrowserThread::DB,
FROM_HERE,
base::Bind(&SqlLiteStorage::Commit, this));
}
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!db_.get() || pending_operations_.empty())
return;
sql::Statement add_stmt(db_->GetCachedStatement(
SQL_FROM_HERE,
"INSERT INTO webrtc_identity_store "
"(origin, identity_name, common_name, certificate,"
" private_key, creation_time) VALUES"
" (?,?,?,?,?,?)"));
CHECK(add_stmt.is_valid());
sql::Statement del_stmt(db_->GetCachedStatement(
SQL_FROM_HERE,
"DELETE FROM webrtc_identity_store WHERE origin=? AND identity_name=?"));
CHECK(del_stmt.is_valid());
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
DLOG(ERROR) << "Failed to begin the transaction.";
return;
}
for (PendingOperationList::iterator it = pending_operations_.begin();
it != pending_operations_.end();
++it) {
scoped_ptr<PendingOperation> po(*it);
switch (po->type) {
case ADD_IDENTITY: {
add_stmt.Reset(true);
add_stmt.BindString(0, po->origin.spec());
add_stmt.BindString(1, po->identity_name);
add_stmt.BindString(2, po->identity.common_name);
const std::string& cert = po->identity.certificate;
add_stmt.BindBlob(3, cert.data(), cert.size());
const std::string& private_key = po->identity.private_key;
add_stmt.BindBlob(4, private_key.data(), private_key.size());
add_stmt.BindInt64(5, po->identity.creation_time);
CHECK(add_stmt.Run());
break;
}
case DELETE_IDENTITY:
del_stmt.Reset(true);
del_stmt.BindString(0, po->origin.spec());
del_stmt.BindString(1, po->identity_name);
CHECK(del_stmt.Run());
break;
default:
NOTREACHED();
break;
}
}
transaction.Commit();
pending_operations_.clear();
}
}