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

Powered by Google App Engine
This is Rietveld