toolkit/components/url-classifier/nsUrlClassifierDBService.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial