1 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Url Classifier code
16 : *
17 : * The Initial Developer of the Original Code is
18 : * the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2011
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Dave Camp <dcamp@mozilla.com>
24 : * Gian-Carlo Pascutto <gpascutto@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "Classifier.h"
41 : #include "nsISimpleEnumerator.h"
42 : #include "nsIRandomGenerator.h"
43 : #include "nsIInputStream.h"
44 : #include "nsISeekableStream.h"
45 : #include "nsIFile.h"
46 : #include "nsAutoPtr.h"
47 : #include "mozilla/Telemetry.h"
48 : #include "prlog.h"
49 :
50 : // NSPR_LOG_MODULES=UrlClassifierDbService:5
51 : extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
52 : #if defined(PR_LOGGING)
53 : #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
54 : #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
55 : #else
56 : #define LOG(args)
57 : #define LOG_ENABLED() (PR_FALSE)
58 : #endif
59 :
60 : namespace mozilla {
61 : namespace safebrowsing {
62 :
63 50 : Classifier::Classifier()
64 50 : : mFreshTime(45 * 60)
65 : {
66 50 : }
67 :
68 100 : Classifier::~Classifier()
69 : {
70 50 : Close();
71 50 : }
72 :
73 : /*
74 : * Generate a unique 32-bit key for this user, which we will
75 : * use to rehash all prefixes. This ensures that different users
76 : * will get hash collisions on different prefixes, which in turn
77 : * avoids that "unlucky" URLs get mysterious slowdowns, and that
78 : * the servers get spammed if any such URL should get slashdotted.
79 : * https://bugzilla.mozilla.org/show_bug.cgi?id=669407#c10
80 : */
81 : nsresult
82 50 : Classifier::InitKey()
83 : {
84 100 : nsCOMPtr<nsIFile> storeFile;
85 50 : nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
86 50 : NS_ENSURE_SUCCESS(rv, rv);
87 :
88 50 : rv = storeFile->AppendNative(NS_LITERAL_CSTRING("classifier.hashkey"));
89 50 : NS_ENSURE_SUCCESS(rv, rv);
90 :
91 : bool exists;
92 50 : rv = storeFile->Exists(&exists);
93 50 : NS_ENSURE_SUCCESS(rv, rv);
94 :
95 50 : if (!exists) {
96 : // generate and store key
97 : nsCOMPtr<nsIRandomGenerator> rg =
98 100 : do_GetService("@mozilla.org/security/random-generator;1");
99 50 : NS_ENSURE_STATE(rg);
100 :
101 : PRUint8 *temp;
102 50 : nsresult rv = rg->GenerateRandomBytes(sizeof(mHashKey), &temp);
103 50 : NS_ENSURE_SUCCESS(rv, rv);
104 50 : memcpy(&mHashKey, temp, sizeof(mHashKey));
105 50 : NS_Free(temp);
106 :
107 100 : nsCOMPtr<nsIOutputStream> out;
108 50 : rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
109 50 : -1, -1, 0);
110 50 : NS_ENSURE_SUCCESS(rv, rv);
111 :
112 : PRUint32 written;
113 50 : rv = out->Write(reinterpret_cast<char*>(&mHashKey), sizeof(PRUint32), &written);
114 50 : NS_ENSURE_SUCCESS(rv, rv);
115 :
116 100 : nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
117 50 : rv = safeOut->Finish();
118 50 : NS_ENSURE_SUCCESS(rv, rv);
119 :
120 50 : LOG(("Initialized classifier, key = %X", mHashKey));
121 : } else {
122 : // read key
123 0 : nsCOMPtr<nsIInputStream> inputStream;
124 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
125 0 : -1, -1, 0);
126 0 : NS_ENSURE_SUCCESS(rv, rv);
127 :
128 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
129 0 : nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
130 0 : NS_ENSURE_SUCCESS(rv, rv);
131 :
132 0 : void *buffer = &mHashKey;
133 : rv = NS_ReadInputStreamToBuffer(inputStream,
134 : &buffer,
135 0 : sizeof(PRUint32));
136 0 : NS_ENSURE_SUCCESS(rv, rv);
137 :
138 0 : LOG(("Loaded classifier key = %X", mHashKey));
139 : }
140 :
141 50 : return NS_OK;
142 : }
143 :
144 : nsresult
145 50 : Classifier::Open(nsIFile& aCacheDirectory)
146 : {
147 : nsresult rv;
148 :
149 50 : mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
150 50 : NS_ENSURE_SUCCESS(rv, rv);
151 :
152 : // Ensure the safebrowsing directory exists.
153 50 : rv = aCacheDirectory.Clone(getter_AddRefs(mStoreDirectory));
154 50 : NS_ENSURE_SUCCESS(rv, rv);
155 :
156 50 : rv = mStoreDirectory->AppendNative(NS_LITERAL_CSTRING("safebrowsing"));
157 50 : NS_ENSURE_SUCCESS(rv, rv);
158 :
159 : bool storeExists;
160 50 : rv = mStoreDirectory->Exists(&storeExists);
161 50 : NS_ENSURE_SUCCESS(rv, rv);
162 :
163 50 : if (!storeExists) {
164 4 : rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
165 4 : NS_ENSURE_SUCCESS(rv, rv);
166 : } else {
167 : bool storeIsDir;
168 46 : rv = mStoreDirectory->IsDirectory(&storeIsDir);
169 46 : NS_ENSURE_SUCCESS(rv, rv);
170 46 : if (!storeIsDir)
171 0 : return NS_ERROR_FILE_DESTINATION_NOT_DIR;
172 : }
173 :
174 50 : rv = InitKey();
175 50 : if (NS_FAILED(rv)) {
176 : // Without a usable key the database is useless
177 0 : Reset();
178 0 : return NS_ERROR_FAILURE;
179 : }
180 :
181 50 : if (!mTableFreshness.Init()) {
182 0 : return NS_ERROR_FAILURE;
183 : }
184 :
185 50 : RegenActiveTables();
186 :
187 50 : return NS_OK;
188 : }
189 :
190 : nsresult
191 100 : Classifier::Close()
192 : {
193 100 : DropStores();
194 :
195 100 : return NS_OK;
196 : }
197 :
198 : nsresult
199 47 : Classifier::Reset()
200 : {
201 47 : DropStores();
202 :
203 94 : nsCOMPtr<nsISimpleEnumerator> entries;
204 47 : nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
205 47 : NS_ENSURE_SUCCESS(rv, rv);
206 :
207 : bool hasMore;
208 260 : while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
209 332 : nsCOMPtr<nsIFile> file;
210 166 : rv = entries->GetNext(getter_AddRefs(file));
211 166 : NS_ENSURE_SUCCESS(rv, rv);
212 :
213 166 : rv = file->Remove(PR_FALSE);
214 166 : NS_ENSURE_SUCCESS(rv, rv);
215 : }
216 47 : NS_ENSURE_SUCCESS(rv, rv);
217 :
218 47 : mTableFreshness.Clear();
219 47 : RegenActiveTables();
220 :
221 47 : return NS_OK;
222 : }
223 :
224 : void
225 48 : Classifier::TableRequest(nsACString& aResult)
226 : {
227 96 : nsTArray<nsCString> tables;
228 48 : ActiveTables(tables);
229 93 : for (uint32 i = 0; i < tables.Length(); i++) {
230 135 : nsAutoPtr<HashStore> store(new HashStore(tables[i], mStoreDirectory));
231 45 : if (!store)
232 0 : continue;
233 :
234 45 : nsresult rv = store->Open();
235 45 : if (NS_FAILED(rv))
236 0 : continue;
237 :
238 45 : aResult.Append(store->TableName());
239 45 : aResult.Append(";");
240 :
241 45 : ChunkSet &adds = store->AddChunks();
242 45 : ChunkSet &subs = store->SubChunks();
243 :
244 45 : if (adds.Length() > 0) {
245 45 : aResult.Append("a:");
246 90 : nsCAutoString addList;
247 45 : adds.Serialize(addList);
248 45 : aResult.Append(addList);
249 : }
250 :
251 45 : if (subs.Length() > 0) {
252 10 : if (adds.Length() > 0)
253 10 : aResult.Append(':');
254 10 : aResult.Append("s:");
255 20 : nsCAutoString subList;
256 10 : subs.Serialize(subList);
257 10 : aResult.Append(subList);
258 : }
259 :
260 45 : aResult.Append('\n');
261 : }
262 48 : }
263 :
264 : nsresult
265 140 : Classifier::Check(const nsACString& aSpec, LookupResultArray& aResults)
266 : {
267 280 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_CHECK_TIME> timer;
268 :
269 : // Get the set of fragments to look up.
270 280 : nsTArray<nsCString> fragments;
271 140 : nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
272 140 : NS_ENSURE_SUCCESS(rv, rv);
273 :
274 280 : nsTArray<nsCString> activeTables;
275 140 : ActiveTables(activeTables);
276 :
277 280 : nsTArray<LookupCache*> cacheArray;
278 574 : for (PRUint32 i = 0; i < activeTables.Length(); i++) {
279 147 : LookupCache *cache = GetLookupCache(activeTables[i]);
280 147 : if (cache) {
281 147 : cacheArray.AppendElement(cache);
282 : } else {
283 0 : return NS_ERROR_FAILURE;
284 : }
285 : }
286 :
287 : // Now check each lookup fragment against the entries in the DB.
288 453 : for (PRUint32 i = 0; i < fragments.Length(); i++) {
289 : Completion lookupHash;
290 313 : lookupHash.FromPlaintext(fragments[i], mCryptoHash);
291 :
292 : // Get list of host keys to look up
293 : Completion hostKey;
294 313 : rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash);
295 313 : if (NS_FAILED(rv)) {
296 : // Local host on the network
297 0 : continue;
298 : }
299 :
300 : #if DEBUG && defined(PR_LOGGING)
301 313 : if (LOG_ENABLED()) {
302 0 : nsCAutoString checking;
303 0 : lookupHash.ToString(checking);
304 0 : LOG(("Checking %s (%X)", checking.get(), lookupHash.ToUint32()));
305 : }
306 : #endif
307 658 : for (PRUint32 i = 0; i < cacheArray.Length(); i++) {
308 345 : LookupCache *cache = cacheArray[i];
309 : bool has, complete;
310 : Prefix codedPrefix;
311 : rv = cache->Has(lookupHash, hostKey, mHashKey,
312 345 : &has, &complete, &codedPrefix);
313 345 : NS_ENSURE_SUCCESS(rv, rv);
314 345 : if (has) {
315 116 : LookupResult *result = aResults.AppendElement();
316 116 : if (!result)
317 0 : return NS_ERROR_OUT_OF_MEMORY;
318 :
319 : PRInt64 age;
320 116 : bool found = mTableFreshness.Get(cache->TableName(), &age);
321 116 : if (!found) {
322 4 : age = 24 * 60 * 60; // just a large number
323 : } else {
324 112 : PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
325 112 : age = now - age;
326 : }
327 :
328 116 : LOG(("Found a result in %s: %s (Age: %Lds)",
329 : cache->TableName().get(),
330 : complete ? "complete." : "Not complete.",
331 : age));
332 :
333 116 : result->hash.complete = lookupHash;
334 116 : result->mCodedPrefix = codedPrefix;
335 116 : result->mComplete = complete;
336 116 : result->mFresh = (age < mFreshTime);
337 116 : result->mTableName.Assign(cache->TableName());
338 : }
339 : }
340 :
341 : }
342 :
343 140 : return NS_OK;
344 : }
345 :
346 : nsresult
347 97 : Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
348 : {
349 194 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_UPDATE_TIME> timer;
350 :
351 : #if defined(PR_LOGGING)
352 97 : PRIntervalTime clockStart = 0;
353 : if (LOG_ENABLED() || true) {
354 97 : clockStart = PR_IntervalNow();
355 : }
356 : #endif
357 :
358 97 : LOG(("Applying table updates."));
359 :
360 : nsresult rv;
361 :
362 224 : for (uint32 i = 0; i < aUpdates->Length(); i++) {
363 : // Previous ApplyTableUpdates() may have consumed this update..
364 127 : if ((*aUpdates)[i]) {
365 : // Run all updates for one table
366 97 : rv = ApplyTableUpdates(aUpdates, aUpdates->ElementAt(i)->TableName());
367 97 : if (NS_FAILED(rv)) {
368 0 : Reset();
369 0 : return rv;
370 : }
371 : }
372 : }
373 97 : aUpdates->Clear();
374 97 : RegenActiveTables();
375 97 : LOG(("Done applying updates."));
376 :
377 : #if defined(PR_LOGGING)
378 : if (LOG_ENABLED() || true) {
379 97 : PRIntervalTime clockEnd = PR_IntervalNow();
380 97 : LOG(("update took %dms\n",
381 : PR_IntervalToMilliseconds(clockEnd - clockStart)));
382 : }
383 : #endif
384 :
385 97 : return NS_OK;
386 : }
387 :
388 : nsresult
389 9 : Classifier::MarkSpoiled(nsTArray<nsCString>& aTables)
390 : {
391 26 : for (uint32 i = 0; i < aTables.Length(); i++) {
392 17 : LOG(("Spoiling table: %s", aTables[i].get()));
393 : // Spoil this table by marking it as no known freshness
394 17 : mTableFreshness.Remove(aTables[i]);
395 : }
396 9 : return NS_OK;
397 : }
398 :
399 : void
400 147 : Classifier::DropStores()
401 : {
402 147 : for (uint32 i = 0; i < mHashStores.Length(); i++) {
403 0 : delete mHashStores[i];
404 : }
405 147 : mHashStores.Clear();
406 192 : for (uint32 i = 0; i < mLookupCaches.Length(); i++) {
407 45 : delete mLookupCaches[i];
408 : }
409 147 : mLookupCaches.Clear();
410 147 : }
411 :
412 : nsresult
413 194 : Classifier::RegenActiveTables()
414 : {
415 194 : mActiveTablesCache.Clear();
416 :
417 388 : nsTArray<nsCString> foundTables;
418 194 : ScanStoreDir(foundTables);
419 :
420 296 : for (uint32 i = 0; i < foundTables.Length(); i++) {
421 306 : nsAutoPtr<HashStore> store(new HashStore(nsCString(foundTables[i]), mStoreDirectory));
422 102 : if (!store)
423 0 : return NS_ERROR_OUT_OF_MEMORY;
424 :
425 102 : nsresult rv = store->Open();
426 102 : if (NS_FAILED(rv))
427 0 : continue;
428 :
429 102 : LookupCache *lookupCache = GetLookupCache(store->TableName());
430 102 : if (!lookupCache) {
431 0 : continue;
432 : }
433 :
434 102 : const ChunkSet &adds = store->AddChunks();
435 102 : const ChunkSet &subs = store->SubChunks();
436 :
437 102 : if (adds.Length() == 0 && subs.Length() == 0)
438 3 : continue;
439 :
440 99 : LOG(("Active table: %s", store->TableName().get()));
441 201 : mActiveTablesCache.AppendElement(store->TableName());
442 : }
443 :
444 194 : return NS_OK;
445 : }
446 :
447 : nsresult
448 194 : Classifier::ScanStoreDir(nsTArray<nsCString>& aTables)
449 : {
450 388 : nsCOMPtr<nsISimpleEnumerator> entries;
451 194 : nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
452 194 : NS_ENSURE_SUCCESS(rv, rv);
453 :
454 : bool hasMore;
455 839 : while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
456 902 : nsCOMPtr<nsIFile> file;
457 451 : rv = entries->GetNext(getter_AddRefs(file));
458 451 : NS_ENSURE_SUCCESS(rv, rv);
459 :
460 902 : nsCString leafName;
461 451 : rv = file->GetNativeLeafName(leafName);
462 451 : NS_ENSURE_SUCCESS(rv, rv);
463 :
464 1353 : nsCString suffix(NS_LITERAL_CSTRING(".sbstore"));
465 :
466 451 : PRInt32 dot = leafName.RFind(suffix, 0);
467 451 : if (dot != -1) {
468 102 : leafName.Cut(dot, suffix.Length());
469 102 : aTables.AppendElement(leafName);
470 : }
471 : }
472 194 : NS_ENSURE_SUCCESS(rv, rv);
473 :
474 194 : return NS_OK;
475 : }
476 :
477 : nsresult
478 217 : Classifier::ActiveTables(nsTArray<nsCString>& aTables)
479 : {
480 217 : aTables = mActiveTablesCache;
481 217 : return NS_OK;
482 : }
483 :
484 : /*
485 : * This will consume+delete updates from the passed nsTArray.
486 : */
487 : nsresult
488 97 : Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
489 : const nsACString& aTable)
490 : {
491 97 : LOG(("Classifier::ApplyTableUpdates(%s)",
492 : PromiseFlatCString(aTable).get()));
493 :
494 291 : nsAutoPtr<HashStore> store(new HashStore(aTable, mStoreDirectory));
495 :
496 97 : if (!store)
497 0 : return NS_ERROR_FAILURE;
498 :
499 : // take the quick exit if there is no valid update for us
500 : // (common case)
501 97 : uint32 validupdates = 0;
502 :
503 233 : for (uint32 i = 0; i < aUpdates->Length(); i++) {
504 136 : TableUpdate *update = aUpdates->ElementAt(i);
505 136 : if (!update || !update->TableName().Equals(store->TableName()))
506 9 : continue;
507 127 : if (update->Empty()) {
508 5 : aUpdates->ElementAt(i) = nsnull;
509 5 : delete update;
510 5 : continue;
511 : }
512 122 : validupdates++;
513 : }
514 :
515 97 : if (!validupdates) {
516 0 : return NS_OK;
517 : }
518 :
519 97 : nsresult rv = store->Open();
520 97 : NS_ENSURE_SUCCESS(rv, rv);
521 97 : rv = store->BeginUpdate();
522 97 : NS_ENSURE_SUCCESS(rv, rv);
523 :
524 : // Read the part of the store that is (only) in the cache
525 97 : LookupCache *prefixSet = GetLookupCache(store->TableName());
526 97 : if (!prefixSet) {
527 0 : return NS_ERROR_FAILURE;
528 : }
529 194 : nsTArray<PRUint32> AddPrefixHashes;
530 97 : rv = prefixSet->GetPrefixes(&AddPrefixHashes);
531 97 : NS_ENSURE_SUCCESS(rv, rv);
532 97 : rv = store->AugmentAdds(AddPrefixHashes);
533 97 : NS_ENSURE_SUCCESS(rv, rv);
534 97 : AddPrefixHashes.Clear();
535 :
536 97 : uint32 applied = 0;
537 97 : bool updateFreshness = false;
538 :
539 233 : for (uint32 i = 0; i < aUpdates->Length(); i++) {
540 136 : TableUpdate *update = aUpdates->ElementAt(i);
541 136 : if (!update || !update->TableName().Equals(store->TableName()))
542 14 : continue;
543 :
544 122 : rv = store->ApplyUpdate(*update);
545 122 : NS_ENSURE_SUCCESS(rv, rv);
546 :
547 122 : applied++;
548 :
549 122 : LOG(("Applied update to table %s:", PromiseFlatCString(store->TableName()).get()));
550 122 : LOG((" %d add chunks", update->AddChunks().Length()));
551 122 : LOG((" %d add prefixes", update->AddPrefixes().Length()));
552 122 : LOG((" %d add completions", update->AddCompletes().Length()));
553 122 : LOG((" %d sub chunks", update->SubChunks().Length()));
554 122 : LOG((" %d sub prefixes", update->SubPrefixes().Length()));
555 122 : LOG((" %d sub completions", update->SubCompletes().Length()));
556 122 : LOG((" %d add expirations", update->AddExpirations().Length()));
557 122 : LOG((" %d sub expirations", update->SubExpirations().Length()));
558 :
559 122 : if (!update->IsLocalUpdate()) {
560 76 : updateFreshness = true;
561 76 : LOG(("Remote update, updating freshness"));
562 : }
563 :
564 122 : aUpdates->ElementAt(i) = nsnull;
565 122 : delete update;
566 : }
567 :
568 97 : LOG(("Applied %d update(s) to %s.", applied, PromiseFlatCString(store->TableName()).get()));
569 :
570 97 : rv = store->Rebuild();
571 97 : NS_ENSURE_SUCCESS(rv, rv);
572 :
573 97 : LOG(("Table %s now has:", PromiseFlatCString(store->TableName()).get()));
574 97 : LOG((" %d add chunks", store->AddChunks().Length()));
575 97 : LOG((" %d add prefixes", store->AddPrefixes().Length()));
576 97 : LOG((" %d add completions", store->AddCompletes().Length()));
577 97 : LOG((" %d sub chunks", store->SubChunks().Length()));
578 97 : LOG((" %d sub prefixes", store->SubPrefixes().Length()));
579 97 : LOG((" %d sub completions", store->SubCompletes().Length()));
580 :
581 97 : rv = store->WriteFile();
582 97 : NS_ENSURE_SUCCESS(rv, rv);
583 :
584 : // At this point the store is updated and written out to disk, but
585 : // the data is still in memory. Build our quick-lookup table here.
586 97 : rv = prefixSet->Build(store->AddPrefixes(), store->AddCompletes());
587 97 : NS_ENSURE_SUCCESS(rv, rv);
588 : #if defined(DEBUG) && defined(PR_LOGGING)
589 97 : prefixSet->Dump();
590 : #endif
591 97 : rv = prefixSet->WriteFile();
592 97 : NS_ENSURE_SUCCESS(rv, rv);
593 :
594 : // This will drop all the temporary storage used during the update.
595 97 : rv = store->FinishUpdate();
596 97 : NS_ENSURE_SUCCESS(rv, rv);
597 :
598 97 : if (updateFreshness) {
599 70 : PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
600 70 : LOG(("Successfully updated %s", PromiseFlatCString(store->TableName()).get()));
601 70 : rv = (mTableFreshness.Put(store->TableName(), now) ? NS_OK : NS_ERROR_FAILURE);
602 : }
603 :
604 97 : return rv;
605 : }
606 :
607 : LookupCache *
608 346 : Classifier::GetLookupCache(const nsACString& aTable)
609 : {
610 383 : for (uint32 i = 0; i < mLookupCaches.Length(); i++) {
611 338 : if (mLookupCaches[i]->TableName().Equals(aTable)) {
612 301 : return mLookupCaches[i];
613 : }
614 : }
615 :
616 90 : LookupCache *cache = new LookupCache(aTable, mStoreDirectory);
617 45 : nsresult rv = cache->Init();
618 45 : if (NS_FAILED(rv)) {
619 0 : return nsnull;
620 : }
621 45 : rv = cache->Open();
622 45 : if (NS_FAILED(rv)) {
623 0 : if (rv == NS_ERROR_FILE_CORRUPTED) {
624 0 : Reset();
625 : }
626 0 : return nsnull;
627 : }
628 45 : mLookupCaches.AppendElement(cache);
629 45 : return cache;
630 : }
631 :
632 : nsresult
633 0 : Classifier::ReadNoiseEntries(const Prefix& aPrefix,
634 : const nsACString& aTableName,
635 : PRInt32 aCount,
636 : PrefixArray* aNoiseEntries)
637 : {
638 0 : LookupCache *cache = GetLookupCache(aTableName);
639 0 : if (!cache) {
640 0 : return NS_ERROR_FAILURE;
641 : }
642 :
643 0 : nsTArray<PRUint32> prefixes;
644 0 : nsresult rv = cache->GetPrefixes(&prefixes);
645 0 : NS_ENSURE_SUCCESS(rv, rv);
646 :
647 0 : PRInt32 idx = prefixes.BinaryIndexOf(aPrefix.ToUint32());
648 :
649 0 : if (idx == nsTArray<PRUint32>::NoIndex) {
650 0 : NS_WARNING("Could not find prefix in PrefixSet during noise lookup");
651 0 : return NS_ERROR_FAILURE;
652 : }
653 :
654 0 : idx -= idx % aCount;
655 :
656 0 : for (PRInt32 i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) {
657 : Prefix newPref;
658 0 : newPref.FromUint32(prefixes[idx+i]);
659 0 : aNoiseEntries->AppendElement(newPref);
660 : }
661 :
662 0 : return NS_OK;
663 : }
664 :
665 : }
666 : }
|