| OLD | NEW |
| 1 /* | 1 /* |
| 2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
| 3 * Copyright (C) 2006-2016 Eyeo GmbH | 3 * Copyright (C) 2006-2016 Eyeo GmbH |
| 4 * | 4 * |
| 5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
| 6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
| 7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
| 8 * | 8 * |
| 9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 import java.io.File; | 25 import java.io.File; |
| 26 import java.io.FileInputStream; | 26 import java.io.FileInputStream; |
| 27 import java.io.FileOutputStream; | 27 import java.io.FileOutputStream; |
| 28 import java.io.IOException; | 28 import java.io.IOException; |
| 29 import java.io.StringReader; | 29 import java.io.StringReader; |
| 30 import java.net.URL; | 30 import java.net.URL; |
| 31 import java.nio.charset.StandardCharsets; | 31 import java.nio.charset.StandardCharsets; |
| 32 import java.security.MessageDigest; | 32 import java.security.MessageDigest; |
| 33 import java.security.NoSuchAlgorithmException; | 33 import java.security.NoSuchAlgorithmException; |
| 34 import java.util.ArrayList; | 34 import java.util.ArrayList; |
| 35 import java.util.Arrays; |
| 35 import java.util.Collection; | 36 import java.util.Collection; |
| 36 import java.util.Collections; | 37 import java.util.Collections; |
| 37 import java.util.HashMap; | 38 import java.util.HashMap; |
| 38 import java.util.HashSet; | 39 import java.util.HashSet; |
| 39 import java.util.List; | 40 import java.util.List; |
| 40 import java.util.Locale; | 41 import java.util.Locale; |
| 41 import java.util.Map; | 42 import java.util.Map; |
| 42 import java.util.Map.Entry; | 43 import java.util.Map.Entry; |
| 43 import java.util.zip.GZIPInputStream; | 44 import java.util.zip.GZIPInputStream; |
| 44 import java.util.zip.GZIPOutputStream; | 45 import java.util.zip.GZIPOutputStream; |
| 45 | 46 |
| 46 import android.text.TextUtils; | 47 import android.text.TextUtils; |
| 48 import android.text.format.DateUtils; |
| 47 import android.util.Log; | 49 import android.util.Log; |
| 48 | 50 |
| 49 /** | 51 /** |
| 50 * Simple subscription representation. | 52 * Simple subscription representation. |
| 51 */ | 53 */ |
| 52 final class Subscription | 54 final class Subscription |
| 53 { | 55 { |
| 54 private static final String TAG = Subscription.class.getSimpleName(); | 56 private static final String TAG = Subscription.class.getSimpleName(); |
| 55 public static final String KEY_TITLE = "title"; | 57 public static final String KEY_TITLE = "title"; |
| 56 public static final String KEY_VERSION = "version"; | 58 public static final String KEY_VERSION = "version"; |
| 57 public static final String KEY_HTTP_ETAG = "_etag"; | 59 public static final String KEY_HTTP_ETAG = "_etag"; |
| 58 public static final String KEY_HTTP_LAST_MODIFIED = "_last_modified"; | 60 public static final String KEY_HTTP_LAST_MODIFIED = "_last_modified"; |
| 59 public static final String KEY_UPDATE_TIMESTAMP = "_update_timestamp"; | 61 public static final String KEY_UPDATE_TIMESTAMP = "_update_timestamp"; |
| 60 public static final String KEY_TRIED_UPDATE_TIMESTAMP = "_tried_update_timesta
mp"; | 62 public static final String KEY_TRIED_UPDATE_TIMESTAMP = "_tried_update_timesta
mp"; |
| 61 public static final String KEY_DOWNLOAD_COUNT = "_download_count"; | 63 public static final String KEY_DOWNLOAD_COUNT = "_download_count"; |
| 62 public static final String KEY_ENABLED = "_enabled"; | 64 public static final String KEY_ENABLED = "_enabled"; |
| 63 public static final String KEY_META_HASH = "_meta_hash"; | 65 public static final String KEY_META_HASH = "_meta_hash"; |
| 64 | 66 |
| 65 public static final long MINIMAL_DOWNLOAD_INTERVAL = Engine.MILLIS_PER_HOUR /
4; | 67 private static final long MINIMAL_DOWNLOAD_INTERVAL = DateUtils.HOUR_IN_MILLIS
/ 4; |
| 66 public static final long DOWNLOAD_RETRY_INTERVAL = Engine.MILLIS_PER_HOUR; | 68 private static final long DOWNLOAD_RETRY_INTERVAL = DateUtils.HOUR_IN_MILLIS; |
| 67 | |
| 68 private static final HashSet<String> ALLOWED_META_KEYS = new HashSet<>(); | |
| 69 private static final Locale LOCALE_EN = Locale.ENGLISH; | |
| 70 | |
| 71 private final long updateInterval = Engine.MILLIS_PER_DAY | |
| 72 + (long) (Engine.MILLIS_PER_HOUR * 8. * Math.random()); | |
| 73 | 69 |
| 74 /** | 70 /** |
| 75 * List of meta keys that are allowed to import from a downloaded | 71 * List of meta keys that are allowed to import from a downloaded |
| 76 * subscription. | 72 * subscription. |
| 77 */ | 73 */ |
| 78 private static final String[] ALLOWED_META_KEYS_ARRAY = | 74 private static final String[] ALLOWED_META_KEYS_ARRAY = |
| 79 { | 75 { |
| 80 "checksum", KEY_VERSION, KEY_TITLE, "last modified", "expires", "homepage"
, "licence" | 76 "checksum", KEY_VERSION, KEY_TITLE, "last modified", "expires", "homep
age", "licence" |
| 81 }; | 77 }; |
| 78 private static final HashSet<String> ALLOWED_META_KEYS = |
| 79 new HashSet<>(Arrays.asList(ALLOWED_META_KEYS_ARRAY)); |
| 80 |
| 81 private static final Locale LOCALE_EN = Locale.ENGLISH; |
| 82 |
| 83 private final long updateInterval = DateUtils.DAY_IN_MILLIS |
| 84 + (long) (DateUtils.HOUR_IN_MILLIS * 8. * Math.random()); |
| 82 | 85 |
| 83 private final URL url; | 86 private final URL url; |
| 84 private final Type type; | 87 private final Type type; |
| 85 private final HashMap<String, String> meta = new HashMap<>(); | 88 private final HashMap<String, String> meta = new HashMap<>(); |
| 86 private final HashSet<String> filters = new HashSet<>(); | 89 private final HashSet<String> filters = new HashSet<>(); |
| 87 | 90 |
| 88 private boolean metaDataValid = true; | 91 private boolean metaDataValid = true; |
| 89 private boolean filtersValid = true; | 92 private boolean filtersValid = true; |
| 90 | 93 |
| 91 static | |
| 92 { | |
| 93 for (final String s : ALLOWED_META_KEYS_ARRAY) | |
| 94 { | |
| 95 ALLOWED_META_KEYS.add(s); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 /** | 94 /** |
| 100 * Subscription type. | 95 * Subscription type. |
| 101 * | 96 * |
| 102 * @author René Jeschke <rene@adblockplus.org> | 97 * @author René Jeschke <rene@adblockplus.org> |
| 103 */ | 98 */ |
| 104 public enum Type | 99 public enum Type |
| 105 { | 100 { |
| 106 /** | 101 /** |
| 107 * Initiated from an URL, can be automatically downloaded. | 102 * Initiated from an URL, can be automatically downloaded. |
| 108 */ | 103 */ |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 return "url:" + subscription.url.toString(); | 344 return "url:" + subscription.url.toString(); |
| 350 case USER: | 345 case USER: |
| 351 return "user:" + subscription.getMeta(KEY_TITLE); | 346 return "user:" + subscription.getMeta(KEY_TITLE); |
| 352 } | 347 } |
| 353 return ""; | 348 return ""; |
| 354 } | 349 } |
| 355 | 350 |
| 356 private static String byteArrayToHexString(final byte[] array) | 351 private static String byteArrayToHexString(final byte[] array) |
| 357 { | 352 { |
| 358 final StringBuilder sb = new StringBuilder(array.length * 2); | 353 final StringBuilder sb = new StringBuilder(array.length * 2); |
| 359 for (int i = 0; i < array.length; i++) | 354 for (final byte b : array) |
| 360 { | 355 { |
| 361 final int value = array[i] & 255; | 356 final int value = b & 255; |
| 362 if (value < 16) | 357 if (value < 16) |
| 363 { | 358 { |
| 364 sb.append('0'); | 359 sb.append('0'); |
| 365 } | 360 } |
| 366 sb.append(Integer.toHexString(value)); | 361 sb.append(Integer.toHexString(value)); |
| 367 } | 362 } |
| 368 return sb.toString(); | 363 return sb.toString(); |
| 369 } | 364 } |
| 370 | 365 |
| 371 private static String createMetaDataHash(final HashMap<String, String> meta) t
hrows IOException | 366 private static String createMetaDataHash(final HashMap<String, String> meta) t
hrows IOException |
| (...skipping 23 matching lines...) Expand all Loading... |
| 395 } | 390 } |
| 396 catch (final NoSuchAlgorithmException e) | 391 catch (final NoSuchAlgorithmException e) |
| 397 { | 392 { |
| 398 throw new IOException("MD5 is unavailable: " + e.getMessage(), e); | 393 throw new IOException("MD5 is unavailable: " + e.getMessage(), e); |
| 399 } | 394 } |
| 400 } | 395 } |
| 401 | 396 |
| 402 public void serializeMetaData(final File metaFile) throws IOException | 397 public void serializeMetaData(final File metaFile) throws IOException |
| 403 { | 398 { |
| 404 this.putMeta(KEY_META_HASH, createMetaDataHash(this.meta)); | 399 this.putMeta(KEY_META_HASH, createMetaDataHash(this.meta)); |
| 405 try (final DataOutputStream metaOut = new DataOutputStream(new GZIPOutputStr
eam( | 400 try (final DataOutputStream metaOut = new DataOutputStream(new BufferedOutpu
tStream( |
| 406 new BufferedOutputStream(new FileOutputStream(metaFile))))) | 401 new GZIPOutputStream(new FileOutputStream(metaFile))))) |
| 407 { | 402 { |
| 408 metaOut.writeUTF(this.url != null ? this.url.toString() : ""); | 403 metaOut.writeUTF(this.url != null ? this.url.toString() : ""); |
| 409 metaOut.writeInt(this.meta.size()); | 404 metaOut.writeInt(this.meta.size()); |
| 410 for (final Entry<String, String> e : this.meta.entrySet()) | 405 for (final Entry<String, String> e : this.meta.entrySet()) |
| 411 { | 406 { |
| 412 metaOut.writeUTF(e.getKey()); | 407 metaOut.writeUTF(e.getKey()); |
| 413 metaOut.writeUTF(e.getValue()); | 408 metaOut.writeUTF(e.getValue()); |
| 414 } | 409 } |
| 415 } | 410 } |
| 416 } | 411 } |
| 417 | 412 |
| 418 public void serializeFilters(final File filtersFile) throws IOException | 413 public void serializeFilters(final File filtersFile) throws IOException |
| 419 { | 414 { |
| 420 try (final DataOutputStream filtersOut = new DataOutputStream(new GZIPOutput
Stream( | 415 try (final DataOutputStream filtersOut = new DataOutputStream(new BufferedOu
tputStream( |
| 421 new BufferedOutputStream(new FileOutputStream(filtersFile))))) | 416 new GZIPOutputStream(new FileOutputStream(filtersFile))))) |
| 422 { | 417 { |
| 423 filtersOut.writeInt(this.filters.size()); | 418 filtersOut.writeInt(this.filters.size()); |
| 424 filtersOut.writeUTF(createFilterHash(new ArrayList<>(this.filters))); | 419 filtersOut.writeUTF(createFilterHash(new ArrayList<>(this.filters))); |
| 425 for (final String s : this.filters) | 420 for (final String s : this.filters) |
| 426 { | 421 { |
| 427 final byte[] b = s.getBytes(StandardCharsets.UTF_8); | 422 final byte[] b = s.getBytes(StandardCharsets.UTF_8); |
| 428 filtersOut.writeInt(b.length); | 423 filtersOut.writeInt(b.length); |
| 429 filtersOut.write(b); | 424 filtersOut.write(b); |
| 430 } | 425 } |
| 431 } | 426 } |
| 432 } | 427 } |
| 433 | 428 |
| 434 public void serializeSubscription(final File metaFile, final File filtersFile)
throws IOException | 429 public void serializeSubscription(final File metaFile, final File filtersFile)
throws IOException |
| 435 { | 430 { |
| 436 this.serializeMetaData(metaFile); | 431 this.serializeMetaData(metaFile); |
| 437 this.serializeFilters(filtersFile); | 432 this.serializeFilters(filtersFile); |
| 438 } | 433 } |
| 439 | 434 |
| 440 public static Subscription deserializeSubscription(final File metaFile) | 435 public static Subscription deserializeSubscription(final File metaFile) |
| 441 { | 436 { |
| 442 Subscription sub = null; | 437 Subscription sub = null; |
| 443 try (final DataInputStream in = new DataInputStream(new GZIPInputStream(new
BufferedInputStream( | 438 try (final DataInputStream in = new DataInputStream(new BufferedInputStream(
new GZIPInputStream( |
| 444 new FileInputStream(metaFile))))) | 439 new FileInputStream(metaFile))))) |
| 445 { | 440 { |
| 446 final String urlString = in.readUTF(); | 441 final String urlString = in.readUTF(); |
| 447 sub = new Subscription(!TextUtils.isEmpty(urlString) ? new URL(urlString)
: null); | 442 sub = new Subscription(!TextUtils.isEmpty(urlString) ? new URL(urlString)
: null); |
| 448 sub.metaDataValid = false; | 443 sub.metaDataValid = false; |
| 449 final int numMetaEntries = in.readInt(); | 444 final int numMetaEntries = in.readInt(); |
| 450 for (int i = 0; i < numMetaEntries; i++) | 445 for (int i = 0; i < numMetaEntries; i++) |
| 451 { | 446 { |
| 452 final String key = in.readUTF(); | 447 final String key = in.readUTF(); |
| 453 final String value = in.readUTF(); | 448 final String value = in.readUTF(); |
| 454 sub.meta.put(key, value); | 449 sub.meta.put(key, value); |
| 455 } | 450 } |
| 456 sub.metaDataValid = createMetaDataHash(sub.meta).equals(sub.getMeta(KEY_ME
TA_HASH)); | 451 sub.metaDataValid = createMetaDataHash(sub.meta).equals(sub.getMeta(KEY_ME
TA_HASH)); |
| 457 } | 452 } |
| 458 catch (Throwable t) | 453 catch (Throwable t) |
| 459 { | 454 { |
| 460 // We catch Throwable here in order to return whatever we could retrieve f
rom the meta file | 455 // We catch Throwable here in order to return whatever we could retrieve f
rom the meta file |
| 461 } | 456 } |
| 462 return sub; | 457 return sub; |
| 463 } | 458 } |
| 464 | 459 |
| 465 public void deserializeFilters(final File filtersFile) | 460 public void deserializeFilters(final File filtersFile) |
| 466 { | 461 { |
| 467 this.clearFilters(); | 462 this.clearFilters(); |
| 468 this.filtersValid = false; | 463 this.filtersValid = false; |
| 469 try (final DataInputStream in = new DataInputStream(new GZIPInputStream(new
BufferedInputStream( | 464 try (final DataInputStream in = new DataInputStream(new BufferedInputStream(
new GZIPInputStream( |
| 470 new FileInputStream(filtersFile))))) | 465 new FileInputStream(filtersFile))))) |
| 471 { | 466 { |
| 472 final int numFilters = in.readInt(); | 467 final int numFilters = in.readInt(); |
| 473 final String filtersHash = in.readUTF(); | 468 final String filtersHash = in.readUTF(); |
| 474 for (int i = 0; i < numFilters; i++) | 469 for (int i = 0; i < numFilters; i++) |
| 475 { | 470 { |
| 476 final int length = in.readInt(); | 471 final int length = in.readInt(); |
| 477 final byte[] b = new byte[length]; | 472 final byte[] b = new byte[length]; |
| 478 in.readFully(b); | 473 in.readFully(b); |
| 479 this.filters.add(new String(b, StandardCharsets.UTF_8)); | 474 this.filters.add(new String(b, StandardCharsets.UTF_8)); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 605 this.serializeMetaData(metaFile); | 600 this.serializeMetaData(metaFile); |
| 606 if (filtersChanged) | 601 if (filtersChanged) |
| 607 { | 602 { |
| 608 this.serializeFilters(filtersFile); | 603 this.serializeFilters(filtersFile); |
| 609 this.clearFilters(); | 604 this.clearFilters(); |
| 610 } | 605 } |
| 611 | 606 |
| 612 return filtersChanged; | 607 return filtersChanged; |
| 613 } | 608 } |
| 614 } | 609 } |
| OLD | NEW |