mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-30 13:12:25 -05:00
Put all import code in StorageImporter
Get rid of StorageImporterEncryptedXml and IStorageImporter. Also AsyncUIProcessor is now obsolete.
This commit is contained in:
parent
45afa3a747
commit
d5197fdc56
@ -1,104 +0,0 @@
|
|||||||
package com.fsck.k9.activity;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.preferences.StorageImporter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The class should be used to run long-running processes invoked from the UI that
|
|
||||||
* do not affect the Stores. There are probably pieces of MessagingController
|
|
||||||
* that can be moved here.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class AsyncUIProcessor {
|
|
||||||
|
|
||||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
|
||||||
private Application mApplication;
|
|
||||||
private static AsyncUIProcessor inst = null;
|
|
||||||
private AsyncUIProcessor(Application application) {
|
|
||||||
mApplication = application;
|
|
||||||
}
|
|
||||||
public synchronized static AsyncUIProcessor getInstance(Application application) {
|
|
||||||
if (inst == null) {
|
|
||||||
inst = new AsyncUIProcessor(application);
|
|
||||||
}
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
public void execute(Runnable runnable) {
|
|
||||||
threadPool.execute(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void importSettings(final Activity activity, final Uri uri, final ImportListener listener) {
|
|
||||||
threadPool.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
ContentResolver resolver = mApplication.getContentResolver();
|
|
||||||
is = resolver.openInputStream(uri);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(K9.LOG_TAG, "Exception while resolving Uri to InputStream", e);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.failure(e.getLocalizedMessage(), e);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final InputStream myIs = is;
|
|
||||||
StorageImporter.importPreferences(activity, is, null, new ImportListener() {
|
|
||||||
@Override
|
|
||||||
public void failure(String message, Exception e) {
|
|
||||||
quietClose(myIs);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.failure(message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void success(int numAccounts) {
|
|
||||||
quietClose(myIs);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.success(numAccounts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void canceled() {
|
|
||||||
quietClose(myIs);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.canceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void started() {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.started();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void quietClose(InputStream is) {
|
|
||||||
if (is != null) {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(K9.LOG_TAG, "Unable to close inputStream", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.fsck.k9.preferences;
|
|
||||||
|
|
||||||
import com.fsck.k9.Preferences;
|
|
||||||
import com.fsck.k9.preferences.StorageImporter.ImportElement;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
public interface IStorageImporter {
|
|
||||||
public boolean needsKey();
|
|
||||||
public abstract int importPreferences(Preferences preferences, SharedPreferences.Editor context, ImportElement dataset, String encryptionKey) throws StorageImportExportException;
|
|
||||||
}
|
|
@ -1,9 +1,13 @@
|
|||||||
package com.fsck.k9.preferences;
|
package com.fsck.k9.preferences;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.xml.parsers.SAXParser;
|
import javax.xml.parsers.SAXParser;
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
@ -14,21 +18,21 @@ import org.xml.sax.SAXException;
|
|||||||
import org.xml.sax.XMLReader;
|
import org.xml.sax.XMLReader;
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.R;
|
|
||||||
import com.fsck.k9.activity.AsyncUIProcessor;
|
|
||||||
import com.fsck.k9.activity.ImportListener;
|
|
||||||
import com.fsck.k9.activity.PasswordEntryDialog;
|
|
||||||
import com.fsck.k9.helper.DateFormatter;
|
import com.fsck.k9.helper.DateFormatter;
|
||||||
|
|
||||||
public class StorageImporter {
|
public class StorageImporter {
|
||||||
|
|
||||||
public static void importPreferences(Activity activity, InputStream is, String providedEncryptionKey, ImportListener listener) {
|
public static void importPreferences(Context context, InputStream is, String encryptionKey,
|
||||||
|
boolean globalSettings, String[] importAccountUuids, boolean overwrite)
|
||||||
|
throws StorageImportExportException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||||
SAXParser sp = spf.newSAXParser();
|
SAXParser sp = spf.newSAXParser();
|
||||||
@ -42,74 +46,88 @@ public class StorageImporter {
|
|||||||
String storageFormat = dataset.attributes.get("version");
|
String storageFormat = dataset.attributes.get("version");
|
||||||
Log.i(K9.LOG_TAG, "Got settings file version " + storageFormat);
|
Log.i(K9.LOG_TAG, "Got settings file version " + storageFormat);
|
||||||
|
|
||||||
IStorageImporter storageImporter = new StorageImporterEncryptedXml();
|
Preferences preferences = Preferences.getPreferences(context);
|
||||||
if (storageImporter.needsKey() && providedEncryptionKey == null) {
|
SharedPreferences storage = preferences.getPreferences();
|
||||||
gatherPassword(activity, storageImporter, dataset, listener);
|
SharedPreferences.Editor editor = storage.edit();
|
||||||
} else {
|
|
||||||
finishImport(activity, storageImporter, dataset, providedEncryptionKey, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.failure(e.getLocalizedMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void finishImport(Activity context, IStorageImporter storageImporter, ImportElement dataset, String encryptionKey, ImportListener listener) throws StorageImportExportException {
|
String data = dataset.data.toString();
|
||||||
if (listener != null) {
|
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
||||||
listener.started();
|
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
||||||
}
|
/**
|
||||||
Preferences preferences = Preferences.getPreferences(context);
|
* We translate UUIDs in the import file into new UUIDs in the local instance for the following reasons:
|
||||||
SharedPreferences storage = preferences.getPreferences();
|
* 1) Accidentally importing the same file twice cannot damage settings in an existing account.
|
||||||
SharedPreferences.Editor editor = storage.edit();
|
* (Say, an account that was imported two months ago and has since had significant settings changes.)
|
||||||
int numAccounts = 0;
|
* 2) Importing a single file multiple times allows for creating multiple accounts from the same template.
|
||||||
if (storageImporter != null) {
|
* 3) Exporting an account and importing back into the same instance is a poor-man's account copy (until a real
|
||||||
numAccounts = storageImporter.importPreferences(preferences, editor, dataset, encryptionKey);
|
* copy function is created, if ever)
|
||||||
}
|
*/
|
||||||
editor.commit();
|
Map<String, String> uuidMapping = new HashMap<String, String>();
|
||||||
Preferences.getPreferences(context).refreshAccounts();
|
String accountUuids = preferences.getPreferences().getString("accountUuids", null);
|
||||||
DateFormatter.clearChosenFormat();
|
|
||||||
K9.loadPrefs(Preferences.getPreferences(context));
|
|
||||||
K9.setServicesEnabled(context);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.success(numAccounts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void gatherPassword(final Activity activity, final IStorageImporter storageImporter, final ImportElement dataset, final ImportListener listener) {
|
StringReader sr = new StringReader(data);
|
||||||
activity.runOnUiThread(new Runnable() {
|
BufferedReader br = new BufferedReader(sr);
|
||||||
@Override
|
String line = null;
|
||||||
public void run() {
|
int settingsImported = 0;
|
||||||
PasswordEntryDialog dialog = new PasswordEntryDialog(activity, activity.getString(R.string.settings_import_encryption_password_prompt),
|
int numAccounts = 0;
|
||||||
new PasswordEntryDialog.PasswordEntryListener() {
|
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.DECRYPT);
|
||||||
public void passwordChosen(final String chosenPassword) {
|
do {
|
||||||
AsyncUIProcessor.getInstance(activity.getApplication()).execute(new Runnable() {
|
line = br.readLine();
|
||||||
@Override
|
if (line != null) {
|
||||||
public void run() {
|
//Log.i(K9.LOG_TAG, "Got line " + line);
|
||||||
try {
|
String[] comps = line.split(":");
|
||||||
finishImport(activity, storageImporter, dataset, chosenPassword, listener);
|
if (comps.length > 1) {
|
||||||
} catch (Exception e) {
|
String keyEnc = comps[0];
|
||||||
Log.w(K9.LOG_TAG, "Failure during import", e);
|
String valueEnc = comps[1];
|
||||||
if (listener != null) {
|
String key = krypto.decrypt(keyEnc);
|
||||||
listener.failure(e.getLocalizedMessage(), e);
|
String value = krypto.decrypt(valueEnc);
|
||||||
}
|
String[] keyParts = key.split("\\.");
|
||||||
}
|
if (keyParts.length > 1) {
|
||||||
|
String oldUuid = keyParts[0];
|
||||||
|
String newUuid = uuidMapping.get(oldUuid);
|
||||||
|
if (newUuid == null) {
|
||||||
|
newUuid = UUID.randomUUID().toString();
|
||||||
|
uuidMapping.put(oldUuid, newUuid);
|
||||||
|
|
||||||
|
Log.i(K9.LOG_TAG, "Mapping oldUuid " + oldUuid + " to newUuid " + newUuid);
|
||||||
}
|
}
|
||||||
});
|
keyParts[0] = newUuid;
|
||||||
}
|
if ("accountNumber".equals(keyParts[1])) {
|
||||||
|
int accountNumber = Account.findNewAccountNumber(accountNumbers);
|
||||||
public void cancel() {
|
accountNumbers.add(accountNumber);
|
||||||
if (listener != null) {
|
value = Integer.toString(accountNumber);
|
||||||
listener.canceled();
|
accountUuids += (accountUuids.length() != 0 ? "," : "") + newUuid;
|
||||||
|
numAccounts++;
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (String part : keyParts) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(".");
|
||||||
|
}
|
||||||
|
builder.append(part);
|
||||||
|
}
|
||||||
|
key = builder.toString();
|
||||||
}
|
}
|
||||||
|
//Log.i(K9.LOG_TAG, "Setting " + key + " = " + value);
|
||||||
|
settingsImported++;
|
||||||
|
editor.putString(key, value);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
dialog.show();
|
} while (line != null);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
editor.putString("accountUuids", accountUuids);
|
||||||
|
Log.i(K9.LOG_TAG, "Imported " + settingsImported + " settings and " + numAccounts + " accounts");
|
||||||
|
|
||||||
|
editor.commit();
|
||||||
|
Preferences.getPreferences(context).refreshAccounts();
|
||||||
|
DateFormatter.clearChosenFormat();
|
||||||
|
K9.loadPrefs(Preferences.getPreferences(context));
|
||||||
|
K9.setServicesEnabled(context);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new StorageImportExportException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class ImportElement {
|
public static class ImportElement {
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
package com.fsck.k9.preferences;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.Preferences;
|
|
||||||
import com.fsck.k9.preferences.StorageImporter.ImportElement;
|
|
||||||
|
|
||||||
public class StorageImporterEncryptedXml implements IStorageImporter {
|
|
||||||
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, ImportElement dataset, String encryptionKey) throws StorageImportExportException {
|
|
||||||
try {
|
|
||||||
|
|
||||||
String data = dataset.data.toString();
|
|
||||||
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
|
||||||
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
|
||||||
/**
|
|
||||||
* We translate UUIDs in the import file into new UUIDs in the local instance for the following reasons:
|
|
||||||
* 1) Accidentally importing the same file twice cannot damage settings in an existing account.
|
|
||||||
* (Say, an account that was imported two months ago and has since had significant settings changes.)
|
|
||||||
* 2) Importing a single file multiple times allows for creating multiple accounts from the same template.
|
|
||||||
* 3) Exporting an account and importing back into the same instance is a poor-man's account copy (until a real
|
|
||||||
* copy function is created, if ever)
|
|
||||||
*/
|
|
||||||
Map<String, String> uuidMapping = new HashMap<String, String>();
|
|
||||||
String accountUuids = preferences.getPreferences().getString("accountUuids", null);
|
|
||||||
|
|
||||||
StringReader sr = new StringReader(data);
|
|
||||||
BufferedReader br = new BufferedReader(sr);
|
|
||||||
String line = null;
|
|
||||||
int settingsImported = 0;
|
|
||||||
int numAccounts = 0;
|
|
||||||
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.DECRYPT);
|
|
||||||
do {
|
|
||||||
line = br.readLine();
|
|
||||||
if (line != null) {
|
|
||||||
//Log.i(K9.LOG_TAG, "Got line " + line);
|
|
||||||
String[] comps = line.split(":");
|
|
||||||
if (comps.length > 1) {
|
|
||||||
String keyEnc = comps[0];
|
|
||||||
String valueEnc = comps[1];
|
|
||||||
String key = krypto.decrypt(keyEnc);
|
|
||||||
String value = krypto.decrypt(valueEnc);
|
|
||||||
String[] keyParts = key.split("\\.");
|
|
||||||
if (keyParts.length > 1) {
|
|
||||||
String oldUuid = keyParts[0];
|
|
||||||
String newUuid = uuidMapping.get(oldUuid);
|
|
||||||
if (newUuid == null) {
|
|
||||||
newUuid = UUID.randomUUID().toString();
|
|
||||||
uuidMapping.put(oldUuid, newUuid);
|
|
||||||
|
|
||||||
Log.i(K9.LOG_TAG, "Mapping oldUuid " + oldUuid + " to newUuid " + newUuid);
|
|
||||||
}
|
|
||||||
keyParts[0] = newUuid;
|
|
||||||
if ("accountNumber".equals(keyParts[1])) {
|
|
||||||
int accountNumber = Account.findNewAccountNumber(accountNumbers);
|
|
||||||
accountNumbers.add(accountNumber);
|
|
||||||
value = Integer.toString(accountNumber);
|
|
||||||
accountUuids += (accountUuids.length() != 0 ? "," : "") + newUuid;
|
|
||||||
numAccounts++;
|
|
||||||
}
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (String part : keyParts) {
|
|
||||||
if (builder.length() > 0) {
|
|
||||||
builder.append(".");
|
|
||||||
}
|
|
||||||
builder.append(part);
|
|
||||||
}
|
|
||||||
key = builder.toString();
|
|
||||||
}
|
|
||||||
//Log.i(K9.LOG_TAG, "Setting " + key + " = " + value);
|
|
||||||
settingsImported++;
|
|
||||||
editor.putString(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (line != null);
|
|
||||||
|
|
||||||
editor.putString("accountUuids", accountUuids);
|
|
||||||
Log.i(K9.LOG_TAG, "Imported " + settingsImported + " settings and " + numAccounts + " accounts");
|
|
||||||
return numAccounts;
|
|
||||||
} catch (IOException ie) {
|
|
||||||
throw new StorageImportExportException("Unable to import settings", ie);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new StorageImportExportException("Unable to decrypt settings", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsKey() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user