From 2b2df42abb372f4e0e36098bfc358e20e8047828 Mon Sep 17 00:00:00 2001 From: "tony%ponderer.org" Date: Tue, 1 Aug 2006 02:01:40 +0000 Subject: [PATCH] Bug 346184: If urlclassifier.sqlite is removed, db is not repopulated patch: check db tables before sending an update request r=darin --- .../url-classifier/content/listmanager.js | 92 ++++------- .../public/nsIUrlClassifierDBService.idl | 10 +- .../src/nsUrlClassifierDBService.cpp | 149 ++++++++++-------- 3 files changed, 128 insertions(+), 123 deletions(-) diff --git a/toolkit/components/url-classifier/content/listmanager.js b/toolkit/components/url-classifier/content/listmanager.js index 48e39044aa6..00624078849 100644 --- a/toolkit/components/url-classifier/content/listmanager.js +++ b/toolkit/components/url-classifier/content/listmanager.js @@ -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) { diff --git a/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl b/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl index c60ad03be3e..c6102c027d8 100644 --- a/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl +++ b/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl @@ -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 diff --git a/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp index fe4f3b1a616..1b5451d30da 100644 --- a/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp @@ -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 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 wrapper = - new nsUrlClassifierCallbackWrapper(c); - NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY); - nsresult rv; // The proxy callback uses the current thread. nsCOMPtr 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 wrapper = - new nsUrlClassifierCallbackWrapper(c); - NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY); - nsresult rv; // The proxy callback uses the current thread. nsCOMPtr 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 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 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 wrapper = - new nsUrlClassifierCallbackWrapper(c); - NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY); - nsresult rv; // The proxy callback uses the current thread. nsCOMPtr 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);