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

Side by Side Diff: src/org/adblockplus/sbrowser/contentblocker/engine/Subscription.java

Issue 29372653: Issue 4813 - UTFDataFormatException while serializing filter (Closed)
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:
View unified diff | Download patch
OLDNEW
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());
431 filtersOut.writeUTF(createFilterHash(new ArrayList<String>(this.filters))) ;
432 for (final String s : this.filters)
430 { 433 {
431 filtersOut.writeInt(0); 434 final byte[] b = s.getBytes(Engine.CHARSET_UTF_8);
432 } 435 filtersOut.writeInt(b.length);
433 else 436 filtersOut.write(b);
434 {
435 filtersOut.writeInt(this.filters.size());
436 filtersOut.writeUTF(createFilterHash(new ArrayList<String>(this.filters) ));
437 for (final String s : this.filters)
438 {
439 filtersOut.writeUTF(s);
440 }
441 } 437 }
442 } 438 }
443 finally 439 finally
444 { 440 {
445 filtersOut.close(); 441 filtersOut.close();
446 } 442 }
447 } 443 }
448 444
449 public void serializeSubscription(final File metaFile, final File filtersFile) throws IOException 445 public void serializeSubscription(final File metaFile, final File filtersFile) throws IOException
450 { 446 {
451 this.serializeMetaData(metaFile); 447 this.serializeMetaData(metaFile);
452 this.serializeFilters(filtersFile); 448 this.serializeFilters(filtersFile);
453 } 449 }
454 450
455 public static Subscription deserializeSubscription(final File metaFile) throws IOException 451 public static Subscription deserializeSubscription(final File metaFile)
456 { 452 {
457 final DataInputStream in = new DataInputStream(new GZIPInputStream(new Buffe redInputStream( 453 Subscription sub = null;
458 new FileInputStream(metaFile)))); 454 DataInputStream in = null;
459 try 455 try
460 { 456 {
457 in = new DataInputStream(new GZIPInputStream(new BufferedInputStream(
458 new FileInputStream(metaFile))));
461 final String urlString = in.readUTF(); 459 final String urlString = in.readUTF();
462 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;
463 final int numMetaEntries = in.readInt(); 462 final int numMetaEntries = in.readInt();
464 for (int i = 0; i < numMetaEntries; i++) 463 for (int i = 0; i < numMetaEntries; i++)
465 { 464 {
466 final String key = in.readUTF(); 465 final String key = in.readUTF();
467 final String value = in.readUTF(); 466 final String value = in.readUTF();
468 sub.meta.put(key, value); 467 sub.meta.put(key, value);
469 } 468 }
470 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));
471 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
472 } 474 }
473 finally 475 finally
474 { 476 {
475 in.close(); 477 if (in != null)
478 {
479 try
480 {
481 in.close();
482 }
483 catch (IOException e)
484 {
485 // Ignored
486 }
487 }
476 } 488 }
489 return sub;
477 } 490 }
478 491
479 public void deserializeFilters(final File filtersFile) throws IOException 492 public void deserializeFilters(final File filtersFile)
480 { 493 {
481 final DataInputStream in = new DataInputStream(new GZIPInputStream(new Buffe redInputStream( 494 this.clearFilters();
482 new FileInputStream(filtersFile)))); 495 this.filtersValid = false;
496 DataInputStream in = null;
483 try 497 try
484 { 498 {
499 in = new DataInputStream(new GZIPInputStream(new BufferedInputStream(
500 new FileInputStream(filtersFile))));
485 final int numFilters = in.readInt(); 501 final int numFilters = in.readInt();
486 if (numFilters == 0) 502 final String filtersHash = in.readUTF();
503 for (int i = 0; i < numFilters; i++)
487 { 504 {
488 this.filters = null; 505 final int length = in.readInt();
506 final byte[] b = new byte[length];
507 in.readFully(b);
508 this.filters.add(new String(b, Engine.CHARSET_UTF_8));
489 } 509 }
490 else 510 this.filtersValid = createFilterHash(new ArrayList<String>(this.filters)). equals(
491 { 511 filtersHash);
492 this.filters = new HashSet<String>(); 512 Log.d(TAG, "Filters valid: " + this.filtersValid);
493 final String filtersHash = in.readUTF(); 513 }
494 for (int i = 0; i < numFilters; i++) 514 catch (Throwable t)
495 { 515 {
496 this.filters.add(in.readUTF()); 516 // We catch Throwable here in order to load whatever we could retrieve fro m the filters file
497 }
498 this.filtersValid = createFilterHash(new ArrayList<String>(this.filters) ).equals(
499 filtersHash);
500 Log.d(TAG, "Filters valid: " + this.filtersValid);
501 }
502 } 517 }
503 finally 518 finally
504 { 519 {
505 in.close(); 520 if (in != null)
521 {
522 try
523 {
524 in.close();
525 }
526 catch (IOException e)
527 {
528 // Ignored
529 }
530 }
506 } 531 }
507 } 532 }
508 533
509 /** 534 /**
510 * 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
511 * subscription. 536 * subscription.
512 * 537 *
513 * @param input 538 * @param input
514 */ 539 */
515 public Subscription parseLine(String input) 540 public Subscription parseLine(String input)
516 { 541 {
517 if (this.filters == null)
518 {
519 this.filters = new HashSet<String>();
520 }
521
522 final String line = input.trim(); 542 final String line = input.trim();
523 if (!line.isEmpty()) 543 if (!line.isEmpty())
524 { 544 {
525 if (line.startsWith("!")) 545 if (line.startsWith("!"))
526 { 546 {
527 // Meta data 547 // Meta data
528 final int colon = line.indexOf(':'); 548 final int colon = line.indexOf(':');
529 if (colon > 2) 549 if (colon > 2)
530 { 550 {
531 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
614 if (lastModified != null) 634 if (lastModified != null)
615 { 635 {
616 this.meta.put(KEY_HTTP_LAST_MODIFIED, lastModified); 636 this.meta.put(KEY_HTTP_LAST_MODIFIED, lastModified);
617 } 637 }
618 else 638 else
619 { 639 {
620 this.meta.remove(KEY_HTTP_LAST_MODIFIED); 640 this.meta.remove(KEY_HTTP_LAST_MODIFIED);
621 } 641 }
622 this.meta.put(KEY_DOWNLOAD_COUNT, Long.toString(this.getDownloadCount( ) + 1)); 642 this.meta.put(KEY_DOWNLOAD_COUNT, Long.toString(this.getDownloadCount( ) + 1));
623 643
624 this.filters = new HashSet<String>(); 644 this.clearFilters();
625 this.parseText(text); 645 this.parseText(text);
626 } 646 }
627 } 647 }
628 } 648 }
629 649
630 this.serializeMetaData(metaFile); 650 this.serializeMetaData(metaFile);
631 if (filtersChanged) 651 if (filtersChanged)
632 { 652 {
633 this.serializeFilters(filtersFile); 653 this.serializeFilters(filtersFile);
634 this.clearFilters(); 654 this.clearFilters();
635 } 655 }
636 656
637 return filtersChanged; 657 return filtersChanged;
638 } 658 }
639 } 659 }
OLDNEW

Powered by Google App Engine
This is Rietveld