Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: src/org/adblockplus/sbrowser/contentblocker/engine/Subscription.java

Issue 29372653: Issue 4813 - UTFDataFormatException while serializing filter (Closed)
Left Patch Set: Created Jan. 18, 2017, 1:36 p.m.
Right Patch Set: Including UTF-8 constant and adding migration/file corruption recovery logic Created March 9, 2017, 7:38 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
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 10 matching lines...) Expand all
21 import java.io.BufferedOutputStream; 21 import java.io.BufferedOutputStream;
22 import java.io.BufferedReader; 22 import java.io.BufferedReader;
23 import java.io.DataInputStream; 23 import java.io.DataInputStream;
24 import java.io.DataOutputStream; 24 import java.io.DataOutputStream;
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;
32 import java.security.MessageDigest; 31 import java.security.MessageDigest;
33 import java.security.NoSuchAlgorithmException; 32 import java.security.NoSuchAlgorithmException;
34 import java.util.ArrayList; 33 import java.util.ArrayList;
35 import java.util.Collection; 34 import java.util.Collection;
36 import java.util.Collections; 35 import java.util.Collections;
37 import java.util.HashMap; 36 import java.util.HashMap;
38 import java.util.HashSet; 37 import java.util.HashSet;
39 import java.util.List; 38 import java.util.List;
40 import java.util.Locale; 39 import java.util.Locale;
41 import java.util.Map; 40 import java.util.Map;
42 import java.util.Map.Entry; 41 import java.util.Map.Entry;
43 import java.util.zip.GZIPInputStream; 42 import java.util.zip.GZIPInputStream;
44 import java.util.zip.GZIPOutputStream; 43 import java.util.zip.GZIPOutputStream;
45 44
45 import android.text.TextUtils;
46 import android.util.Log; 46 import android.util.Log;
47 47
48 /** 48 /**
49 * Simple subscription representation. 49 * Simple subscription representation.
50 */ 50 */
51 final class Subscription 51 final class Subscription
52 { 52 {
53 private static final String TAG = Subscription.class.getSimpleName(); 53 private static final String TAG = Subscription.class.getSimpleName();
54 public static final String KEY_TITLE = "title"; 54 public static final String KEY_TITLE = "title";
55 public static final String KEY_VERSION = "version"; 55 public static final String KEY_VERSION = "version";
(...skipping 21 matching lines...) Expand all
77 * subscription. 77 * subscription.
78 */ 78 */
79 private static final String[] ALLOWED_META_KEYS_ARRAY = 79 private static final String[] ALLOWED_META_KEYS_ARRAY =
80 { 80 {
81 "checksum", KEY_VERSION, KEY_TITLE, "last modified", "expires", "homepage" , "licence" 81 "checksum", KEY_VERSION, KEY_TITLE, "last modified", "expires", "homepage" , "licence"
82 }; 82 };
83 83
84 private final URL url; 84 private final URL url;
85 private final Type type; 85 private final Type type;
86 private final HashMap<String, String> meta = new HashMap<String, String>(); 86 private final HashMap<String, String> meta = new HashMap<String, String>();
87 private HashSet<String> filters = null; 87 private final HashSet<String> filters = new HashSet<String>();
88 88
89 private boolean metaDataValid = true; 89 private boolean metaDataValid = true;
90 private boolean filtersValid = true; 90 private boolean filtersValid = true;
91 91
92 static 92 static
93 { 93 {
94 for (final String s : ALLOWED_META_KEYS_ARRAY) 94 for (final String s : ALLOWED_META_KEYS_ARRAY)
95 { 95 {
96 ALLOWED_META_KEYS.add(s); 96 ALLOWED_META_KEYS.add(s);
97 } 97 }
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 public boolean isEnabled() 314 public boolean isEnabled()
315 { 315 {
316 return "true".equals(this.getMeta(KEY_ENABLED)); 316 return "true".equals(this.getMeta(KEY_ENABLED));
317 } 317 }
318 318
319 public void setEnabled(boolean enable) 319 public void setEnabled(boolean enable)
320 { 320 {
321 this.putMeta(KEY_ENABLED, Boolean.toString(enable)); 321 this.putMeta(KEY_ENABLED, Boolean.toString(enable));
322 } 322 }
323 323
324 public void getFilters(Collection<String> filters) 324 public void copyFilters(Collection<String> filters)
325 { 325 {
326 if (this.filters != null) 326 if (filters != null)
327 { 327 {
328 filters.addAll(this.filters); 328 filters.addAll(this.filters);
329 } 329 }
330 } 330 }
331 331
332 public void clearFilters() 332 public void clearFilters()
333 { 333 {
334 this.filters = null; 334 this.filters.clear();
335 } 335 }
336 336
337 /** 337 /**
338 * @return an internal management ID 338 * @return an internal management ID
339 */ 339 */
340 public String getId() 340 public String getId()
341 { 341 {
342 return getId(this); 342 return getId(this);
343 } 343 }
344 344
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 } 383 }
384 384
385 private static String createFilterHash(List<String> filters) throws IOExceptio n 385 private static String createFilterHash(List<String> filters) throws IOExceptio n
386 { 386 {
387 try 387 try
388 { 388 {
389 final MessageDigest md5 = MessageDigest.getInstance("MD5"); 389 final MessageDigest md5 = MessageDigest.getInstance("MD5");
390 Collections.sort(filters); 390 Collections.sort(filters);
391 for (final String filter : filters) 391 for (final String filter : filters)
392 { 392 {
393 md5.update(filter.getBytes("UTF-8")); 393 md5.update(filter.getBytes(Engine.CHARSET_UTF_8));
394 } 394 }
395 return byteArrayToHexString(md5.digest()); 395 return byteArrayToHexString(md5.digest());
396 } 396 }
397 catch (final NoSuchAlgorithmException e) 397 catch (final NoSuchAlgorithmException e)
398 { 398 {
399 throw new IOException("MD5 is unavailable: " + e.getMessage(), e); 399 throw new IOException("MD5 is unavailable: " + e.getMessage(), e);
400 } 400 }
401 } 401 }
402 402
403 public void serializeMetaData(final File metaFile) throws IOException 403 public void serializeMetaData(final File metaFile) throws IOException
(...skipping 16 matching lines...) Expand all
420 metaOut.close(); 420 metaOut.close();
421 } 421 }
422 } 422 }
423 423
424 public void serializeFilters(final File filtersFile) throws IOException 424 public void serializeFilters(final File filtersFile) throws IOException
425 { 425 {
426 final DataOutputStream filtersOut = new DataOutputStream(new GZIPOutputStrea m( 426 final DataOutputStream filtersOut = new DataOutputStream(new GZIPOutputStrea m(
427 new BufferedOutputStream(new FileOutputStream(filtersFile)))); 427 new BufferedOutputStream(new FileOutputStream(filtersFile))));
428 try 428 try
429 { 429 {
430 if (this.filters == null) 430 filtersOut.writeInt(this.filters.size());
431 { 431 filtersOut.writeUTF(createFilterHash(new ArrayList<String>(this.filters))) ;
432 filtersOut.writeInt(0); 432 for (final String s : this.filters)
433 } 433 {
434 else 434 final byte[] b = s.getBytes(Engine.CHARSET_UTF_8);
435 { 435 filtersOut.writeInt(b.length);
436 filtersOut.writeInt(this.filters.size()); 436 filtersOut.write(b);
437 filtersOut.writeUTF(createFilterHash(new ArrayList<String>(this.filters) ));
438 for (final String s : this.filters)
439 {
440 final byte[] b = s.getBytes(StandardCharsets.UTF_8);
anton 2017/01/23 06:49:40 StandardCharsets is available starting Java 7. If
diegocarloslima 2017/01/30 17:14:30 For instance, I'll use Charset.forName("UTF-8"). T
441 filtersOut.writeInt(b.length);
442 filtersOut.write(b);
443 }
444 } 437 }
445 } 438 }
446 finally 439 finally
447 { 440 {
448 filtersOut.close(); 441 filtersOut.close();
449 } 442 }
450 } 443 }
451 444
452 public void serializeSubscription(final File metaFile, final File filtersFile) throws IOException 445 public void serializeSubscription(final File metaFile, final File filtersFile) throws IOException
453 { 446 {
454 this.serializeMetaData(metaFile); 447 this.serializeMetaData(metaFile);
455 this.serializeFilters(filtersFile); 448 this.serializeFilters(filtersFile);
456 } 449 }
457 450
458 public static Subscription deserializeSubscription(final File metaFile) throws IOException 451 public static Subscription deserializeSubscription(final File metaFile)
459 { 452 {
460 final DataInputStream in = new DataInputStream(new GZIPInputStream(new Buffe redInputStream( 453 Subscription sub = null;
461 new FileInputStream(metaFile)))); 454 DataInputStream in = null;
462 try 455 try
463 { 456 {
457 in = new DataInputStream(new GZIPInputStream(new BufferedInputStream(
458 new FileInputStream(metaFile))));
464 final String urlString = in.readUTF(); 459 final String urlString = in.readUTF();
465 final Subscription sub = new Subscription(urlString.length() > 0 ? new URL (urlString) : null); 460 sub = new Subscription(!TextUtils.isEmpty(urlString) ? new URL(urlString) : null);
461 sub.metaDataValid = false;
466 final int numMetaEntries = in.readInt(); 462 final int numMetaEntries = in.readInt();
467 for (int i = 0; i < numMetaEntries; i++) 463 for (int i = 0; i < numMetaEntries; i++)
468 { 464 {
469 final String key = in.readUTF(); 465 final String key = in.readUTF();
470 final String value = in.readUTF(); 466 final String value = in.readUTF();
471 sub.meta.put(key, value); 467 sub.meta.put(key, value);
472 } 468 }
473 sub.metaDataValid = createMetaDataHash(sub.meta).equals(sub.getMeta(KEY_ME TA_HASH)); 469 sub.metaDataValid = createMetaDataHash(sub.meta).equals(sub.getMeta(KEY_ME TA_HASH));
474 return sub; 470 }
471 catch (Throwable t)
472 {
473 // We catch Throwable here in order to return whatever we could retrieve f rom the meta file
475 } 474 }
476 finally 475 finally
477 { 476 {
478 in.close(); 477 if (in != null)
479 } 478 {
480 } 479 try
481 480 {
482 public void deserializeFilters(final File filtersFile) throws IOException 481 in.close();
483 { 482 }
484 final DataInputStream in = new DataInputStream(new GZIPInputStream(new Buffe redInputStream( 483 catch (IOException e)
485 new FileInputStream(filtersFile)))); 484 {
486 try 485 // Ignored
487 { 486 }
487 }
488 }
489 return sub;
490 }
491
492 public void deserializeFilters(final File filtersFile)
493 {
494 this.clearFilters();
495 this.filtersValid = false;
496 DataInputStream in = null;
497 try
498 {
499 in = new DataInputStream(new GZIPInputStream(new BufferedInputStream(
500 new FileInputStream(filtersFile))));
488 final int numFilters = in.readInt(); 501 final int numFilters = in.readInt();
489 if (numFilters == 0) 502 final String filtersHash = in.readUTF();
490 { 503 for (int i = 0; i < numFilters; i++)
491 this.filters = null; 504 {
492 } 505 final int length = in.readInt();
493 else 506 final byte[] b = new byte[length];
494 { 507 in.readFully(b);
495 this.filters = new HashSet<String>(); 508 this.filters.add(new String(b, Engine.CHARSET_UTF_8));
496 final String filtersHash = in.readUTF(); 509 }
497 for (int i = 0; i < numFilters; i++) 510 this.filtersValid = createFilterHash(new ArrayList<String>(this.filters)). equals(
511 filtersHash);
512 Log.d(TAG, "Filters valid: " + this.filtersValid);
513 }
514 catch (Throwable t)
515 {
516 // We catch Throwable here in order to load whatever we could retrieve fro m the filters file
517 }
518 finally
519 {
520 if (in != null)
521 {
522 try
498 { 523 {
499 final int length = in.readInt(); 524 in.close();
500 final byte[] b = new byte[length];
501 in.readFully(b);
502 this.filters.add(new String(b, StandardCharsets.UTF_8));
503 } 525 }
504 this.filtersValid = createFilterHash(new ArrayList<String>(this.filters) ).equals( 526 catch (IOException e)
505 filtersHash); 527 {
506 Log.d(TAG, "Filters valid: " + this.filtersValid); 528 // Ignored
507 } 529 }
508 } 530 }
509 finally
510 {
511 in.close();
512 } 531 }
513 } 532 }
514 533
515 /** 534 /**
516 * Adds the given string, which should be a single filter to this 535 * Adds the given string, which should be a single filter to this
517 * subscription. 536 * subscription.
518 * 537 *
519 * @param input 538 * @param input
520 */ 539 */
521 public Subscription parseLine(String input) 540 public Subscription parseLine(String input)
522 { 541 {
523 if (this.filters == null)
524 {
525 this.filters = new HashSet<String>();
526 }
527
528 final String line = input.trim(); 542 final String line = input.trim();
529 if (!line.isEmpty()) 543 if (!line.isEmpty())
530 { 544 {
531 if (line.startsWith("!")) 545 if (line.startsWith("!"))
532 { 546 {
533 // Meta data 547 // Meta data
534 final int colon = line.indexOf(':'); 548 final int colon = line.indexOf(':');
535 if (colon > 2) 549 if (colon > 2)
536 { 550 {
537 final String key = line.substring(1, colon).trim().toLowerCase(LOCALE_ EN); 551 final String key = line.substring(1, colon).trim().toLowerCase(LOCALE_ EN);
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 if (lastModified != null) 634 if (lastModified != null)
621 { 635 {
622 this.meta.put(KEY_HTTP_LAST_MODIFIED, lastModified); 636 this.meta.put(KEY_HTTP_LAST_MODIFIED, lastModified);
623 } 637 }
624 else 638 else
625 { 639 {
626 this.meta.remove(KEY_HTTP_LAST_MODIFIED); 640 this.meta.remove(KEY_HTTP_LAST_MODIFIED);
627 } 641 }
628 this.meta.put(KEY_DOWNLOAD_COUNT, Long.toString(this.getDownloadCount( ) + 1)); 642 this.meta.put(KEY_DOWNLOAD_COUNT, Long.toString(this.getDownloadCount( ) + 1));
629 643
630 this.filters = new HashSet<String>(); 644 this.clearFilters();
631 this.parseText(text); 645 this.parseText(text);
632 } 646 }
633 } 647 }
634 } 648 }
635 649
636 this.serializeMetaData(metaFile); 650 this.serializeMetaData(metaFile);
637 if (filtersChanged) 651 if (filtersChanged)
638 { 652 {
639 this.serializeFilters(filtersFile); 653 this.serializeFilters(filtersFile);
640 this.clearFilters(); 654 this.clearFilters();
641 } 655 }
642 656
643 return filtersChanged; 657 return filtersChanged;
644 } 658 }
645 } 659 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld