Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent "too long" commands on Windows. #1758

Merged
merged 7 commits into from
Apr 6, 2018
135 changes: 85 additions & 50 deletions brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import brut.androlib.meta.MetaInfo;
import brut.androlib.meta.UsesFramework;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResConfigFlags;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.ResUnknownFiles;
Expand All @@ -41,8 +40,6 @@
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
Expand Down Expand Up @@ -161,28 +158,15 @@ public void decodeRawFiles(ExtFile apkFile, File outDir, short decodeAssetMode)
}
}

public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFilesOrExts) throws AndrolibException {
public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFiles) throws AndrolibException {
try {
Directory unk = apkFile.getDirectory();
Set<String> files = unk.getFiles(true);
String ext;

for (String file : files) {
if (isAPKFileNames(file) && !NO_COMPRESS_PATTERN.matcher(file).find()) {
if (unk.getCompressionLevel(file) == 0) {

if (StringUtils.countMatches(file, ".") > 1) {
ext = file;
} else {
ext = FilenameUtils.getExtension(file);
if (ext.isEmpty()) {
ext = file;
}
}

if (! uncompressedFilesOrExts.contains(ext)) {
uncompressedFilesOrExts.add(ext);
}
if (unk.getCompressionLevel(file) == 0 && !uncompressedFiles.contains(file)) {
uncompressedFiles.add(file);
}
}
}
Expand Down Expand Up @@ -277,6 +261,7 @@ public void build(ExtFile appDir, File outFile)
apkOptions.isFramework = meta.isFrameworkApk;
apkOptions.resourcesAreCompressed = meta.compressionType;
apkOptions.doNotCompress = meta.doNotCompress;
apkOptions.noCompressAssets = meta.noCompressAssets;

mAndRes.setSdkInfo(meta.sdkInfo);
mAndRes.setPackageId(meta.packageInfo);
Expand Down Expand Up @@ -308,8 +293,8 @@ public void build(ExtFile appDir, File outFile)
buildApk(appDir, outFile);

// we must go after the Apk is built, and copy the files in via Zip
// this is because Aapt won't add files it doesn't know (ex unknown files)
buildUnknownFiles(appDir, outFile, meta);
// this is because Aapt won't add files it doesn't know (ex unknown files & uncompressed assets)
buildUnknownAndUncompressedAssets(appDir, outFile, meta);

// we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it
// lets restore the unedited one, to not change the original
Expand Down Expand Up @@ -603,38 +588,46 @@ public void buildCopyOriginalFiles(File appDir)
}
}

public void buildUnknownFiles(File appDir, File outFile, MetaInfo meta)
public void buildUnknownAndUncompressedAssets(File appDir, File outFile, MetaInfo meta)
throws AndrolibException {
if (meta.unknownFiles == null && meta.noCompressAssets == null) {
return;
}

if (meta.unknownFiles != null) {
LOGGER.info("Copying unknown files/dir...");
}

Map<String, String> files = meta.unknownFiles;
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile);
if (!renamed) {
throw new AndrolibException("Unable to rename temporary file");
}

try (
ZipFile inputFile = new ZipFile(tempFile);
ZipOutputStream actualOutput = new ZipOutputStream(new FileOutputStream(outFile))
) {
copyExistingFiles(inputFile, actualOutput);
copyUnknownFiles(appDir, actualOutput, files);
} catch (IOException | BrutException ex) {
throw new AndrolibException(ex);
}
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile);
if (!renamed) {
throw new AndrolibException("Unable to rename temporary file");
}

// Remove our temporary file.
tempFile.delete();
try (
ZipFile inputFile = new ZipFile(tempFile);
ZipOutputStream actualOutput = new ZipOutputStream(new FileOutputStream(outFile))
) {
copyExistingFiles(inputFile, actualOutput, meta.noCompressAssets);
copyUncompressedAssetFiles(appDir, actualOutput, meta.noCompressAssets);
copyUnknownFiles(appDir, actualOutput, meta.unknownFiles);
} catch (IOException | BrutException ex) {
throw new AndrolibException(ex);
}

// Remove our temporary file.
tempFile.delete();
}

private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile) throws IOException {
private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile, Collection<String> assets)
throws IOException {
// First, copy the contents from the existing outFile:
Enumeration<? extends ZipEntry> entries = inputFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = new ZipEntry(entries.nextElement());
if (assets != null && assets.contains(entry.getName())) {
continue;
}

// We can't reuse the compressed size because it depends on compression sizes.
entry.setCompressedSize(-1);
Expand All @@ -651,25 +644,26 @@ private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile) th

private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<String, String> files)
throws BrutException, IOException {

File unknownFileDir = new File(appDir, UNK_DIRNAME);
if (files == null || files.isEmpty()) {
return;
}

// loop through unknown files
for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) {
File inputFile = new File(unknownFileDir, BrutIO.sanitizeUnknownFile(unknownFileDir, unknownFileInfo.getKey()));
String normalizedPath = BrutIO.normalizePath(unknownFileInfo.getKey());
String cleanedPath = BrutIO.sanitizeUnknownFile(unknownFileDir, normalizedPath);
File inputFile = new File(unknownFileDir, cleanedPath);
if (inputFile.isDirectory()) {
continue;
}

ZipEntry newEntry = new ZipEntry(unknownFileInfo.getKey());
ZipEntry newEntry = new ZipEntry(cleanedPath);
int method = Integer.parseInt(unknownFileInfo.getValue());
LOGGER.fine(String.format("Copying unknown file %s with method %d", unknownFileInfo.getKey(), method));
if (method == ZipEntry.STORED) {
newEntry.setMethod(ZipEntry.STORED);
newEntry.setSize(inputFile.length());
newEntry.setCompressedSize(-1);
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
CRC32 crc = BrutIO.calculateCrc(unknownFile);
newEntry.setCrc(crc.getValue());
newEntry = getStoredZipEntry(cleanedPath, inputFile);
} else {
newEntry.setMethod(ZipEntry.DEFLATED);
}
Expand All @@ -680,6 +674,46 @@ private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<Strin
}
}

private void copyUncompressedAssetFiles(File appDir, ZipOutputStream outputFile, Collection<String> files)
throws BrutException, IOException {

if (files == null || files.isEmpty()) {
return;
}

File assetFileDir = new File(appDir, ASSET_DIRNAME);

for (String asset : files) {
String normalizedPath = BrutIO.normalizePath(asset);
String cleanedPath = BrutIO.sanitizeUnknownFile(assetFileDir, normalizedPath);

File inputFile = new File(appDir, cleanedPath);
if (inputFile.isDirectory()) {
continue;
}

LOGGER.fine(String.format("Copying uncompressed asset: %s", normalizedPath));
ZipEntry newEntry = getStoredZipEntry(normalizedPath, inputFile);
outputFile.putNextEntry(newEntry);
BrutIO.copy(inputFile, outputFile);
outputFile.closeEntry();
}
}

private ZipEntry getStoredZipEntry(String cleanedPath, File inputFile)
throws IOException {

ZipEntry newEntry = new ZipEntry(cleanedPath);
newEntry.setMethod(ZipEntry.STORED);
newEntry.setSize(inputFile.length());
newEntry.setCompressedSize(-1);
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
CRC32 crc = BrutIO.calculateCrc(unknownFile);
newEntry.setCrc(crc.getValue());

return newEntry;
}

public void buildApk(File appDir, File outApk) throws AndrolibException {
LOGGER.info("Building apk file...");
if (outApk.exists()) {
Expand Down Expand Up @@ -777,6 +811,7 @@ public void close() throws IOException {
private final static String SMALI_DIRNAME = "smali";
private final static String APK_DIRNAME = "build/apk";
private final static String UNK_DIRNAME = "unknown";
private final static String ASSET_DIRNAME = "assets";
private final static String[] APK_RESOURCES_FILENAMES = new String[] {
"resources.arsc", "AndroidManifest.xml", "res" };
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
Expand All @@ -791,6 +826,6 @@ public void close() throws IOException {
// Taken from AOSP's frameworks/base/tools/aapt/Package.cpp
private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("\\.(" +
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv)$");
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv|arsc)$");

}
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ private void putUnknownInfo(MetaInfo meta) throws AndrolibException {

private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) {
meta.doNotCompress = mUncompressedFiles;
meta.noCompressAssets = mUncompressedFiles;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public class ApkOptions {
public boolean isFramework = false;
public boolean resourcesAreCompressed = false;
public boolean useAapt2 = false;

@Deprecated
public Collection<String> doNotCompress;
public Collection<String> noCompressAssets;

public String frameworkFolderLocation = null;
public String frameworkTag = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ public class MetaInfo {
public boolean sharedLibrary;
public boolean sparseResources;
public Map<String, String> unknownFiles;

@Deprecated
public Collection<String> doNotCompress;
public Collection<String> noCompressAssets;

private static Yaml getYaml() {
DumperOptions options = new DumperOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package brut.androlib.res.data;

import brut.util.BrutIO;
import java.util.LinkedHashMap;
import java.util.Map;

Expand All @@ -27,7 +28,7 @@ public class ResUnknownFiles {
private final Map<String, String> mUnknownFiles = new LinkedHashMap<>();

public void addUnknownFileInfo(String file, String value) {
mUnknownFiles.put(file, value);
mUnknownFiles.put(BrutIO.normalizePath(file), value);
}

public Map<String, String> getUnknownFiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Set;
import java.util.logging.Logger;

import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
Expand All @@ -45,11 +46,11 @@ protected void compareUnknownFiles() throws BrutException {

Map<String, String> controlFiles = control.unknownFiles;
Map<String, String> testFiles = test.unknownFiles;
assertTrue(controlFiles.size() == testFiles.size());
assertEquals(controlFiles.size(), testFiles.size());

// Make sure that the compression methods are still the same
for (Map.Entry<String, String> controlEntry : controlFiles.entrySet()) {
assertTrue(controlEntry.getValue().equals(testFiles.get(controlEntry.getKey())));
assertEquals(controlEntry.getValue(), testFiles.get(controlEntry.getKey()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void multipleExtensionUnknownFileTest() throws BrutException, IOException
apkDecoder.decode();

MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk);
for (String string : metaInfo.doNotCompress) {
for (String string : metaInfo.noCompressAssets) {
if (StringUtils.countMatches(string, ".") > 1) {
assertTrue(string.equalsIgnoreCase("assets/bin/Data/sharedassets1.assets.split0"));
}
Expand Down