|
|
@@ -5,13 +5,19 @@ import android.content.Context;
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
import java.io.File;
|
|
|
import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
import java.util.ArrayDeque;
|
|
|
+import java.util.logging.Logger;
|
|
|
import java.util.zip.ZipEntry;
|
|
|
+import java.util.zip.ZipInputStream;
|
|
|
import java.util.zip.ZipOutputStream;
|
|
|
|
|
|
import info.knacki.gitdroid.callback.OnResponseListener;
|
|
|
+import info.knacki.pass.R;
|
|
|
+import info.knacki.pass.ui.alertPrompt.views.ConflictFileView;
|
|
|
|
|
|
public class FileMigratoryUtils {
|
|
|
+ public final static Logger log = Logger.getLogger(FileMigratoryUtils.class.getName());
|
|
|
public interface MigratePasswordCondition {
|
|
|
boolean isRelevantForMigration(File f);
|
|
|
}
|
|
|
@@ -19,7 +25,7 @@ public class FileMigratoryUtils {
|
|
|
private static void ListPasswords(File dir, MigratePasswordCondition condition, ArrayDeque<File> arr) {
|
|
|
if (!dir.exists() || !dir.isDirectory())
|
|
|
return;
|
|
|
- for (File f: dir.listFiles()) {
|
|
|
+ for (File f : dir.listFiles()) {
|
|
|
if (f.isHidden())
|
|
|
continue;
|
|
|
if (f.isDirectory())
|
|
|
@@ -41,8 +47,7 @@ public class FileMigratoryUtils {
|
|
|
public void OnResponse(String result) {
|
|
|
try {
|
|
|
to.WriteFile(result, onDone);
|
|
|
- }
|
|
|
- catch (IFileInterface.InvalidPasswordException e) {
|
|
|
+ } catch (IFileInterface.InvalidPasswordException e) {
|
|
|
OnError(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
@@ -77,8 +82,57 @@ public class FileMigratoryUtils {
|
|
|
IFileInterface GetOutput(File input);
|
|
|
}
|
|
|
|
|
|
- private static void MigratePasswords(final Context ctx, final FileInterfaceFactory.PasswordGetter passwordGetter, final OutputGetter outputGetter, ArrayDeque<File> inputFiles, OnResponseListener<Void> onDone) {
|
|
|
- final ArrayDeque<String> oldPasswords = new ArrayDeque<>();
|
|
|
+ private static class PasswordGetterWithCache implements FileInterfaceFactory.PasswordGetter {
|
|
|
+ public final ArrayDeque<String> fCache = new ArrayDeque<>();
|
|
|
+ public final ArrayDeque<String> fAllCache = new ArrayDeque<>();
|
|
|
+ public final FileInterfaceFactory.PasswordGetter fOther;
|
|
|
+
|
|
|
+ public PasswordGetterWithCache(FileInterfaceFactory.PasswordGetter other) {
|
|
|
+ fOther = other;
|
|
|
+ }
|
|
|
+
|
|
|
+ public PasswordGetterWithCache reset() {
|
|
|
+ fCache.clear();
|
|
|
+ fCache.addAll(fAllCache);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public FileInterfaceFactory.PasswordGetter GetPassword(FileInterfaceFactory.OnPasswordEnteredListener onPassword) {
|
|
|
+ if (fCache.isEmpty()) {
|
|
|
+ fOther.GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
|
|
|
+ @Override
|
|
|
+ public boolean OnResponse(String password) {
|
|
|
+ fAllCache.push(password);
|
|
|
+ return onPassword.OnResponse(password);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void OnError(String msg, Throwable e) {
|
|
|
+ onPassword.OnError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ onPassword.OnResponse(fCache.pop());
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public FileInterfaceFactory.PasswordGetter SetFile(File file) {
|
|
|
+ fOther.SetFile(file);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public FileInterfaceFactory.PasswordGetter SetTitle(String title) {
|
|
|
+ fOther.SetTitle(title);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void MigratePasswords(final Context ctx, final FileInterfaceFactory.PasswordGetter _passwordGetter, final OutputGetter outputGetter, ArrayDeque<File> inputFiles, OnResponseListener<Void> onDone) {
|
|
|
+ PasswordGetterWithCache passwordGetter = new PasswordGetterWithCache(_passwordGetter);
|
|
|
|
|
|
final Runnable nextFile = new Runnable() {
|
|
|
private void NextFile() {
|
|
|
@@ -88,30 +142,8 @@ public class FileMigratoryUtils {
|
|
|
return;
|
|
|
}
|
|
|
final File currentFile = inputFiles.poll();
|
|
|
- final ArrayDeque<String> currentPasswordToTry = new ArrayDeque<>(oldPasswords);
|
|
|
- MigratePassword(ctx, currentFile, outputGetter.GetOutput(currentFile), new FileInterfaceFactory.PasswordGetter() {
|
|
|
- @Override
|
|
|
- public FileInterfaceFactory.PasswordGetter SetFile(File file) { return this; }
|
|
|
-
|
|
|
- @Override
|
|
|
- public FileInterfaceFactory.PasswordGetter GetPassword(FileInterfaceFactory.OnPasswordEnteredListener listener) {
|
|
|
- if (!currentPasswordToTry.isEmpty())
|
|
|
- listener.OnResponse(currentPasswordToTry.poll());
|
|
|
- else
|
|
|
- passwordGetter.SetFile(currentFile).GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
|
|
|
- @Override
|
|
|
- public boolean OnResponse(String pass) {
|
|
|
- oldPasswords.add(pass);
|
|
|
- return listener.OnResponse(pass);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void OnError(String what, Throwable e) {
|
|
|
- }
|
|
|
- });
|
|
|
- return this;
|
|
|
- }
|
|
|
- }, new OnResponseListener<Void>() {
|
|
|
+ passwordGetter.reset().SetFile(currentFile);
|
|
|
+ MigratePassword(ctx, currentFile, outputGetter.GetOutput(currentFile), passwordGetter, new OnResponseListener<Void>() {
|
|
|
@Override
|
|
|
public void OnResponse(Void result) {
|
|
|
NextFile();
|
|
|
@@ -138,11 +170,129 @@ public class FileMigratoryUtils {
|
|
|
return absPath.substring(rootDir.length());
|
|
|
}
|
|
|
|
|
|
+ private static class ImportResult {
|
|
|
+ public final ConflictFileView.eDefaultBehaviour fOnFileExists;
|
|
|
+ public final boolean fWritten;
|
|
|
+
|
|
|
+ public ImportResult(ConflictFileView.eDefaultBehaviour onFileExists, boolean written) {
|
|
|
+ fOnFileExists = onFileExists;
|
|
|
+ fWritten = written;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void ImportFromStream(final Context ctx, byte[] in, final String fileName, PasswordGetterWithCache passwordGetter, OnResponseListener<ImportResult> onDone, ConflictFileView.eDefaultBehaviour onFileExists) {
|
|
|
+ class DoWrite {
|
|
|
+ public void DoWrite(File output, String result, ConflictFileView.eDefaultBehaviour finalOnFileExists) {
|
|
|
+ if (output.exists())
|
|
|
+ output.delete();
|
|
|
+ try {
|
|
|
+ FileInterfaceFactory.GetFileInterface(ctx, null, output).WriteFile(result, new OnResponseListener<Void>() {
|
|
|
+ @Override
|
|
|
+ public void OnResponse(Void result) {
|
|
|
+ onDone.OnResponse(new ImportResult(finalOnFileExists, true));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void OnError(String msg, Throwable e) {
|
|
|
+ onDone.OnError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ catch (IFileInterface.InvalidPasswordException e) {
|
|
|
+ onDone.OnError("Cannot write file " +fileName, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ final DoWrite _do = new DoWrite();
|
|
|
+ (new PasswordFileInterface(ctx, passwordGetter, in)).ReadFile(new OnResponseListener<String>() {
|
|
|
+ @Override
|
|
|
+ public void OnResponse(String result) {
|
|
|
+ File output = new File(PathUtils.GetPassDir(ctx), fileName);
|
|
|
+ if (!output.exists() || ConflictFileView.eDefaultBehaviour.eOverride.equals(onFileExists)) {
|
|
|
+ _do.DoWrite(output, result, onFileExists);
|
|
|
+ } else if (ConflictFileView.eDefaultBehaviour.eUnknown.equals(onFileExists)) {
|
|
|
+ ConflictFileView.Show(ctx, fileName, new OnResponseListener<ConflictFileView.Result>() {
|
|
|
+ @Override
|
|
|
+ public void OnResponse(ConflictFileView.Result conflictResult) {
|
|
|
+ if (ConflictFileView.eDefaultBehaviour.eOverride.equals(conflictResult.fResponse))
|
|
|
+ _do.DoWrite(output, result, conflictResult.fSave ? conflictResult.fResponse : ConflictFileView.eDefaultBehaviour.eUnknown);
|
|
|
+ else
|
|
|
+ onDone.OnResponse(new ImportResult(conflictResult.fSave ? conflictResult.fResponse : ConflictFileView.eDefaultBehaviour.eUnknown, false));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void OnError(String msg, Throwable e) {
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ onDone.OnResponse(new ImportResult(onFileExists, false));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void OnError(String msg, Throwable e) {
|
|
|
+ onDone.OnError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void ImportAllPasswords(final Context ctx, InputStream in, FileInterfaceFactory.PasswordGetter _passwordGetter, OnResponseListener<Integer> onDone) {
|
|
|
+ final ZipInputStream stream = new ZipInputStream(in);
|
|
|
+ final PasswordGetterWithCache passwordGetter = new PasswordGetterWithCache(_passwordGetter);
|
|
|
+
|
|
|
+ final Runnable nextFile = new Runnable() {
|
|
|
+ private int importedFileCount = 0;
|
|
|
+ public void Next(ConflictFileView.eDefaultBehaviour onFileExists) {
|
|
|
+ ZipEntry currentEntry = null;
|
|
|
+ try {
|
|
|
+ currentEntry = stream.getNextEntry();
|
|
|
+ if (currentEntry != null) {
|
|
|
+ passwordGetter.reset();
|
|
|
+ byte[] bb = FileUtils.ReadAllStream(stream);
|
|
|
+ ImportFromStream(ctx, bb, currentEntry.getName(), passwordGetter, new OnResponseListener<ImportResult>() {
|
|
|
+ @Override
|
|
|
+ public void OnResponse(ImportResult result) {
|
|
|
+ if (result.fWritten)
|
|
|
+ importedFileCount++;
|
|
|
+ Next(result.fOnFileExists);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void OnError(String msg, Throwable e) {
|
|
|
+ onDone.OnError(msg, e);
|
|
|
+ }
|
|
|
+ }, onFileExists);
|
|
|
+ } else {
|
|
|
+ onDone.OnResponse(importedFileCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (IOException e) {
|
|
|
+ if (currentEntry != null)
|
|
|
+ onDone.OnError("Cannot read from file " +currentEntry.getName(), e);
|
|
|
+ else
|
|
|
+ onDone.OnError("Cannot read from ZIP file", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ Next(ConflictFileView.eDefaultBehaviour.eUnknown);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ nextFile.run();
|
|
|
+ }
|
|
|
+
|
|
|
public static void ExportAllPasswords(final Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter, OnResponseListener<byte[]> onDone) {
|
|
|
// Get output password
|
|
|
- passwordGetter.GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
|
|
|
+ passwordGetter.SetTitle(ctx.getResources().getString(R.string.enter_output_password)).GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
|
|
|
@Override
|
|
|
public boolean OnResponse(String outputPassword) {
|
|
|
+ if (outputPassword == null) {
|
|
|
+ onDone.OnResponse(null);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
ZipOutputStream compressedStream = new ZipOutputStream(out);
|
|
|
|