diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java index 4df7a83a..b4ad4218 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java @@ -4,17 +4,20 @@ import android.content.Context; import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.otp.OtpInfoException; +import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.vault.VaultFile; import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.VaultFileException; import com.beemdevelopment.aegis.vault.slots.SlotList; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.io.InputStream; public class AegisImporter extends DatabaseImporter { @@ -23,19 +26,14 @@ public class AegisImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { + protected SuFile getAppPath() { throw new UnsupportedOperationException(); } @Override - protected String getAppSubPath() { - throw new UnsupportedOperationException(); - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { - byte[] bytes = reader.readAll(); + byte[] bytes = IOUtils.readAll(stream); VaultFile file = VaultFile.fromBytes(bytes); if (file.isEncrypted()) { return new EncryptedState(file); diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java index 01034cc7..bd6fccbb 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java @@ -17,13 +17,16 @@ import com.beemdevelopment.aegis.otp.SteamInfo; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.ui.Dialogs; import com.beemdevelopment.aegis.ui.tasks.ProgressDialogTask; +import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; @@ -55,20 +58,15 @@ public class AndOtpImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { + protected SuFile getAppPath() { throw new UnsupportedOperationException(); } @Override - protected String getAppSubPath() { - throw new UnsupportedOperationException(); - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { byte[] bytes; try { - bytes = reader.readAll(); + bytes = IOUtils.readAll(stream); } catch (IOException e) { throw new DatabaseImporterException(e); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java index 14b125a5..bb82760c 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java @@ -3,6 +3,8 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; import com.beemdevelopment.aegis.ui.Dialogs; +import com.beemdevelopment.aegis.util.IOUtils; +import com.topjohnwu.superuser.io.SuFile; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.LocalFileHeader; @@ -11,6 +13,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; public class AuthenticatorPlusImporter extends DatabaseImporter { private static final String FILENAME = "Accounts.txt"; @@ -20,19 +23,14 @@ public class AuthenticatorPlusImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { + protected SuFile getAppPath() { throw new UnsupportedOperationException(); } @Override - protected String getAppSubPath() { - throw new UnsupportedOperationException(); - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { - return new EncryptedState(reader.readAll()); + return new EncryptedState(IOUtils.readAll(stream)); } catch (IOException e) { throw new DatabaseImporterException(e); } @@ -55,9 +53,8 @@ public class AuthenticatorPlusImporter extends DatabaseImporter { while ((header = zipStream.getNextEntry()) != null) { File file = new File(header.getFileName()); if (file.getName().equals(FILENAME)) { - FileReader reader = new FileReader(zipStream); GoogleAuthUriImporter importer = new GoogleAuthUriImporter(context); - GoogleAuthUriImporter.State state = importer.read(reader); + DatabaseImporter.State state = importer.read(zipStream); listener.onStateDecrypted(state); return; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java index 8cccafa5..367d7918 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java @@ -1,6 +1,7 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; import android.util.Xml; import com.beemdevelopment.aegis.R; @@ -13,6 +14,7 @@ import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.ui.Dialogs; import com.beemdevelopment.aegis.util.PreferenceParser; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; @@ -21,6 +23,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -53,21 +56,16 @@ public class AuthyImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(reader.getStream(), null); + parser.setInput(stream, null); parser.nextTag(); JSONArray array = new JSONArray(); diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java index aae63b0b..da6672bb 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java @@ -3,10 +3,10 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; import android.content.pm.PackageManager; -import com.beemdevelopment.aegis.vault.VaultEntry; -import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.util.UUIDMap; +import com.beemdevelopment.aegis.vault.VaultEntry; import com.topjohnwu.superuser.io.SuFile; +import com.topjohnwu.superuser.io.SuFileInputStream; import java.io.IOException; import java.io.InputStream; @@ -57,20 +57,27 @@ public abstract class DatabaseImporter { return _context; } - public SuFile getAppPath() throws DatabaseImporterException, PackageManager.NameNotFoundException { - return getAppPath(getAppPkgName(), getAppSubPath()); - } + protected abstract SuFile getAppPath() throws DatabaseImporterException, PackageManager.NameNotFoundException; protected SuFile getAppPath(String pkgName, String subPath) throws PackageManager.NameNotFoundException { PackageManager man = getContext().getPackageManager(); return new SuFile(man.getApplicationInfo(pkgName, 0).dataDir, subPath); } - protected abstract String getAppPkgName(); + protected abstract State read(InputStream stream, boolean isInternal) throws DatabaseImporterException; - protected abstract String getAppSubPath() throws DatabaseImporterException, PackageManager.NameNotFoundException; + public State read(InputStream stream) throws DatabaseImporterException { + return read(stream, false); + } - public abstract State read(FileReader reader) throws DatabaseImporterException; + public State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException { + SuFile file = getAppPath(); + try (SuFileInputStream stream = new SuFileInputStream(file)) { + return read(stream, true); + } catch (IOException e) { + throw new DatabaseImporterException(e); + } + } public static DatabaseImporter create(Context context, Class type) { try { @@ -138,36 +145,6 @@ public abstract class DatabaseImporter { } } - public static class FileReader { - private InputStream _stream; - private boolean _internal; - - public FileReader(InputStream stream) { - this(stream, false); - } - - public FileReader(InputStream stream, boolean internal) { - _stream = stream; - _internal = internal; - } - - public byte[] readAll() throws IOException { - return IOUtils.readAll(_stream); - } - - public InputStream getStream() { - return _stream; - } - - /** - * Reports whether this reader reads the internal state of an app. - * @return true if reading from internal file, false if reading from external file - */ - public boolean isInternal() { - return _internal; - } - } - public interface DecryptListener { void onStateDecrypted(State state); void onError(Exception e); diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java index be1fa9f2..d63525ca 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java @@ -1,15 +1,17 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; import android.util.Xml; -import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.otp.HotpInfo; import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.SteamInfo; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.util.PreferenceParser; +import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; @@ -18,6 +20,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -30,21 +33,16 @@ public class FreeOtpImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(reader.getStream(), null); + parser.setInput(stream, null); parser.nextTag(); List entries = new ArrayList<>(); diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpPlusImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpPlusImporter.java index 00a46ba6..3761658d 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpPlusImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpPlusImporter.java @@ -1,12 +1,17 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; + +import com.beemdevelopment.aegis.util.IOUtils; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -20,24 +25,19 @@ public class FreeOtpPlusImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { State state; - if (reader.isInternal()) { - state = new FreeOtpImporter(getContext()).read(reader); + if (isInternal) { + state = new FreeOtpImporter(getContext()).read(stream); } else { try { - String json = new String(reader.readAll(), StandardCharsets.UTF_8); + String json = new String(IOUtils.readAll(stream), StandardCharsets.UTF_8); JSONObject obj = new JSONObject(json); JSONArray array = obj.getJSONArray("tokens"); diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java index c4d8377e..f7f4e2bf 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java @@ -1,6 +1,7 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import com.beemdevelopment.aegis.encoding.Base32; @@ -10,7 +11,9 @@ import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; +import java.io.InputStream; import java.util.List; public class GoogleAuthImporter extends DatabaseImporter { @@ -25,19 +28,22 @@ public class GoogleAuthImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } - + @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { SqlImporterHelper helper = new SqlImporterHelper(getContext()); - List entries = helper.read(Entry.class, reader.getStream(), "accounts"); + List entries = helper.read(Entry.class, stream, "accounts"); + return new State(entries); + } + + @Override + public DatabaseImporter.State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException { + SuFile path = getAppPath(); + SqlImporterHelper helper = new SqlImporterHelper(getContext()); + List entries = helper.read(Entry.class, path, "accounts"); return new State(entries); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthUriImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthUriImporter.java index 5e9d33e9..7923a165 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthUriImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthUriImporter.java @@ -5,9 +5,11 @@ import android.content.Context; import com.beemdevelopment.aegis.otp.GoogleAuthInfo; import com.beemdevelopment.aegis.otp.GoogleAuthInfoException; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; @@ -17,20 +19,15 @@ public class GoogleAuthUriImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return null; + protected SuFile getAppPath() { + throw new UnsupportedOperationException(); } @Override - protected String getAppSubPath() { - return null; - } - - @Override - public GoogleAuthUriImporter.State read(DatabaseImporter.FileReader reader) throws DatabaseImporterException { + public GoogleAuthUriImporter.State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { ArrayList lines = new ArrayList<>(); - try (InputStreamReader streamReader = new InputStreamReader(reader.getStream()); + try (InputStreamReader streamReader = new InputStreamReader(stream); BufferedReader bufferedReader = new BufferedReader(streamReader)) { String line; while ((line = bufferedReader.readLine()) != null) { diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/MicrosoftAuthImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/MicrosoftAuthImporter.java index 33af0eae..84da1ce7 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/MicrosoftAuthImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/MicrosoftAuthImporter.java @@ -1,6 +1,7 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import com.beemdevelopment.aegis.encoding.Base32; @@ -10,7 +11,9 @@ import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; +import java.io.InputStream; import java.util.List; public class MicrosoftAuthImporter extends DatabaseImporter { @@ -25,19 +28,22 @@ public class MicrosoftAuthImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { SqlImporterHelper helper = new SqlImporterHelper(getContext()); - List entries = helper.read(Entry.class, reader.getStream(), "accounts"); + List entries = helper.read(Entry.class, stream, "accounts"); + return new State(entries); + } + + @Override + public DatabaseImporter.State readFromApp() throws PackageManager.NameNotFoundException, DatabaseImporterException { + SuFile path = getAppPath(); + SqlImporterHelper helper = new SqlImporterHelper(getContext()); + List entries = helper.read(Entry.class, path, "accounts"); return new State(entries); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java b/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java index f7cd62a8..dac6748a 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java @@ -5,7 +5,10 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; -import com.topjohnwu.superuser.ShellUtils; +import com.beemdevelopment.aegis.util.IOUtils; +import com.google.common.io.Files; +import com.topjohnwu.superuser.io.SuFile; +import com.topjohnwu.superuser.io.SuFileInputStream; import java.io.File; import java.io.FileOutputStream; @@ -24,19 +27,75 @@ public class SqlImporterHelper { _context = context; } - public List read(Class type, InputStream inStream, String table) throws DatabaseImporterException { - File file; + public List read(Class type, SuFile path, String table) throws DatabaseImporterException { + File dir = Files.createTempDir(); + File mainFile = new File(dir, path.getName()); + List fileCopies = new ArrayList<>(); + for (SuFile file : SqlImporterHelper.findDatabaseFiles(path)) { + // create temporary copies of the database files so that SQLiteDatabase can open them + File fileCopy = null; + try (SuFileInputStream inStream = new SuFileInputStream(file)) { + fileCopy = new File(dir, file.getName()); + try (FileOutputStream out = new FileOutputStream(fileCopy)) { + IOUtils.copy(inStream, out); + } + fileCopies.add(fileCopy); + } catch (IOException e) { + if (fileCopy != null) { + fileCopy.delete(); + } + + for (File fileCopy2 : fileCopies) { + fileCopy2.delete(); + } + + throw new DatabaseImporterException(e); + } + } + + try { + return read(type, mainFile, table); + } finally { + for (File fileCopy : fileCopies) { + fileCopy.delete(); + } + } + } + + private static SuFile[] findDatabaseFiles(SuFile path) throws DatabaseImporterException { + SuFile[] files = path.getParentFile().listFiles((d, name) -> name.startsWith(path.getName())); + if (files == null || files.length == 0) { + throw new DatabaseImporterException(String.format("File does not exist: %s", path.getAbsolutePath())); + } + + return files; + } + + public List read(Class type, InputStream inStream, String table) throws DatabaseImporterException { + File file = null; try { // create a temporary copy of the database so that SQLiteDatabase can open it file = File.createTempFile("db-import-", "", _context.getCacheDir()); try (FileOutputStream out = new FileOutputStream(file)) { - ShellUtils.pump(inStream, out); + IOUtils.copy(inStream, out); } } catch (IOException e) { + if (file != null) { + file.delete(); + } throw new DatabaseImporterException(e); } + try { + return read(type, file, table); + } finally { + // always delete the temporary file + file.delete(); + } + } + + private List read(Class type, File file, String table) throws DatabaseImporterException { try (SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, OPEN_READONLY)) { try (Cursor cursor = db.rawQuery(String.format("SELECT * FROM %s", table), null)) { List entries = new ArrayList<>(); @@ -55,9 +114,6 @@ public class SqlImporterHelper { } } catch (SQLiteException e) { throw new DatabaseImporterException(e); - } finally { - // always delete the temporary file - file.delete(); } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/SteamImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/SteamImporter.java index 28e1ff45..3ef36277 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/SteamImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/SteamImporter.java @@ -7,6 +7,7 @@ import com.beemdevelopment.aegis.encoding.Base64; import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.SteamInfo; +import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.vault.VaultEntry; import com.topjohnwu.superuser.io.SuFile; @@ -14,6 +15,7 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; public class SteamImporter extends DatabaseImporter { @@ -25,27 +27,22 @@ public class SteamImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; - } - - @Override - protected String getAppSubPath() throws DatabaseImporterException, PackageManager.NameNotFoundException { + protected SuFile getAppPath() throws DatabaseImporterException, PackageManager.NameNotFoundException { // NOTE: this assumes that a global root shell has already been obtained by the caller - SuFile path = getAppPath(getAppPkgName(), _subDir); + SuFile path = getAppPath(_pkgName, _subDir); SuFile[] files = path.listFiles((d, name) -> name.startsWith("Steamguard-")); if (files == null || files.length == 0) { throw new DatabaseImporterException(String.format("Empty directory: %s", path.getAbsolutePath())); } // TODO: handle multiple files (can this even occur?) - return new SuFile(_subDir, files[0].getName()).getPath(); + return files[0]; } @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { - byte[] bytes = reader.readAll(); + byte[] bytes = IOUtils.readAll(stream); JSONObject obj = new JSONObject(new String(bytes, StandardCharsets.UTF_8)); return new State(obj); } catch (IOException | JSONException e) { diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java index cd21e3d2..95c4b9ff 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java @@ -1,6 +1,7 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; +import android.content.pm.PackageManager; import android.util.Xml; import androidx.appcompat.app.AlertDialog; @@ -14,8 +15,10 @@ import com.beemdevelopment.aegis.encoding.Hex; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.ui.Dialogs; +import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.util.PreferenceParser; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; import org.json.JSONArray; import org.json.JSONException; @@ -24,6 +27,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -59,22 +63,17 @@ public class TotpAuthenticatorImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return _pkgName; + protected SuFile getAppPath() throws PackageManager.NameNotFoundException { + return getAppPath(_pkgName, _subPath); } @Override - protected String getAppSubPath() { - return _subPath; - } - - @Override - public State read(FileReader reader) throws DatabaseImporterException { + public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { try { - if (reader.isInternal()) { + if (isInternal) { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(reader.getStream(), null); + parser.setInput(stream, null); parser.nextTag(); String data = null; @@ -91,7 +90,7 @@ public class TotpAuthenticatorImporter extends DatabaseImporter { List entries = parse(data); return new DecryptedState(entries); } else { - byte[] base64 = reader.readAll(); + byte[] base64 = IOUtils.readAll(stream); byte[] cipherText = Base64.decode(base64); return new EncryptedState(cipherText); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/WinAuthImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/WinAuthImporter.java index 774a31c2..6af3ee60 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/WinAuthImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/WinAuthImporter.java @@ -3,6 +3,9 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; import com.beemdevelopment.aegis.vault.VaultEntry; +import com.topjohnwu.superuser.io.SuFile; + +import java.io.InputStream; public class WinAuthImporter extends DatabaseImporter { public WinAuthImporter(Context context) { @@ -10,32 +13,27 @@ public class WinAuthImporter extends DatabaseImporter { } @Override - protected String getAppPkgName() { - return null; + protected SuFile getAppPath() { + throw new UnsupportedOperationException(); } @Override - protected String getAppSubPath() { - return null; - } - - @Override - public WinAuthImporter.State read(FileReader reader) throws DatabaseImporterException { + public WinAuthImporter.State read(InputStream stream, boolean isInternal) throws DatabaseImporterException { GoogleAuthUriImporter importer = new GoogleAuthUriImporter(getContext()); - GoogleAuthUriImporter.State state = importer.read(reader); + DatabaseImporter.State state = importer.read(stream); return new State(state); } public static class State extends DatabaseImporter.State { - private GoogleAuthUriImporter.State _state; + private DatabaseImporter.State _state; - private State(GoogleAuthUriImporter.State state) { + private State(DatabaseImporter.State state) { super(false); _state = state; } @Override - public Result convert() { + public Result convert() throws DatabaseImporterException { Result result = _state.convert(); for (VaultEntry entry : result.getEntries()) { diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java index 103e0dfe..b380d665 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java @@ -49,8 +49,6 @@ import com.beemdevelopment.aegis.vault.slots.Slot; import com.beemdevelopment.aegis.vault.slots.SlotException; import com.beemdevelopment.aegis.vault.slots.SlotList; import com.topjohnwu.superuser.Shell; -import com.topjohnwu.superuser.io.SuFile; -import com.topjohnwu.superuser.io.SuFileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -527,11 +525,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat { return; } - SuFile file = importer.getAppPath(); - try (SuFileInputStream stream = new SuFileInputStream(file)) { - DatabaseImporter.FileReader reader = new DatabaseImporter.FileReader(stream, true); - importDatabase(importer, reader); - } + DatabaseImporter.State state = importer.readFromApp(); + processImporterState(state); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); Toast.makeText(getActivity(), R.string.app_lookup_error, Toast.LENGTH_SHORT).show(); @@ -541,9 +536,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat { } } - private void importDatabase(DatabaseImporter importer, DatabaseImporter.FileReader reader) { + private void processImporterState(DatabaseImporter.State state) { try { - DatabaseImporter.State state = importer.read(reader); if (state.isEncrypted()) { // temporary special case for encrypted Aegis vaults if (state instanceof AegisImporter.EncryptedState) { @@ -604,11 +598,11 @@ public class PreferencesFragment extends PreferenceFragmentCompat { try (InputStream stream = getContext().getContentResolver().openInputStream(uri)) { DatabaseImporter importer = DatabaseImporter.create(getContext(), _importerType); - DatabaseImporter.FileReader reader = new DatabaseImporter.FileReader(stream); - importDatabase(importer, reader); + DatabaseImporter.State state = importer.read(stream); + processImporterState(state); } catch (FileNotFoundException e) { Toast.makeText(getActivity(), R.string.file_not_found, Toast.LENGTH_SHORT).show(); - } catch (IOException e) { + } catch (DatabaseImporterException | IOException e) { e.printStackTrace(); Dialogs.showErrorDialog(getContext(), R.string.reading_file_error, e); }