From a8058d169dad450eca428ca76c5a0f44e45f41a7 Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Tue, 30 Oct 2012 19:54:34 +0900
Subject: [PATCH] Cleanup after dicttool

Arrange for temporary files to be deleted.
Also, add a security on stack overflows.

Change-Id: I9da2ebefb06409a71b235243ea835ce10d6a9b81
---
 .../dicttool/BinaryDictOffdeviceUtils.java    | 21 +++++++++++++------
 .../inputmethod/latin/dicttool/Info.java      |  2 +-
 .../BinaryDictOffdeviceUtilsTests.java        |  3 +++
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index 0803b08d5b..c2c77d61ab 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -50,6 +50,8 @@ public final class BinaryDictOffdeviceUtils {
     public final static String COMPRESSION = "compressed";
     public final static String ENCRYPTION = "encrypted";
 
+    private final static int MAX_DECODE_DEPTH = 8;
+
     public static class DecoderChainSpec {
         ArrayList<String> mDecoderSpec = new ArrayList<String>();
         File mFile;
@@ -85,12 +87,17 @@ public final class BinaryDictOffdeviceUtils {
      * If this is not a binary dictionary, the method returns null.
      */
     public static DecoderChainSpec getRawBinaryDictionaryOrNull(final File src) {
-        return getRawBinaryDictionaryOrNullInternal(new DecoderChainSpec(), src);
+        return getRawBinaryDictionaryOrNullInternal(new DecoderChainSpec(), src, 0);
     }
 
     private static DecoderChainSpec getRawBinaryDictionaryOrNullInternal(
-            final DecoderChainSpec spec, final File src) {
-        // TODO: arrange for the intermediary files to be deleted
+            final DecoderChainSpec spec, final File src, final int depth) {
+        // Unfortunately the decoding scheme we use can consider any data to be encrypted
+        // and will product some output, meaning it's not possible to reliably detect encrypted
+        // data. Thus, some non-dictionary files (especially small) ones may successfully decrypt
+        // over and over, ending in a stack overflow. Hence we limit the depth at which we try
+        // decoding the file.
+        if (depth > MAX_DECODE_DEPTH) return null;
         if (BinaryDictInputOutput.isBinaryDictionary(src)) {
             spec.mFile = src;
             return spec;
@@ -99,7 +106,7 @@ public final class BinaryDictOffdeviceUtils {
         final File uncompressedFile = tryGetUncompressedFile(src);
         if (null != uncompressedFile) {
             final DecoderChainSpec newSpec =
-                    getRawBinaryDictionaryOrNullInternal(spec, uncompressedFile);
+                    getRawBinaryDictionaryOrNullInternal(spec, uncompressedFile, depth + 1);
             if (null == newSpec) return null;
             return newSpec.addStep(COMPRESSION);
         }
@@ -107,7 +114,7 @@ public final class BinaryDictOffdeviceUtils {
         final File decryptedFile = tryGetDecryptedFile(src);
         if (null != decryptedFile) {
             final DecoderChainSpec newSpec =
-                    getRawBinaryDictionaryOrNullInternal(spec, decryptedFile);
+                    getRawBinaryDictionaryOrNullInternal(spec, decryptedFile, depth + 1);
             if (null == newSpec) return null;
             return newSpec.addStep(ENCRYPTION);
         }
@@ -122,6 +129,7 @@ public final class BinaryDictOffdeviceUtils {
     private static File tryGetUncompressedFile(final File src) {
         try {
             final File dst = File.createTempFile(PREFIX, SUFFIX);
+            dst.deleteOnExit();
             final FileOutputStream dstStream = new FileOutputStream(dst);
             copy(Compress.getUncompressedStream(new BufferedInputStream(new FileInputStream(src))),
                     new BufferedOutputStream(dstStream)); // #copy() closes the streams
@@ -140,12 +148,13 @@ public final class BinaryDictOffdeviceUtils {
     private static File tryGetDecryptedFile(final File src) {
         try {
             final File dst = File.createTempFile(PREFIX, SUFFIX);
+            dst.deleteOnExit();
             final FileOutputStream dstStream = new FileOutputStream(dst);
             copy(Crypt.getDecryptedStream(new BufferedInputStream(new FileInputStream(src))),
                     dstStream); // #copy() closes the streams
             return dst;
         } catch (IOException e) {
-            // Could not uncompress the file: presumably the file is simply not a compressed file
+            // Could not decrypt the file: presumably the file is simply not a crypted file
             return null;
         }
     }
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
index 7f25818fbc..d91a409d3b 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
@@ -32,7 +32,7 @@ public class Info extends Dicttool.Command {
 
     @Override
     public String getHelp() {
-        return COMMAND + "<filename>: prints various information about a dictionary file";
+        return COMMAND + " <filename>: prints various information about a dictionary file";
     }
 
     private static void showInfo(final FusionDictionary dict) {
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 7a686e556a..554bd24783 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -54,6 +54,7 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
         dict.add("fool", 1, null, false /* isNotAWord */);
 
         final File dst = File.createTempFile("testGetRawDict", ".tmp");
+        dst.deleteOnExit();
         final OutputStream out = Compress.getCompressedStream(
                 Compress.getCompressedStream(
                         Compress.getCompressedStream(
@@ -81,6 +82,7 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
     public void testGetRawDictFails() throws IOException {
         // Randomly create some 4k file containing garbage
         final File dst = File.createTempFile("testGetRawDict", ".tmp");
+        dst.deleteOnExit();
         final OutputStream out = new BufferedOutputStream(new FileOutputStream(dst));
         for (int i = 0; i < 1024; ++i) {
             out.write(0x12345678);
@@ -92,6 +94,7 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
                 BinaryDictOffdeviceUtils.getRawBinaryDictionaryOrNull(dst));
 
         final File gzDst = File.createTempFile("testGetRawDict", ".tmp");
+        gzDst.deleteOnExit();
         final OutputStream gzOut =
                 Compress.getCompressedStream(new BufferedOutputStream(new FileOutputStream(gzDst)));
         for (int i = 0; i < 1024; ++i) {
-- 
GitLab