toolkit/components/url-classifier/nsUrlClassifierDBService.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsAutoPtr.h"
     7 #include "nsCOMPtr.h"
     8 #include "nsAppDirectoryServiceDefs.h"
     9 #include "nsCRT.h"
    10 #include "nsICryptoHash.h"
    11 #include "nsICryptoHMAC.h"
    12 #include "nsIDirectoryService.h"
    13 #include "nsIKeyModule.h"
    14 #include "nsIObserverService.h"
    15 #include "nsIPermissionManager.h"
    16 #include "nsIPrefBranch.h"
    17 #include "nsIPrefService.h"
    18 #include "nsIProperties.h"
    19 #include "nsToolkitCompsCID.h"
    20 #include "nsIUrlClassifierUtils.h"
    21 #include "nsUrlClassifierDBService.h"
    22 #include "nsUrlClassifierUtils.h"
    23 #include "nsUrlClassifierProxies.h"
    24 #include "nsURILoader.h"
    25 #include "nsString.h"
    26 #include "nsReadableUtils.h"
    27 #include "nsTArray.h"
    28 #include "nsNetUtil.h"
    29 #include "nsNetCID.h"
    30 #include "nsThreadUtils.h"
    31 #include "nsXPCOMStrings.h"
    32 #include "nsProxyRelease.h"
    33 #include "nsString.h"
    34 #include "mozilla/Atomics.h"
    35 #include "mozilla/DebugOnly.h"
    36 #include "mozilla/Mutex.h"
    37 #include "mozilla/Preferences.h"
    38 #include "mozilla/TimeStamp.h"
    39 #include "mozilla/Telemetry.h"
    40 #include "prlog.h"
    41 #include "prprf.h"
    42 #include "prnetdb.h"
    43 #include "Entries.h"
    44 #include "mozilla/Attributes.h"
    45 #include "nsIPrincipal.h"
    46 #include "Classifier.h"
    47 #include "ProtocolParser.h"
    48 #include "nsContentUtils.h"
    50 using namespace mozilla;
    51 using namespace mozilla::safebrowsing;
    53 // NSPR_LOG_MODULES=UrlClassifierDbService:5
    54 #if defined(PR_LOGGING)
    55 PRLogModuleInfo *gUrlClassifierDbServiceLog = nullptr;
    56 #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
    57 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
    58 #else
    59 #define LOG(args)
    60 #define LOG_ENABLED() (false)
    61 #endif
    63 // Prefs for implementing nsIURIClassifier to block page loads
    64 #define CHECK_MALWARE_PREF      "browser.safebrowsing.malware.enabled"
    65 #define CHECK_MALWARE_DEFAULT   false
    67 #define CHECK_PHISHING_PREF     "browser.safebrowsing.enabled"
    68 #define CHECK_PHISHING_DEFAULT  false
    70 #define GETHASH_NOISE_PREF      "urlclassifier.gethashnoise"
    71 #define GETHASH_NOISE_DEFAULT   4
    73 // Comma-separated lists
    74 #define MALWARE_TABLE_PREF      "urlclassifier.malware_table"
    75 #define PHISH_TABLE_PREF        "urlclassifier.phish_table"
    76 #define DOWNLOAD_BLOCK_TABLE_PREF "urlclassifier.downloadBlockTable"
    77 #define DOWNLOAD_ALLOW_TABLE_PREF "urlclassifier.downloadAllowTable"
    78 #define DISALLOW_COMPLETION_TABLE_PREF "urlclassifier.disallow_completions"
    80 #define CONFIRM_AGE_PREF        "urlclassifier.max-complete-age"
    81 #define CONFIRM_AGE_DEFAULT_SEC (45 * 60)
    83 class nsUrlClassifierDBServiceWorker;
    85 // Singleton instance.
    86 static nsUrlClassifierDBService* sUrlClassifierDBService;
    88 nsIThread* nsUrlClassifierDBService::gDbBackgroundThread = nullptr;
    90 // Once we've committed to shutting down, don't do work in the background
    91 // thread.
    92 static bool gShuttingDownThread = false;
    94 static mozilla::Atomic<int32_t> gFreshnessGuarantee(CONFIRM_AGE_DEFAULT_SEC);
    96 // -------------------------------------------------------------------------
    97 // Actual worker implemenatation
    98 class nsUrlClassifierDBServiceWorker MOZ_FINAL :
    99   public nsIUrlClassifierDBServiceWorker
   100 {
   101 public:
   102   nsUrlClassifierDBServiceWorker();
   104   NS_DECL_THREADSAFE_ISUPPORTS
   105   NS_DECL_NSIURLCLASSIFIERDBSERVICE
   106   NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
   108   nsresult Init(uint32_t aGethashNoise, nsCOMPtr<nsIFile> aCacheDir);
   110   // Queue a lookup for the worker to perform, called in the main thread.
   111   // tables is a comma-separated list of tables to query
   112   nsresult QueueLookup(const nsACString& lookupKey,
   113                        const nsACString& tables,
   114                        nsIUrlClassifierLookupCallback* callback);
   116   // Handle any queued-up lookups.  We call this function during long-running
   117   // update operations to prevent lookups from blocking for too long.
   118   nsresult HandlePendingLookups();
   120 private:
   121   // No subclassing
   122   ~nsUrlClassifierDBServiceWorker();
   124   // Disallow copy constructor
   125   nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
   127   nsresult OpenDb();
   129   // Applies the current transaction and resets the update/working times.
   130   nsresult ApplyUpdate();
   132   // Reset the in-progress update stream
   133   void ResetStream();
   135   // Reset the in-progress update
   136   void ResetUpdate();
   138   // Perform a classifier lookup for a given url.
   139   nsresult DoLookup(const nsACString& spec,
   140                     const nsACString& tables,
   141                     nsIUrlClassifierLookupCallback* c);
   143   nsresult AddNoise(const Prefix aPrefix,
   144                     const nsCString tableName,
   145                     uint32_t aCount,
   146                     LookupResultArray& results);
   148   nsCOMPtr<nsICryptoHash> mCryptoHash;
   150   nsAutoPtr<Classifier> mClassifier;
   151   // The class that actually parses the update chunks.
   152   nsAutoPtr<ProtocolParser> mProtocolParser;
   154   // Directory where to store the SB databases.
   155   nsCOMPtr<nsIFile> mCacheDir;
   157   // XXX: maybe an array of autoptrs.  Or maybe a class specifically
   158   // storing a series of updates.
   159   nsTArray<TableUpdate*> mTableUpdates;
   161   int32_t mUpdateWait;
   163   // Entries that cannot be completed. We expect them to die at
   164   // the next update
   165   PrefixArray mMissCache;
   167   nsresult mUpdateStatus;
   168   nsTArray<nsCString> mUpdateTables;
   170   nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdateObserver;
   171   bool mInStream;
   173   // The number of noise entries to add to the set of lookup results.
   174   uint32_t mGethashNoise;
   176   // Pending lookups are stored in a queue for processing.  The queue
   177   // is protected by mPendingLookupLock.
   178   Mutex mPendingLookupLock;
   180   class PendingLookup {
   181   public:
   182     TimeStamp mStartTime;
   183     nsCString mKey;
   184     nsCString mTables;
   185     nsCOMPtr<nsIUrlClassifierLookupCallback> mCallback;
   186   };
   188   // list of pending lookups
   189   nsTArray<PendingLookup> mPendingLookups;
   190 };
   192 NS_IMPL_ISUPPORTS(nsUrlClassifierDBServiceWorker,
   193                   nsIUrlClassifierDBServiceWorker,
   194                   nsIUrlClassifierDBService)
   196 nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
   197   : mInStream(false)
   198   , mGethashNoise(0)
   199   , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock")
   200 {
   201 }
   203 nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
   204 {
   205   NS_ASSERTION(!mClassifier,
   206                "Db connection not closed, leaking memory!  Call CloseDb "
   207                "to close the connection.");
   208 }
   210 nsresult
   211 nsUrlClassifierDBServiceWorker::Init(uint32_t aGethashNoise,
   212                                      nsCOMPtr<nsIFile> aCacheDir)
   213 {
   214   mGethashNoise = aGethashNoise;
   215   mCacheDir = aCacheDir;
   217   ResetUpdate();
   219   return NS_OK;
   220 }
   222 nsresult
   223 nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec,
   224                                             const nsACString& tables,
   225                                             nsIUrlClassifierLookupCallback* callback)
   226 {
   227   MutexAutoLock lock(mPendingLookupLock);
   229   PendingLookup* lookup = mPendingLookups.AppendElement();
   230   if (!lookup) return NS_ERROR_OUT_OF_MEMORY;
   232   lookup->mStartTime = TimeStamp::Now();
   233   lookup->mKey = spec;
   234   lookup->mCallback = callback;
   235   lookup->mTables = tables;
   237   return NS_OK;
   238 }
   240 /**
   241  * Lookup up a key in the database is a two step process:
   242  *
   243  * a) First we look for any Entries in the database that might apply to this
   244  *    url.  For each URL there are one or two possible domain names to check:
   245  *    the two-part domain name (example.com) and the three-part name
   246  *    (www.example.com).  We check the database for both of these.
   247  * b) If we find any entries, we check the list of fragments for that entry
   248  *    against the possible subfragments of the URL as described in the
   249  *    "Simplified Regular Expression Lookup" section of the protocol doc.
   250  */
   251 nsresult
   252 nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
   253                                          const nsACString& tables,
   254                                          nsIUrlClassifierLookupCallback* c)
   255 {
   256   if (gShuttingDownThread) {
   257     c->LookupComplete(nullptr);
   258     return NS_ERROR_NOT_INITIALIZED;
   259   }
   261   nsresult rv = OpenDb();
   262   if (NS_FAILED(rv)) {
   263     c->LookupComplete(nullptr);
   264     NS_ERROR("Unable to open SafeBrowsing database.");
   265     return NS_ERROR_FAILURE;
   266   }
   268 #if defined(PR_LOGGING)
   269   PRIntervalTime clockStart = 0;
   270   if (LOG_ENABLED()) {
   271     clockStart = PR_IntervalNow();
   272   }
   273 #endif
   275   nsAutoPtr<LookupResultArray> results(new LookupResultArray());
   276   if (!results) {
   277     c->LookupComplete(nullptr);
   278     return NS_ERROR_OUT_OF_MEMORY;
   279   }
   281   // we ignore failures from Check because we'd rather return the
   282   // results that were found than fail.
   283   mClassifier->SetFreshTime(gFreshnessGuarantee);
   284   mClassifier->Check(spec, tables, *results);
   286   LOG(("Found %d results.", results->Length()));
   289 #if defined(PR_LOGGING)
   290   if (LOG_ENABLED()) {
   291     PRIntervalTime clockEnd = PR_IntervalNow();
   292     LOG(("query took %dms\n",
   293          PR_IntervalToMilliseconds(clockEnd - clockStart)));
   294   }
   295 #endif
   297   nsAutoPtr<LookupResultArray> completes(new LookupResultArray());
   299   for (uint32_t i = 0; i < results->Length(); i++) {
   300     if (!mMissCache.Contains(results->ElementAt(i).hash.prefix)) {
   301       completes->AppendElement(results->ElementAt(i));
   302     }
   303   }
   305   for (uint32_t i = 0; i < completes->Length(); i++) {
   306     if (!completes->ElementAt(i).Confirmed()) {
   307       // We're going to be doing a gethash request, add some extra entries.
   308       // Note that we cannot pass the first two by reference, because we
   309       // add to completes, whicah can cause completes to reallocate and move.
   310       AddNoise(completes->ElementAt(i).hash.prefix,
   311                completes->ElementAt(i).mTableName,
   312                mGethashNoise, *completes);
   313       break;
   314     }
   315   }
   317   // At this point ownership of 'results' is handed to the callback.
   318   c->LookupComplete(completes.forget());
   320   return NS_OK;
   321 }
   323 nsresult
   324 nsUrlClassifierDBServiceWorker::HandlePendingLookups()
   325 {
   326   MutexAutoLock lock(mPendingLookupLock);
   327   while (mPendingLookups.Length() > 0) {
   328     PendingLookup lookup = mPendingLookups[0];
   329     mPendingLookups.RemoveElementAt(0);
   330     {
   331       MutexAutoUnlock unlock(mPendingLookupLock);
   332       DoLookup(lookup.mKey, lookup.mTables, lookup.mCallback);
   333     }
   334     double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds();
   335     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME,
   336                           static_cast<uint32_t>(lookupTime));
   337   }
   339   return NS_OK;
   340 }
   342 nsresult
   343 nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix,
   344                                          const nsCString tableName,
   345                                          uint32_t aCount,
   346                                          LookupResultArray& results)
   347 {
   348   if (aCount < 1) {
   349     return NS_OK;
   350   }
   352   PrefixArray noiseEntries;
   353   nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName,
   354                                               aCount, &noiseEntries);
   355   NS_ENSURE_SUCCESS(rv, rv);
   357   for (uint32_t i = 0; i < noiseEntries.Length(); i++) {
   358     LookupResult *result = results.AppendElement();
   359     if (!result)
   360       return NS_ERROR_OUT_OF_MEMORY;
   362     result->hash.prefix = noiseEntries[i];
   363     result->mNoise = true;
   365     result->mTableName.Assign(tableName);
   366   }
   368   return NS_OK;
   369 }
   371 // Lookup a key in the db.
   372 NS_IMETHODIMP
   373 nsUrlClassifierDBServiceWorker::Lookup(nsIPrincipal* aPrincipal,
   374                                        const nsACString& aTables,
   375                                        nsIUrlClassifierCallback* c)
   376 {
   377   return HandlePendingLookups();
   378 }
   380 NS_IMETHODIMP
   381 nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c)
   382 {
   383   if (gShuttingDownThread)
   384     return NS_ERROR_NOT_INITIALIZED;
   386   nsresult rv = OpenDb();
   387   if (NS_FAILED(rv)) {
   388     NS_ERROR("Unable to open SafeBrowsing database");
   389     return NS_ERROR_FAILURE;
   390   }
   392   NS_ENSURE_SUCCESS(rv, rv);
   394   nsAutoCString response;
   395   mClassifier->TableRequest(response);
   396   c->HandleEvent(response);
   398   return rv;
   399 }
   401 void
   402 nsUrlClassifierDBServiceWorker::ResetStream()
   403 {
   404   LOG(("ResetStream"));
   405   mInStream = false;
   406   mProtocolParser = nullptr;
   407 }
   409 void
   410 nsUrlClassifierDBServiceWorker::ResetUpdate()
   411 {
   412   LOG(("ResetUpdate"));
   413   mUpdateWait = 0;
   414   mUpdateStatus = NS_OK;
   415   mUpdateObserver = nullptr;
   416 }
   418 NS_IMETHODIMP
   419 nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
   420                                                  nsIUrlClassifierHashCompleter *completer)
   421 {
   422   return NS_ERROR_NOT_IMPLEMENTED;
   423 }
   425 NS_IMETHODIMP
   426 nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
   427                                             const nsACString &tables)
   428 {
   429   LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
   431   if (gShuttingDownThread)
   432     return NS_ERROR_NOT_INITIALIZED;
   434   NS_ENSURE_STATE(!mUpdateObserver);
   436   nsresult rv = OpenDb();
   437   if (NS_FAILED(rv)) {
   438     NS_ERROR("Unable to open SafeBrowsing database");
   439     return NS_ERROR_FAILURE;
   440   }
   442   mUpdateStatus = NS_OK;
   443   mUpdateObserver = observer;
   444   Classifier::SplitTables(tables, mUpdateTables);
   446   return NS_OK;
   447 }
   449 // Called from the stream updater.
   450 NS_IMETHODIMP
   451 nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table)
   452 {
   453   LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
   455   if (gShuttingDownThread)
   456     return NS_ERROR_NOT_INITIALIZED;
   458   NS_ENSURE_STATE(mUpdateObserver);
   459   NS_ENSURE_STATE(!mInStream);
   461   mInStream = true;
   463   NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser.");
   465   mProtocolParser = new ProtocolParser();
   466   if (!mProtocolParser)
   467     return NS_ERROR_OUT_OF_MEMORY;
   469   mProtocolParser->Init(mCryptoHash);
   471   if (!table.IsEmpty()) {
   472     mProtocolParser->SetCurrentTable(table);
   473   }
   475   return NS_OK;
   476 }
   478 /**
   479  * Updating the database:
   480  *
   481  * The Update() method takes a series of chunks separated with control data,
   482  * as described in
   483  * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
   484  *
   485  * It will iterate through the control data until it reaches a chunk.  By
   486  * the time it reaches a chunk, it should have received
   487  * a) the table to which this chunk applies
   488  * b) the type of chunk (add, delete, expire add, expire delete).
   489  * c) the chunk ID
   490  * d) the length of the chunk.
   491  *
   492  * For add and subtract chunks, it needs to read the chunk data (expires
   493  * don't have any data).  Chunk data is a list of URI fragments whose
   494  * encoding depends on the type of table (which is indicated by the end
   495  * of the table name):
   496  * a) tables ending with -exp are a zlib-compressed list of URI fragments
   497  *    separated by newlines.
   498  * b) tables ending with -sha128 have the form
   499  *    [domain][N][frag0]...[fragN]
   500  *       16    1   16        16
   501  *    If N is 0, the domain is reused as a fragment.
   502  * c) any other tables are assumed to be a plaintext list of URI fragments
   503  *    separated by newlines.
   504  *
   505  * Update() can be fed partial data;  It will accumulate data until there is
   506  * enough to act on.  Finish() should be called when there will be no more
   507  * data.
   508  */
   509 NS_IMETHODIMP
   510 nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk)
   511 {
   512   if (gShuttingDownThread)
   513     return NS_ERROR_NOT_INITIALIZED;
   515   NS_ENSURE_STATE(mInStream);
   517   HandlePendingLookups();
   519   // Feed the chunk to the parser.
   520   return mProtocolParser->AppendStream(chunk);
   521 }
   523 NS_IMETHODIMP
   524 nsUrlClassifierDBServiceWorker::FinishStream()
   525 {
   526   if (gShuttingDownThread)
   527     return NS_ERROR_NOT_INITIALIZED;
   529   NS_ENSURE_STATE(mInStream);
   530   NS_ENSURE_STATE(mUpdateObserver);
   532   mInStream = false;
   534   if (NS_SUCCEEDED(mProtocolParser->Status())) {
   535     if (mProtocolParser->UpdateWait()) {
   536       mUpdateWait = mProtocolParser->UpdateWait();
   537     }
   538     // XXX: Only allow forwards from the initial update?
   539     const nsTArray<ProtocolParser::ForwardedUpdate> &forwards =
   540       mProtocolParser->Forwards();
   541     for (uint32_t i = 0; i < forwards.Length(); i++) {
   542       const ProtocolParser::ForwardedUpdate &forward = forwards[i];
   543       mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
   544     }
   545     // Hold on to any TableUpdate objects that were created by the
   546     // parser.
   547     mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
   548     mProtocolParser->ForgetTableUpdates();
   549   } else {
   550     mUpdateStatus = mProtocolParser->Status();
   551   }
   552   mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
   554   if (NS_SUCCEEDED(mUpdateStatus)) {
   555     if (mProtocolParser->ResetRequested()) {
   556       mClassifier->Reset();
   557    }
   558   }
   560   mProtocolParser = nullptr;
   562   return NS_OK;
   563 }
   565 NS_IMETHODIMP
   566 nsUrlClassifierDBServiceWorker::FinishUpdate()
   567 {
   568   if (gShuttingDownThread)
   569     return NS_ERROR_NOT_INITIALIZED;
   570   NS_ENSURE_STATE(mUpdateObserver);
   572   if (NS_SUCCEEDED(mUpdateStatus)) {
   573     mUpdateStatus = ApplyUpdate();
   574   }
   576   mMissCache.Clear();
   578   if (NS_SUCCEEDED(mUpdateStatus)) {
   579     LOG(("Notifying success: %d", mUpdateWait));
   580     mUpdateObserver->UpdateSuccess(mUpdateWait);
   581   } else {
   582     LOG(("Notifying error: %d", mUpdateStatus));
   583     mUpdateObserver->UpdateError(mUpdateStatus);
   584     /*
   585      * mark the tables as spoiled, we don't want to block hosts
   586      * longer than normal because our update failed
   587     */
   588     mClassifier->MarkSpoiled(mUpdateTables);
   589   }
   590   mUpdateObserver = nullptr;
   592   return NS_OK;
   593 }
   595 nsresult
   596 nsUrlClassifierDBServiceWorker::ApplyUpdate()
   597 {
   598   LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate()"));
   599   return mClassifier->ApplyUpdates(&mTableUpdates);
   600 }
   602 NS_IMETHODIMP
   603 nsUrlClassifierDBServiceWorker::ResetDatabase()
   604 {
   605   nsresult rv = OpenDb();
   607   if (NS_SUCCEEDED(rv)) {
   608     mClassifier->Reset();
   609   }
   611   rv = CloseDb();
   612   NS_ENSURE_SUCCESS(rv, rv);
   614   return NS_OK;
   615 }
   617 NS_IMETHODIMP
   618 nsUrlClassifierDBServiceWorker::CancelUpdate()
   619 {
   620   LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate"));
   622   if (mUpdateObserver) {
   623     LOG(("UpdateObserver exists, cancelling"));
   625     mUpdateStatus = NS_BINDING_ABORTED;
   627     mUpdateObserver->UpdateError(mUpdateStatus);
   629     /*
   630      * mark the tables as spoiled, we don't want to block hosts
   631      * longer than normal because our update failed
   632     */
   633     mClassifier->MarkSpoiled(mUpdateTables);
   635     ResetStream();
   636     ResetUpdate();
   637   } else {
   638     LOG(("No UpdateObserver, nothing to cancel"));
   639   }
   641   return NS_OK;
   642 }
   644 // Allows the main thread to delete the connection which may be in
   645 // a background thread.
   646 // XXX This could be turned into a single shutdown event so the logic
   647 // is simpler in nsUrlClassifierDBService::Shutdown.
   648 NS_IMETHODIMP
   649 nsUrlClassifierDBServiceWorker::CloseDb()
   650 {
   651   if (mClassifier) {
   652     mClassifier->Close();
   653     mClassifier = nullptr;
   654   }
   656   mCryptoHash = nullptr;
   657   LOG(("urlclassifier db closed\n"));
   659   return NS_OK;
   660 }
   662 NS_IMETHODIMP
   663 nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results)
   664 {
   665   LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this));
   666   if (!mClassifier)
   667     return NS_OK;
   669   // Ownership is transferred in to us
   670   nsAutoPtr<CacheResultArray> resultsPtr(results);
   672   nsAutoPtr<ProtocolParser> pParse(new ProtocolParser());
   673   nsTArray<TableUpdate*> updates;
   675   // Only cache results for tables that we have, don't take
   676   // in tables we might accidentally have hit during a completion.
   677   // This happens due to goog vs googpub lists existing.
   678   nsTArray<nsCString> tables;
   679   nsresult rv = mClassifier->ActiveTables(tables);
   680   NS_ENSURE_SUCCESS(rv, rv);
   682   for (uint32_t i = 0; i < resultsPtr->Length(); i++) {
   683     bool activeTable = false;
   684     for (uint32_t table = 0; table < tables.Length(); table++) {
   685       if (tables[table].Equals(resultsPtr->ElementAt(i).table)) {
   686         activeTable = true;
   687         break;
   688       }
   689     }
   690     if (activeTable) {
   691       TableUpdate * tu = pParse->GetTableUpdate(resultsPtr->ElementAt(i).table);
   692       LOG(("CacheCompletion Addchunk %d hash %X", resultsPtr->ElementAt(i).entry.addChunk,
   693            resultsPtr->ElementAt(i).entry.ToUint32()));
   694       tu->NewAddComplete(resultsPtr->ElementAt(i).entry.addChunk,
   695                          resultsPtr->ElementAt(i).entry.complete);
   696       tu->NewAddChunk(resultsPtr->ElementAt(i).entry.addChunk);
   697       tu->SetLocalUpdate();
   698       updates.AppendElement(tu);
   699       pParse->ForgetTableUpdates();
   700     } else {
   701       LOG(("Completion received, but table is not active, so not caching."));
   702     }
   703    }
   705   mClassifier->ApplyUpdates(&updates);
   706   return NS_OK;
   707 }
   709 NS_IMETHODIMP
   710 nsUrlClassifierDBServiceWorker::CacheMisses(PrefixArray *results)
   711 {
   712   LOG(("nsUrlClassifierDBServiceWorker::CacheMisses [%p] %d",
   713        this, results->Length()));
   715   // Ownership is transferred in to us
   716   nsAutoPtr<PrefixArray> resultsPtr(results);
   718   for (uint32_t i = 0; i < resultsPtr->Length(); i++) {
   719     mMissCache.AppendElement(resultsPtr->ElementAt(i));
   720    }
   721   return NS_OK;
   722 }
   724 nsresult
   725 nsUrlClassifierDBServiceWorker::OpenDb()
   726 {
   727   // Connection already open, don't do anything.
   728   if (mClassifier) {
   729     return NS_OK;
   730   }
   732   LOG(("Opening db"));
   734   nsresult rv;
   735   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   736   NS_ENSURE_SUCCESS(rv, rv);
   738   nsAutoPtr<Classifier> classifier(new Classifier());
   739   if (!classifier) {
   740     return NS_ERROR_OUT_OF_MEMORY;
   741   }
   743   classifier->SetFreshTime(gFreshnessGuarantee);
   745   rv = classifier->Open(*mCacheDir);
   746   NS_ENSURE_SUCCESS(rv, rv);
   748   mClassifier = classifier;
   750   return NS_OK;
   751 }
   753 // -------------------------------------------------------------------------
   754 // nsUrlClassifierLookupCallback
   755 //
   756 // This class takes the results of a lookup found on the worker thread
   757 // and handles any necessary partial hash expansions before calling
   758 // the client callback.
   760 class nsUrlClassifierLookupCallback MOZ_FINAL : public nsIUrlClassifierLookupCallback
   761                                               , public nsIUrlClassifierHashCompleterCallback
   762 {
   763 public:
   764   NS_DECL_THREADSAFE_ISUPPORTS
   765   NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
   766   NS_DECL_NSIURLCLASSIFIERHASHCOMPLETERCALLBACK
   768   nsUrlClassifierLookupCallback(nsUrlClassifierDBService *dbservice,
   769                                 nsIUrlClassifierCallback *c)
   770     : mDBService(dbservice)
   771     , mResults(nullptr)
   772     , mPendingCompletions(0)
   773     , mCallback(c)
   774     {}
   776   ~nsUrlClassifierLookupCallback();
   778 private:
   779   nsresult HandleResults();
   781   nsRefPtr<nsUrlClassifierDBService> mDBService;
   782   nsAutoPtr<LookupResultArray> mResults;
   784   // Completed results to send back to the worker for caching.
   785   nsAutoPtr<CacheResultArray> mCacheResults;
   787   uint32_t mPendingCompletions;
   788   nsCOMPtr<nsIUrlClassifierCallback> mCallback;
   789 };
   791 NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback,
   792                   nsIUrlClassifierLookupCallback,
   793                   nsIUrlClassifierHashCompleterCallback)
   795 nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback()
   796 {
   797   nsCOMPtr<nsIThread> thread;
   798   (void)NS_GetMainThread(getter_AddRefs(thread));
   800   if (mCallback) {
   801     (void)NS_ProxyRelease(thread, mCallback, false);
   802   }
   803 }
   805 NS_IMETHODIMP
   806 nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
   807 {
   808   NS_ASSERTION(mResults == nullptr,
   809                "Should only get one set of results per nsUrlClassifierLookupCallback!");
   811   if (!results) {
   812     HandleResults();
   813     return NS_OK;
   814   }
   816   mResults = results;
   818   // Check the results entries that need to be completed.
   819   for (uint32_t i = 0; i < results->Length(); i++) {
   820     LookupResult& result = results->ElementAt(i);
   822     // We will complete partial matches and matches that are stale.
   823     if (!result.Confirmed()) {
   824       nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
   825       if (mDBService->GetCompleter(result.mTableName,
   826                                    getter_AddRefs(completer))) {
   827         nsAutoCString partialHash;
   828         partialHash.Assign(reinterpret_cast<char*>(&result.hash.prefix),
   829                            PREFIX_SIZE);
   831         nsresult rv = completer->Complete(partialHash, this);
   832         if (NS_SUCCEEDED(rv)) {
   833           mPendingCompletions++;
   834         }
   835       } else {
   836         // For tables with no hash completer, a complete hash match is
   837         // good enough, we'll consider it fresh.
   838         if (result.Complete()) {
   839           result.mFresh = true;
   840         } else {
   841           NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
   842         }
   843       }
   844     }
   845   }
   847   if (mPendingCompletions == 0) {
   848     // All results were complete, we're ready!
   849     HandleResults();
   850   }
   852   return NS_OK;
   853 }
   855 NS_IMETHODIMP
   856 nsUrlClassifierLookupCallback::CompletionFinished(nsresult status)
   857 {
   858   LOG(("nsUrlClassifierLookupCallback::CompletionFinished [%p, %08x]",
   859        this, status));
   860   if (NS_FAILED(status)) {
   861     NS_WARNING("gethash response failed.");
   862   }
   864   mPendingCompletions--;
   865   if (mPendingCompletions == 0) {
   866     HandleResults();
   867   }
   869   return NS_OK;
   870 }
   872 NS_IMETHODIMP
   873 nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash,
   874                                           const nsACString& tableName,
   875                                           uint32_t chunkId)
   876 {
   877   LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d]",
   878        this, PromiseFlatCString(tableName).get(), chunkId));
   879   mozilla::safebrowsing::Completion hash;
   880   hash.Assign(completeHash);
   882   // Send this completion to the store for caching.
   883   if (!mCacheResults) {
   884     mCacheResults = new CacheResultArray();
   885     if (!mCacheResults)
   886       return NS_ERROR_OUT_OF_MEMORY;
   887   }
   889   CacheResult result;
   890   result.entry.addChunk = chunkId;
   891   result.entry.complete = hash;
   892   result.table = tableName;
   894   // OK if this fails, we just won't cache the item.
   895   mCacheResults->AppendElement(result);
   897   // Check if this matched any of our results.
   898   for (uint32_t i = 0; i < mResults->Length(); i++) {
   899     LookupResult& result = mResults->ElementAt(i);
   901     // Now, see if it verifies a lookup
   902     if (result.CompleteHash() == hash && result.mTableName.Equals(tableName)) {
   903       result.mProtocolConfirmed = true;
   904     }
   905   }
   907   return NS_OK;
   908 }
   910 nsresult
   911 nsUrlClassifierLookupCallback::HandleResults()
   912 {
   913   if (!mResults) {
   914     // No results, this URI is clean.
   915     return mCallback->HandleEvent(NS_LITERAL_CSTRING(""));
   916   }
   918   nsTArray<nsCString> tables;
   919   // Build a stringified list of result tables.
   920   for (uint32_t i = 0; i < mResults->Length(); i++) {
   921     LookupResult& result = mResults->ElementAt(i);
   923     // Leave out results that weren't confirmed, as their existence on
   924     // the list can't be verified.  Also leave out randomly-generated
   925     // noise.
   926     if (!result.Confirmed() || result.mNoise) {
   927       LOG(("Skipping result from table %s", result.mTableName.get()));
   928       continue;
   929     }
   931     LOG(("Confirmed result from table %s", result.mTableName.get()));
   933     if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
   934       tables.AppendElement(result.mTableName);
   935     }
   936   }
   938   // Some parts of this gethash request generated no hits at all.
   939   // Prefixes must have been removed from the database since our last update.
   940   // Save the prefixes we checked to prevent repeated requests
   941   // until the next update.
   942   nsAutoPtr<PrefixArray> cacheMisses(new PrefixArray());
   943   if (cacheMisses) {
   944     for (uint32_t i = 0; i < mResults->Length(); i++) {
   945       LookupResult &result = mResults->ElementAt(i);
   946       if (!result.Confirmed() && !result.mNoise) {
   947         cacheMisses->AppendElement(result.PrefixHash());
   948       }
   949     }
   950     // Hands ownership of the miss array back to the worker thread.
   951     mDBService->CacheMisses(cacheMisses.forget());
   952   }
   954   if (mCacheResults) {
   955     // This hands ownership of the cache results array back to the worker
   956     // thread.
   957     mDBService->CacheCompletions(mCacheResults.forget());
   958   }
   960   nsAutoCString tableStr;
   961   for (uint32_t i = 0; i < tables.Length(); i++) {
   962     if (i != 0)
   963       tableStr.Append(',');
   964     tableStr.Append(tables[i]);
   965   }
   967   return mCallback->HandleEvent(tableStr);
   968 }
   971 // -------------------------------------------------------------------------
   972 // Helper class for nsIURIClassifier implementation, translates table names
   973 // to nsIURIClassifier enums.
   975 class nsUrlClassifierClassifyCallback MOZ_FINAL : public nsIUrlClassifierCallback
   976 {
   977 public:
   978   NS_DECL_THREADSAFE_ISUPPORTS
   979   NS_DECL_NSIURLCLASSIFIERCALLBACK
   981   nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c,
   982                                   bool checkMalware,
   983                                   bool checkPhishing)
   984     : mCallback(c)
   985     , mCheckMalware(checkMalware)
   986     , mCheckPhishing(checkPhishing)
   987     {}
   989 private:
   990   nsCOMPtr<nsIURIClassifierCallback> mCallback;
   991   bool mCheckMalware;
   992   bool mCheckPhishing;
   993 };
   995 NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback,
   996                   nsIUrlClassifierCallback)
   998 NS_IMETHODIMP
   999 nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
  1001   // XXX: we should probably have the wardens tell the service which table
  1002   // names match with which classification.  For now the table names give
  1003   // enough information.
  1004   nsresult response = NS_OK;
  1006   nsACString::const_iterator begin, end;
  1008   tables.BeginReading(begin);
  1009   tables.EndReading(end);
  1010   if (mCheckMalware &&
  1011       FindInReadable(NS_LITERAL_CSTRING("-malware-"), begin, end)) {
  1012     response = NS_ERROR_MALWARE_URI;
  1013   } else {
  1014     // Reset begin before checking phishing table
  1015     tables.BeginReading(begin);
  1017     if (mCheckPhishing &&
  1018         FindInReadable(NS_LITERAL_CSTRING("-phish-"), begin, end)) {
  1019       response = NS_ERROR_PHISHING_URI;
  1023   mCallback->OnClassifyComplete(response);
  1025   return NS_OK;
  1029 // -------------------------------------------------------------------------
  1030 // Proxy class implementation
  1032 NS_IMPL_ISUPPORTS(nsUrlClassifierDBService,
  1033                   nsIUrlClassifierDBService,
  1034                   nsIURIClassifier,
  1035                   nsIObserver)
  1037 /* static */ nsUrlClassifierDBService*
  1038 nsUrlClassifierDBService::GetInstance(nsresult *result)
  1040   *result = NS_OK;
  1041   if (!sUrlClassifierDBService) {
  1042     sUrlClassifierDBService = new nsUrlClassifierDBService();
  1043     if (!sUrlClassifierDBService) {
  1044       *result = NS_ERROR_OUT_OF_MEMORY;
  1045       return nullptr;
  1048     NS_ADDREF(sUrlClassifierDBService);   // addref the global
  1050     *result = sUrlClassifierDBService->Init();
  1051     if (NS_FAILED(*result)) {
  1052       NS_RELEASE(sUrlClassifierDBService);
  1053       return nullptr;
  1055   } else {
  1056     // Already exists, just add a ref
  1057     NS_ADDREF(sUrlClassifierDBService);   // addref the return result
  1059   return sUrlClassifierDBService;
  1063 nsUrlClassifierDBService::nsUrlClassifierDBService()
  1064  : mCheckMalware(CHECK_MALWARE_DEFAULT)
  1065  , mCheckPhishing(CHECK_PHISHING_DEFAULT)
  1066  , mInUpdate(false)
  1070 nsUrlClassifierDBService::~nsUrlClassifierDBService()
  1072   sUrlClassifierDBService = nullptr;
  1075 nsresult
  1076 nsUrlClassifierDBService::ReadTablesFromPrefs()
  1078   nsCString allTables;
  1079   nsCString tables;
  1080   Preferences::GetCString(PHISH_TABLE_PREF, &allTables);
  1082   Preferences::GetCString(MALWARE_TABLE_PREF, &tables);
  1083   if (!tables.IsEmpty()) {
  1084     allTables.Append(',');
  1085     allTables.Append(tables);
  1088   Preferences::GetCString(DOWNLOAD_BLOCK_TABLE_PREF, &tables);
  1089   if (!tables.IsEmpty()) {
  1090     allTables.Append(',');
  1091     allTables.Append(tables);
  1094   Preferences::GetCString(DOWNLOAD_ALLOW_TABLE_PREF, &tables);
  1095   if (!tables.IsEmpty()) {
  1096     allTables.Append(',');
  1097     allTables.Append(tables);
  1100   Classifier::SplitTables(allTables, mGethashTables);
  1102   Preferences::GetCString(DISALLOW_COMPLETION_TABLE_PREF, &tables);
  1103   Classifier::SplitTables(tables, mDisallowCompletionsTables);
  1105   return NS_OK;
  1108 nsresult
  1109 nsUrlClassifierDBService::Init()
  1111 #if defined(PR_LOGGING)
  1112   if (!gUrlClassifierDbServiceLog)
  1113     gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService");
  1114 #endif
  1116   // Retrieve all the preferences.
  1117   mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
  1118     CHECK_MALWARE_DEFAULT);
  1119   mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
  1120     CHECK_PHISHING_DEFAULT);
  1121   uint32_t gethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
  1122     GETHASH_NOISE_DEFAULT);
  1123   gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
  1124     CONFIRM_AGE_DEFAULT_SEC);
  1125   ReadTablesFromPrefs();
  1127   // Do we *really* need to be able to change all of these at runtime?
  1128   Preferences::AddStrongObserver(this, CHECK_MALWARE_PREF);
  1129   Preferences::AddStrongObserver(this, CHECK_PHISHING_PREF);
  1130   Preferences::AddStrongObserver(this, GETHASH_NOISE_PREF);
  1131   Preferences::AddStrongObserver(this, CONFIRM_AGE_PREF);
  1132   Preferences::AddStrongObserver(this, PHISH_TABLE_PREF);
  1133   Preferences::AddStrongObserver(this, MALWARE_TABLE_PREF);
  1134   Preferences::AddStrongObserver(this, DOWNLOAD_BLOCK_TABLE_PREF);
  1135   Preferences::AddStrongObserver(this, DOWNLOAD_ALLOW_TABLE_PREF);
  1136   Preferences::AddStrongObserver(this, DISALLOW_COMPLETION_TABLE_PREF);
  1138   // Force PSM loading on main thread
  1139   nsresult rv;
  1140   nsCOMPtr<nsICryptoHash> acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  1141   NS_ENSURE_SUCCESS(rv, rv);
  1143   // Directory providers must also be accessed on the main thread.
  1144   nsCOMPtr<nsIFile> cacheDir;
  1145   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  1146                               getter_AddRefs(cacheDir));
  1147   if (NS_FAILED(rv)) {
  1148     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  1149                                 getter_AddRefs(cacheDir));
  1152   // Start the background thread.
  1153   rv = NS_NewNamedThread("URL Classifier", &gDbBackgroundThread);
  1154   if (NS_FAILED(rv))
  1155     return rv;
  1157   mWorker = new nsUrlClassifierDBServiceWorker();
  1158   if (!mWorker)
  1159     return NS_ERROR_OUT_OF_MEMORY;
  1161   rv = mWorker->Init(gethashNoise, cacheDir);
  1162   if (NS_FAILED(rv)) {
  1163     mWorker = nullptr;
  1164     return rv;
  1167   // Proxy for calling the worker on the background thread
  1168   mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
  1170   // Add an observer for shutdown
  1171   nsCOMPtr<nsIObserverService> observerService =
  1172       mozilla::services::GetObserverService();
  1173   if (!observerService)
  1174     return NS_ERROR_FAILURE;
  1176   observerService->AddObserver(this, "profile-before-change", false);
  1177   observerService->AddObserver(this, "xpcom-shutdown-threads", false);
  1179   return NS_OK;
  1182 // nsChannelClassifier is the only consumer of this interface.
  1183 NS_IMETHODIMP
  1184 nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
  1185                                    nsIURIClassifierCallback* c,
  1186                                    bool* result)
  1188   NS_ENSURE_ARG(aPrincipal);
  1189   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1191   if (!(mCheckMalware || mCheckPhishing)) {
  1192     *result = false;
  1193     return NS_OK;
  1196   nsRefPtr<nsUrlClassifierClassifyCallback> callback =
  1197     new nsUrlClassifierClassifyCallback(c, mCheckMalware, mCheckPhishing);
  1198   if (!callback) return NS_ERROR_OUT_OF_MEMORY;
  1200   nsAutoCString tables;
  1201   nsAutoCString malware;
  1202   // LookupURI takes a comma-separated list already.
  1203   Preferences::GetCString(MALWARE_TABLE_PREF, &malware);
  1204   if (!malware.IsEmpty()) {
  1205     tables.Append(malware);
  1207   nsAutoCString phishing;
  1208   Preferences::GetCString(PHISH_TABLE_PREF, &phishing);
  1209   if (!phishing.IsEmpty()) {
  1210     tables.Append(",");
  1211     tables.Append(phishing);
  1213   nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
  1214   if (rv == NS_ERROR_MALFORMED_URI) {
  1215     *result = false;
  1216     // The URI had no hostname, don't try to classify it.
  1217     return NS_OK;
  1219   NS_ENSURE_SUCCESS(rv, rv);
  1221   return NS_OK;
  1224 NS_IMETHODIMP
  1225 nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
  1226                                  const nsACString& tables,
  1227                                  nsIUrlClassifierCallback* c)
  1229   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1231   bool dummy;
  1232   return LookupURI(aPrincipal, tables, c, true, &dummy);
  1235 nsresult
  1236 nsUrlClassifierDBService::LookupURI(nsIPrincipal* aPrincipal,
  1237                                     const nsACString& tables,
  1238                                     nsIUrlClassifierCallback* c,
  1239                                     bool forceLookup,
  1240                                     bool *didLookup)
  1242   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1243   NS_ENSURE_ARG(aPrincipal);
  1245   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1246     *didLookup = false;
  1247     return NS_OK;
  1250   nsCOMPtr<nsIURI> uri;
  1251   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
  1252   NS_ENSURE_SUCCESS(rv, rv);
  1253   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
  1255   uri = NS_GetInnermostURI(uri);
  1256   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
  1258   nsAutoCString key;
  1259   // Canonicalize the url
  1260   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
  1261     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
  1262   rv = utilsService->GetKeyForURI(uri, key);
  1263   if (NS_FAILED(rv))
  1264     return rv;
  1266   if (forceLookup) {
  1267     *didLookup = true;
  1268   } else {
  1269     bool clean = false;
  1271     if (!clean) {
  1272       nsCOMPtr<nsIPermissionManager> permissionManager =
  1273         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
  1275       if (permissionManager) {
  1276         uint32_t perm;
  1277         rv = permissionManager->TestPermissionFromPrincipal(aPrincipal,
  1278                                                            "safe-browsing", &perm);
  1279         NS_ENSURE_SUCCESS(rv, rv);
  1281         clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
  1285     *didLookup = !clean;
  1286     if (clean) {
  1287       return NS_OK;
  1291   // Create an nsUrlClassifierLookupCallback object.  This object will
  1292   // take care of confirming partial hash matches if necessary before
  1293   // calling the client's callback.
  1294   nsCOMPtr<nsIUrlClassifierLookupCallback> callback =
  1295     new nsUrlClassifierLookupCallback(this, c);
  1296   if (!callback)
  1297     return NS_ERROR_OUT_OF_MEMORY;
  1299   nsCOMPtr<nsIUrlClassifierLookupCallback> proxyCallback =
  1300     new UrlClassifierLookupCallbackProxy(callback);
  1302   // Queue this lookup and call the lookup function to flush the queue if
  1303   // necessary.
  1304   rv = mWorker->QueueLookup(key, tables, proxyCallback);
  1305   NS_ENSURE_SUCCESS(rv, rv);
  1307   // This seems to just call HandlePendingLookups.
  1308   nsAutoCString dummy;
  1309   return mWorkerProxy->Lookup(nullptr, dummy, nullptr);
  1312 NS_IMETHODIMP
  1313 nsUrlClassifierDBService::GetTables(nsIUrlClassifierCallback* c)
  1315   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1317   // The proxy callback uses the current thread.
  1318   nsCOMPtr<nsIUrlClassifierCallback> proxyCallback =
  1319     new UrlClassifierCallbackProxy(c);
  1321   return mWorkerProxy->GetTables(proxyCallback);
  1324 NS_IMETHODIMP
  1325 nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
  1326                                            nsIUrlClassifierHashCompleter *completer)
  1328   if (completer) {
  1329     mCompleters.Put(tableName, completer);
  1330   } else {
  1331     mCompleters.Remove(tableName);
  1334   return NS_OK;
  1337 NS_IMETHODIMP
  1338 nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
  1339                                       const nsACString &updateTables)
  1341   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1343   if (mInUpdate)
  1344     return NS_ERROR_NOT_AVAILABLE;
  1346   mInUpdate = true;
  1348   // The proxy observer uses the current thread
  1349   nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
  1350     new UrlClassifierUpdateObserverProxy(observer);
  1352   return mWorkerProxy->BeginUpdate(proxyObserver, updateTables);
  1355 NS_IMETHODIMP
  1356 nsUrlClassifierDBService::BeginStream(const nsACString &table)
  1358   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1360   return mWorkerProxy->BeginStream(table);
  1363 NS_IMETHODIMP
  1364 nsUrlClassifierDBService::UpdateStream(const nsACString& aUpdateChunk)
  1366   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1368   return mWorkerProxy->UpdateStream(aUpdateChunk);
  1371 NS_IMETHODIMP
  1372 nsUrlClassifierDBService::FinishStream()
  1374   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1376   return mWorkerProxy->FinishStream();
  1379 NS_IMETHODIMP
  1380 nsUrlClassifierDBService::FinishUpdate()
  1382   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1384   mInUpdate = false;
  1386   return mWorkerProxy->FinishUpdate();
  1390 NS_IMETHODIMP
  1391 nsUrlClassifierDBService::CancelUpdate()
  1393   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1395   mInUpdate = false;
  1397   return mWorkerProxy->CancelUpdate();
  1400 NS_IMETHODIMP
  1401 nsUrlClassifierDBService::ResetDatabase()
  1403   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1405   return mWorkerProxy->ResetDatabase();
  1408 nsresult
  1409 nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results)
  1411   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1413   return mWorkerProxy->CacheCompletions(results);
  1416 nsresult
  1417 nsUrlClassifierDBService::CacheMisses(PrefixArray *results)
  1419   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
  1421   return mWorkerProxy->CacheMisses(results);
  1424 bool
  1425 nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
  1426                                        nsIUrlClassifierHashCompleter **completer)
  1428   // If we have specified a completer, go ahead and query it. This is only
  1429   // used by tests.
  1430   if (mCompleters.Get(tableName, completer)) {
  1431     return true;
  1434   // If we don't know about this table at all, or are disallowing completions
  1435   // for it, skip completion checks.
  1436   if (!mGethashTables.Contains(tableName) ||
  1437       mDisallowCompletionsTables.Contains(tableName)) {
  1438     return false;
  1441   MOZ_ASSERT(!StringBeginsWith(tableName, NS_LITERAL_CSTRING("test-")),
  1442              "We should never fetch hash completions for test tables");
  1444   // Otherwise, call gethash to find the hash completions.
  1445   return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
  1446                                      completer));
  1449 NS_IMETHODIMP
  1450 nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
  1451                                   const char16_t *aData)
  1453   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  1454     nsresult rv;
  1455     nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
  1456     NS_ENSURE_SUCCESS(rv, rv);
  1457     if (NS_LITERAL_STRING(CHECK_MALWARE_PREF).Equals(aData)) {
  1458       mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
  1459         CHECK_MALWARE_DEFAULT);
  1460     } else if (NS_LITERAL_STRING(CHECK_PHISHING_PREF).Equals(aData)) {
  1461       mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
  1462         CHECK_PHISHING_DEFAULT);
  1463     } else if (
  1464       NS_LITERAL_STRING(PHISH_TABLE_PREF).Equals(aData) ||
  1465       NS_LITERAL_STRING(MALWARE_TABLE_PREF).Equals(aData) ||
  1466       NS_LITERAL_STRING(DOWNLOAD_BLOCK_TABLE_PREF).Equals(aData) ||
  1467       NS_LITERAL_STRING(DOWNLOAD_ALLOW_TABLE_PREF).Equals(aData) ||
  1468       NS_LITERAL_STRING(DISALLOW_COMPLETION_TABLE_PREF).Equals(aData)) {
  1469       // Just read everything again.
  1470       ReadTablesFromPrefs();
  1471     } else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
  1472       gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
  1473         CONFIRM_AGE_DEFAULT_SEC);
  1475   } else if (!strcmp(aTopic, "profile-before-change") ||
  1476              !strcmp(aTopic, "xpcom-shutdown-threads")) {
  1477     Shutdown();
  1478   } else {
  1479     return NS_ERROR_UNEXPECTED;
  1482   return NS_OK;
  1485 // Join the background thread if it exists.
  1486 nsresult
  1487 nsUrlClassifierDBService::Shutdown()
  1489   LOG(("shutting down db service\n"));
  1491   if (!gDbBackgroundThread)
  1492     return NS_OK;
  1494   mCompleters.Clear();
  1496   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  1497   if (prefs) {
  1498     prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
  1499     prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
  1500     prefs->RemoveObserver(PHISH_TABLE_PREF, this);
  1501     prefs->RemoveObserver(MALWARE_TABLE_PREF, this);
  1502     prefs->RemoveObserver(DOWNLOAD_BLOCK_TABLE_PREF, this);
  1503     prefs->RemoveObserver(DOWNLOAD_ALLOW_TABLE_PREF, this);
  1504     prefs->RemoveObserver(DISALLOW_COMPLETION_TABLE_PREF, this);
  1505     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
  1508   DebugOnly<nsresult> rv;
  1509   // First close the db connection.
  1510   if (mWorker) {
  1511     rv = mWorkerProxy->CancelUpdate();
  1512     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
  1514     rv = mWorkerProxy->CloseDb();
  1515     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
  1518   mWorkerProxy = nullptr;
  1520   LOG(("joining background thread"));
  1522   gShuttingDownThread = true;
  1524   nsIThread *backgroundThread = gDbBackgroundThread;
  1525   gDbBackgroundThread = nullptr;
  1526   backgroundThread->Shutdown();
  1527   NS_RELEASE(backgroundThread);
  1529   return NS_OK;
  1532 nsIThread*
  1533 nsUrlClassifierDBService::BackgroundThread()
  1535   return gDbBackgroundThread;

mercurial