Bug 346184: If urlclassifier.sqlite is removed, db is not repopulated

patch: check db tables before sending an update request
r=darin
This commit is contained in:
tony%ponderer.org 2006-08-01 02:01:40 +00:00
Родитель da606dc502
Коммит 2b2df42abb
3 изменённых файлов: 128 добавлений и 123 удалений

Просмотреть файл

@ -439,6 +439,35 @@ PROT_ListManager.prototype.checkForUpdates = function() {
G_Debug(this, 'checkForUpdates: no update server url');
return false;
}
// Check to make sure our tables still exist (maybe the db got corrupted or
// the user deleted the file). If not, we need to reset the table version
// before sending the update check.
var tableNames = [];
for (var tableName in this.tablesKnown_) {
tableNames.push(tableName);
}
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
.getService(Ci.nsIUrlClassifierDBService);
dbService.checkTables(tableNames.join(","),
BindToObject(this.makeUpdateRequest_, this));
return true;
}
/**
* Method that fires the actual HTTP update request.
* First we reset any tables that have disappeared.
* @param tableNames String comma separated list of tables that
* don't exist
*/
PROT_ListManager.prototype.makeUpdateRequest_ = function(tableNames) {
// Clear prefs that track table version if they no longer exist in the db.
var tables = tableNames.split(",");
for (var i = 0; i < tables.length; ++i) {
G_Debug(this, "Table |" + tables[i] + "| no longer exists, clearing pref.");
this.prefs_.clearPref(kTableVersionPrefPrefix + tables[i]);
}
// Ok, now reload the table version.
this.loadTableVersions_();
G_Debug(this, 'checkForUpdates: scheduling request..');
@ -449,69 +478,12 @@ PROT_ListManager.prototype.checkForUpdates = function() {
streamer.updateUrl = url;
} catch (e) {
G_Debug(this, 'invalid url');
return false;
return;
}
return streamer.downloadUpdates(BindToObject(this.setTableVersion_, this));
}
/**
* Given the server response, extract out the new table lines and table
* version numbers. If the table has a mac, also check to see if it matches
* the data.
*
* @param data String update string from the server
* @return String The same update string sans tables with invalid macs.
*/
PROT_ListManager.prototype.checkMac_ = function(data) {
var dataTables = data.split('\n\n');
var returnString = "";
for (var table = null, t = 0; table = dataTables[t]; ++t) {
var firstLineEnd = table.indexOf("\n");
while (firstLineEnd == 0) {
// Skip leading blank lines
table = table.substring(1);
firstLineEnd = table.indexOf("\n");
}
if (firstLineEnd == -1) {
continue;
}
var versionLine = table.substring(0, firstLineEnd);
var versionParser = new PROT_VersionParser("dummy");
if (!versionParser.fromString(versionLine)) {
// Failed to parse the version string, skip this table.
G_Debug(this, "Failed to parse version string");
continue;
}
if (versionParser.mac && versionParser.macval.length > 0) {
// Includes a mac, so we check it.
var updateData = table.substring(firstLineEnd + 1) + '\n';
if (!this.urlCrypto_)
this.urlCrypto_ = new PROT_UrlCrypto();
var computedMac = this.urlCrypto_.computeMac(updateData);
if (computedMac != versionParser.macval) {
G_Debug(this, "mac doesn't match: " + computedMac + " != " +
versionParser.macval)
continue;
}
} else {
// No mac in the return. Check to see if it's required. If it is
// required, skip this data.
if (this.tablesKnown_[versionParser.type] &&
this.tablesKnown_[versionParser.type].requireMac) {
continue;
}
}
// Everything looks ok, add it to the return string.
returnString += table + "\n\n";
if (!streamer.downloadUpdates(BindToObject(this.setTableVersion_, this))) {
G_Debug(this, "pending update, wait until later");
}
return returnString;
}
PROT_ListManager.prototype.QueryInterface = function(iid) {

Просмотреть файл

@ -49,7 +49,7 @@ interface nsIUrlClassifierCallback : nsISupports {
* It provides async methods for querying and updating the database. As the
* methods complete, they call the callback function.
*/
[scriptable, uuid(61759a52-fbbb-4c5e-b1df-fce67b6d10c8)]
[scriptable, uuid(211d5360-4af6-4a1d-99c1-926d35861eaf)]
interface nsIUrlClassifierDBService : nsISupports
{
/**
@ -61,6 +61,14 @@ interface nsIUrlClassifierDBService : nsISupports
void exists(in ACString tableName, in ACString key,
in nsIUrlClassifierCallback c);
/**
* Checks to see if the tables exist. tableNames is a comma separated list
* of table names to check. The callback is called with a comma separated
* list of tables that no longer exist (either the db is corrupted or the
* user deleted the file).
*/
void checkTables(in ACString tableNames, in nsIUrlClassifierCallback c);
/**
* Updates the table in the background. Calls callback after each table
* completes processing with the new table line as the parameter. This

Просмотреть файл

@ -72,37 +72,6 @@ static nsUrlClassifierDBService* sUrlClassifierDBService;
// Thread that we do the updates on.
static nsIThread* gDbBackgroundThread = nsnull;
// -------------------------------------------------------------------------
// Wrapper for JS-implemented nsIUrlClassifierCallback that protects against
// bug 337492. We should be able to remove this code once that bug is fixed.
#include "nsProxyRelease.h"
class nsUrlClassifierCallbackWrapper : public nsIUrlClassifierCallback
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_NSIURLCLASSIFIERCALLBACK(mInner->)
nsUrlClassifierCallbackWrapper(nsIUrlClassifierCallback *inner)
: mInner(inner)
{
NS_ADDREF(mInner);
}
~nsUrlClassifierCallbackWrapper()
{
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_ProxyRelease(mainThread, mInner);
}
private:
nsIUrlClassifierCallback *mInner;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierCallbackWrapper,
nsIUrlClassifierCallback)
// -------------------------------------------------------------------------
// Actual worker implemenatation
class nsUrlClassifierDBServiceWorker : public nsIUrlClassifierDBServiceWorker
@ -134,14 +103,14 @@ private:
// Handle a new table line of the form [table-name #.####]. We create the
// table if it doesn't exist and set the aTableName, aUpdateStatement,
// and aDeleteStatement.
nsresult ProcessNewTable(const nsDependentCSubstring& aLine,
nsresult ProcessNewTable(const nsCSubstring& aLine,
nsCString* aTableName,
mozIStorageStatement** aUpdateStatement,
mozIStorageStatement** aDeleteStatement);
// Handle an add or remove line. We execute additional update or delete
// statements.
nsresult ProcessUpdateTable(const nsDependentCSubstring& aLine,
nsresult ProcessUpdateTable(const nsCSubstring& aLine,
const nsCString& aTableName,
mozIStorageStatement* aUpdateStatement,
mozIStorageStatement* aDeleteStatement);
@ -224,6 +193,48 @@ nsUrlClassifierDBServiceWorker::Exists(const nsACString& tableName,
return NS_OK;
}
// We get a comma separated list of table names. For each table that doesn't
// exist, we return it in a comma separated list via the callback.
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::CheckTables(const nsACString & tableNames,
nsIUrlClassifierCallback *c)
{
nsresult rv = OpenDb();
if (NS_FAILED(rv)) {
NS_ERROR("Unable to open database");
return NS_ERROR_FAILURE;
}
nsCAutoString changedTables;
// tablesNames is a comma separated list, so get each table name out for
// checking.
PRUint32 cur = 0;
PRInt32 next;
while (cur < tableNames.Length()) {
next = tableNames.FindChar(',', cur);
if (kNotFound == next) {
next = tableNames.Length();
}
const nsCSubstring &tableName = Substring(tableNames, cur, next - cur);
cur = next + 1;
nsCString dbTableName;
GetDbTableName(tableName, &dbTableName);
PRBool exists;
nsresult rv = mConnection->TableExists(dbTableName, &exists);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists) {
if (changedTables.Length() > 0)
changedTables.Append(",");
changedTables.Append(tableName);
}
}
c->HandleEvent(changedTables);
return NS_OK;
}
// Do a batch update of the database. After we complete processing a table,
// we call the callback with the table line.
NS_IMETHODIMP
@ -252,8 +263,7 @@ nsUrlClassifierDBServiceWorker::UpdateTables(const nsACString& updateString,
while(cur < updateString.Length() &&
(next = updateString.FindChar('\n', cur)) != kNotFound) {
count ++;
const nsDependentCSubstring &line = Substring(updateString,
cur, next - cur);
const nsCSubstring &line = Substring(updateString, cur, next - cur);
cur = next + 1; // prepare for next run
// Skip blank lines
@ -319,7 +329,7 @@ nsUrlClassifierDBServiceWorker::Update(const nsACString& chunk)
} else {
PRUint32 numTables = mTableUpdateLines.Length();
if (numTables > 0) {
const nsDependentCSubstring &line = Substring(
const nsCSubstring &line = Substring(
mTableUpdateLines[numTables - 1], 0);
rv = ProcessNewTable(line, &dbTableName,
@ -333,8 +343,7 @@ nsUrlClassifierDBServiceWorker::Update(const nsACString& chunk)
PRInt32 next;
while(cur < updateString.Length() &&
(next = updateString.FindChar('\n', cur)) != kNotFound) {
const nsDependentCSubstring &line = Substring(updateString,
cur, next - cur);
const nsCSubstring &line = Substring(updateString, cur, next - cur);
cur = next + 1; // prepare for next run
// Skip blank lines
@ -415,7 +424,7 @@ nsUrlClassifierDBServiceWorker::CloseDb()
nsresult
nsUrlClassifierDBServiceWorker::ProcessNewTable(
const nsDependentCSubstring& aLine,
const nsCSubstring& aLine,
nsCString* aDbTableName,
mozIStorageStatement** aUpdateStatement,
mozIStorageStatement** aDeleteStatement)
@ -427,7 +436,7 @@ nsUrlClassifierDBServiceWorker::ProcessNewTable(
return NS_ERROR_FAILURE;
}
const nsDependentCSubstring &tableName = Substring(aLine, 1, spacePos - 1);
const nsCSubstring &tableName = Substring(aLine, 1, spacePos - 1);
GetDbTableName(tableName, aDbTableName);
// Create the table
@ -456,7 +465,7 @@ nsUrlClassifierDBServiceWorker::ProcessNewTable(
nsresult
nsUrlClassifierDBServiceWorker::ProcessUpdateTable(
const nsDependentCSubstring& aLine,
const nsCSubstring& aLine,
const nsCString& aTableName,
mozIStorageStatement* aUpdateStatement,
mozIStorageStatement* aDeleteStatement)
@ -479,8 +488,8 @@ nsUrlClassifierDBServiceWorker::ProcessUpdateTable(
if ('+' == op && spacePos != kNotFound) {
// Insert operation of the form "+KEY\tVALUE"
const nsDependentCSubstring &key = Substring(aLine, 1, spacePos - 1);
const nsDependentCSubstring &value = Substring(aLine, spacePos + 1);
const nsCSubstring &key = Substring(aLine, 1, spacePos - 1);
const nsCSubstring &value = Substring(aLine, spacePos + 1);
aUpdateStatement->BindUTF8StringParameter(0, key);
aUpdateStatement->BindUTF8StringParameter(1, value);
@ -490,11 +499,11 @@ nsUrlClassifierDBServiceWorker::ProcessUpdateTable(
// Remove operation of the form "-KEY"
if (spacePos == kNotFound) {
// No trailing tab
const nsDependentCSubstring &key = Substring(aLine, 1);
const nsCSubstring &key = Substring(aLine, 1);
aDeleteStatement->BindUTF8StringParameter(0, key);
} else {
// With trailing tab
const nsDependentCSubstring &key = Substring(aLine, 1, spacePos - 1);
const nsCSubstring &key = Substring(aLine, 1, spacePos - 1);
aDeleteStatement->BindUTF8StringParameter(0, key);
}
@ -640,16 +649,12 @@ nsUrlClassifierDBService::Exists(const nsACString& tableName,
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIUrlClassifierCallback> wrapper =
new nsUrlClassifierCallbackWrapper(c);
NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
NS_GET_IID(nsIUrlClassifierCallback),
wrapper,
c,
NS_PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);
@ -667,21 +672,45 @@ nsUrlClassifierDBService::Exists(const nsACString& tableName,
}
NS_IMETHODIMP
nsUrlClassifierDBService::UpdateTables(const nsACString& updateString,
nsIUrlClassifierCallback *c)
nsUrlClassifierDBService::CheckTables(const nsACString & tableNames,
nsIUrlClassifierCallback *c)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIUrlClassifierCallback> wrapper =
new nsUrlClassifierCallbackWrapper(c);
NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
NS_GET_IID(nsIUrlClassifierCallback),
wrapper,
c,
NS_PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);
// The actual worker uses the background thread.
nsCOMPtr<nsIUrlClassifierDBServiceWorker> proxy;
rv = NS_GetProxyForObject(gDbBackgroundThread,
NS_GET_IID(nsIUrlClassifierDBServiceWorker),
mWorker,
NS_PROXY_ASYNC,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
return proxy->CheckTables(tableNames, proxyCallback);
}
NS_IMETHODIMP
nsUrlClassifierDBService::UpdateTables(const nsACString& updateString,
nsIUrlClassifierCallback *c)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
NS_GET_IID(nsIUrlClassifierCallback),
c,
NS_PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);
@ -722,16 +751,12 @@ nsUrlClassifierDBService::Finish(nsIUrlClassifierCallback *c)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIUrlClassifierCallback> wrapper =
new nsUrlClassifierCallbackWrapper(c);
NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
NS_GET_IID(nsIUrlClassifierCallback),
wrapper,
c,
NS_PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);