Fix export for new unified key list #409

This commit is contained in:
uberspot 2014-03-15 01:51:01 +02:00
parent efab1d27ac
commit e387dd7c54
9 changed files with 113 additions and 80 deletions

View File

@ -119,6 +119,7 @@ public final class Id {
public static final int secret_key = 0x21070002; public static final int secret_key = 0x21070002;
public static final int user_id = 0x21070003; public static final int user_id = 0x21070003;
public static final int key = 0x21070004; public static final int key = 0x21070004;
public static final int public_secret_key = 0x21070005;
} }
public static final class choice { public static final class choice {

View File

@ -30,12 +30,18 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.lang.reflect.Array;
import java.security.Provider;
import java.util.ArrayList;
public class ExportHelper { public class ExportHelper {
protected FileDialogFragment mFileDialog; protected FileDialogFragment mFileDialog;
protected String mExportFilename; protected String mExportFilename;
@ -62,8 +68,8 @@ public class ExportHelper {
/** /**
* Show dialog where to export keys * Show dialog where to export keys
*/ */
public void showExportKeysDialog(final long[] rowIds, final int keyType, public void showExportKeysDialog(final long[] masterKeyIds, final int keyType,
final String exportFilename) { final String exportFilename, final String checkboxString) {
mExportFilename = exportFilename; mExportFilename = exportFilename;
// Message is received after file is selected // Message is received after file is selected
@ -72,9 +78,14 @@ public class ExportHelper {
public void handleMessage(Message message) { public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) { if (message.what == FileDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData(); Bundle data = message.getData();
int type = keyType;
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
exportKeys(rowIds, keyType); if( data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED) ) {
type = Id.type.public_secret_key;
}
exportKeys(masterKeyIds, type);
} }
} }
}; };
@ -85,7 +96,7 @@ public class ExportHelper {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() { public void run() {
String title = null; String title = null;
if (rowIds == null) { if (masterKeyIds == null) {
// export all keys // export all keys
title = mActivity.getString(R.string.title_export_keys); title = mActivity.getString(R.string.title_export_keys);
} else { } else {
@ -93,15 +104,10 @@ public class ExportHelper {
title = mActivity.getString(R.string.title_export_key); title = mActivity.getString(R.string.title_export_key);
} }
String message = null; String message = mActivity.getString(R.string.specify_file_to_export_to);
if (keyType == Id.type.public_key) {
message = mActivity.getString(R.string.specify_file_to_export_to);
} else {
message = mActivity.getString(R.string.specify_file_to_export_secret_keys_to);
}
mFileDialog = FileDialogFragment.newInstance(messenger, title, message, mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
exportFilename, null); exportFilename, checkboxString);
mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog"); mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
} }
@ -111,7 +117,7 @@ public class ExportHelper {
/** /**
* Export keys * Export keys
*/ */
public void exportKeys(long[] rowIds, int keyType) { public void exportKeys(long[] masterKeyIds, int keyType) {
Log.d(Constants.TAG, "exportKeys started"); Log.d(Constants.TAG, "exportKeys started");
// Send all information needed to service to export key in other thread // Send all information needed to service to export key in other thread
@ -125,10 +131,10 @@ public class ExportHelper {
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType); data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
if (rowIds == null) { if (masterKeyIds == null) {
data.putBoolean(KeychainIntentService.EXPORT_ALL, true); data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
} else { } else {
data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds); data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
} }
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View File

@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.Provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -158,60 +159,68 @@ public class PgpImportExport {
return returnData; return returnData;
} }
public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType, public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds, ArrayList<Long> secretKeyRingMasterIds,
OutputStream outStream) throws PgpGeneralException, OutputStream outStream) throws PgpGeneralException,
PGPException, IOException { PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
int rowIdsSize = keyRingRowIds.size(); int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
int progress = 0;
updateProgress( updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
rowIdsSize), 0, 100); masterKeyIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException( throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready)); mContext.getString(R.string.error_external_storage_not_ready));
} }
// For each row id // For each public masterKey id
for (int i = 0; i < rowIdsSize; ++i) { for (long pubKeyMasterId : publicKeyRingMasterIds) {
progress++;
// Create an output stream // Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext)); arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
// If the keyType is secret get the PGPSecretKeyRing updateProgress(progress * 100 / masterKeyIdsSize, 100);
// based on the row id and encode it to the output PGPPublicKeyRing publicKeyRing =
if (keyType == Id.type.secret_key) { ProviderHelper.getPGPPublicKeyRingByMasterKeyId(mContext, pubKeyMasterId);
updateProgress(i * 100 / rowIdsSize / 2, 100);
PGPSecretKeyRing secretKeyRing =
ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (secretKeyRing != null) { if (publicKeyRing != null) {
secretKeyRing.encode(arOutStream); publicKeyRing.encode(arOutStream);
} }
if (mKeychainServiceListener.hasServiceStopped()) {
arOutStream.close();
return null;
}
} else {
updateProgress(i * 100 / rowIdsSize, 100);
PGPPublicKeyRing publicKeyRing =
ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (publicKeyRing != null) { if (mKeychainServiceListener.hasServiceStopped()) {
publicKeyRing.encode(arOutStream); arOutStream.close();
} return null;
if (mKeychainServiceListener.hasServiceStopped()) {
arOutStream.close();
return null;
}
} }
arOutStream.close(); arOutStream.close();
} }
returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize); // For each secret masterKey id
for (long secretKeyMasterId : secretKeyRingMasterIds) {
progress++;
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
updateProgress(progress * 100 / masterKeyIdsSize, 100);
PGPSecretKeyRing secretKeyRing =
ProviderHelper.getPGPSecretKeyRingByMasterKeyId(mContext, secretKeyMasterId);
if (secretKeyRing != null) {
secretKeyRing.encode(arOutStream);
}
if (mKeychainServiceListener.hasServiceStopped()) {
arOutStream.close();
return null;
}
arOutStream.close();
}
returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
updateProgress(R.string.progress_done, 100, 100); updateProgress(R.string.progress_done, 100, 100);

View File

@ -131,7 +131,6 @@ public class KeychainIntentService extends IntentService
public static final String EXPORT_KEY_TYPE = "export_key_type"; public static final String EXPORT_KEY_TYPE = "export_key_type";
public static final String EXPORT_ALL = "export_all"; public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id"; public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
public static final String EXPORT_KEY_RING_ROW_ID = "export_key_rind_row_id";
// upload key // upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server"; public static final String UPLOAD_KEY_SERVER = "upload_key_server";
@ -660,16 +659,11 @@ public class KeychainIntentService extends IntentService
if (data.containsKey(EXPORT_KEY_TYPE)) { if (data.containsKey(EXPORT_KEY_TYPE)) {
keyType = data.getInt(EXPORT_KEY_TYPE); keyType = data.getInt(EXPORT_KEY_TYPE);
} }
long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
String outputFile = data.getString(EXPORT_FILENAME); String outputFile = data.getString(EXPORT_FILENAME);
long[] rowIds = new long[0]; // If not exporting all keys get the masterKeyIds of the keys to export from the intent
// If not exporting all keys get the rowIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL); boolean exportAll = data.getBoolean(EXPORT_ALL);
if (!exportAll) {
rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
}
/* Operation */ /* Operation */
@ -678,30 +672,38 @@ public class KeychainIntentService extends IntentService
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
} }
// OutputStream ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
FileOutputStream outStream = new FileOutputStream(outputFile); ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
ArrayList<Long> allPublicMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
ArrayList<Long> allSecretMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
if (exportAll) { if (exportAll) {
// get all public key ring MasterKey ids
// get all key ring row ids based on export type if (keyType == Id.type.public_key || keyType == Id.type.public_secret_key) {
if (keyType == Id.type.public_key) { publicMasterKeyIds = allPublicMasterKeyIds;
keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this); }
} else { // get all secret key ring MasterKey ids
keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this); if (keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) {
secretMasterKeyIds = allSecretMasterKeyIds;
} }
} else { } else {
for (long rowId : rowIds) {
keyRingRowIds.add(rowId); for (long masterKeyId : masterKeyIds) {
if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key)
&& allPublicMasterKeyIds.contains(masterKeyId)) {
publicMasterKeyIds.add(masterKeyId);
}
if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key)
&& allSecretMasterKeyIds.contains(masterKeyId)) {
secretMasterKeyIds.add(masterKeyId);
}
} }
} }
Bundle resultData;
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this); PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
Bundle resultData = pgpImportExport
resultData = pgpImportExport .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
.exportKeyRings(keyRingRowIds, keyType, outStream); new FileOutputStream(outputFile));
if (mIsCanceled) { if (mIsCanceled) {
boolean isDeleted = new File(outputFile).delete(); boolean isDeleted = new File(outputFile).delete();

View File

@ -322,8 +322,10 @@ public class EditKeyActivity extends ActionBarActivity {
cancelClicked(); cancelClicked();
return true; return true;
case R.id.menu_key_edit_export_file: case R.id.menu_key_edit_export_file:
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())}; long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC); long[] ids = new long[]{masterKeyId};
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC,
null);
return true; return true;
case R.id.menu_key_edit_delete: { case R.id.menu_key_edit_delete: {
// Message is received after key is deleted // Message is received after key is deleted

View File

@ -58,8 +58,7 @@ public class KeyListActivity extends DrawerActivity {
return true; return true;
case R.id.menu_key_list_export: case R.id.menu_key_list_export:
// TODO fix this for unified keylist mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB, null);
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB);
return true; return true;
case R.id.menu_key_list_create: case R.id.menu_key_list_create:
@ -71,7 +70,7 @@ public class KeyListActivity extends DrawerActivity {
return true; return true;
case R.id.menu_key_list_secret_export: case R.id.menu_key_list_secret_export:
mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC); mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, null);
return true; return true;
default: default:

View File

@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter; import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -183,13 +184,22 @@ public class KeyListFragment extends Fragment
break; break;
} }
case R.id.menu_key_list_multi_export: { case R.id.menu_key_list_multi_export: {
// todo: public/secret needs to be handled differently here
ids = mStickyList.getWrappedList().getCheckedItemIds(); ids = mStickyList.getWrappedList().getCheckedItemIds();
long[] masterKeyIds = new long[2*ids.length];
ArrayList<Long> allPubRowIds =
ProviderHelper.getPublicKeyRingsRowIds(getActivity());
for (int i = 0; i < ids.length; i++) {
if (allPubRowIds.contains(ids[i])) {
masterKeyIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), ids[i]);
} else {
masterKeyIds[i] = ProviderHelper.getSecretMasterKeyId(getActivity(), ids[i]);
}
}
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
mExportHelper mExportHelper
.showExportKeysDialog(ids, .showExportKeysDialog(masterKeyIds, Id.type.public_key,
Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB,
Constants.Path.APP_DIR_FILE_PUB); getString(R.string.also_export_secret_keys));
break; break;
} }
case R.id.menu_key_list_multi_select_all: { case R.id.menu_key_list_multi_select_all: {

View File

@ -124,8 +124,11 @@ public class ViewKeyActivity extends ActionBarActivity {
uploadToKeyserver(mDataUri); uploadToKeyserver(mDataUri);
return true; return true;
case R.id.menu_key_view_export_file: case R.id.menu_key_view_export_file:
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())}; long masterKeyId =
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB); ProviderHelper.getPublicMasterKeyId(this, Long.valueOf(mDataUri.getLastPathSegment()));
long[] ids = new long[]{masterKeyId};
mExportHelper.showExportKeysDialog(ids, Id.type.public_key,
Constants.Path.APP_DIR_FILE_PUB, null);
return true; return true;
case R.id.menu_key_view_share_default_fingerprint: case R.id.menu_key_view_share_default_fingerprint:
shareKey(mDataUri, true); shareKey(mDataUri, true);

View File

@ -219,6 +219,7 @@
<string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string> <string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string>
<string name="key_deletion_confirmation_multi">Do you really want to delete all selected keys?\nYou can\'t undo this!</string> <string name="key_deletion_confirmation_multi">Do you really want to delete all selected keys?\nYou can\'t undo this!</string>
<string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string> <string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string>
<string name="also_export_secret_keys">Also export secret keys?</string>
<plurals name="keys_added_and_updated_1"> <plurals name="keys_added_and_updated_1">
<item quantity="one">Successfully added %d key</item> <item quantity="one">Successfully added %d key</item>