externalized import, export, started working on qr code import

This commit is contained in:
Dominik 2012-09-10 11:13:32 +02:00
parent 5cd51b2ad0
commit 45d760008c
16 changed files with 815 additions and 561 deletions

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" >
<LinearLayout
android:id="@+id/import_from_qr_code_footer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<Button
android:id="@+id/import_from_qr_code_import"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/import_from_qr_code_import" />
<Button
android:id="@+id/import_from_qr_code_import_sign_and_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/import_from_qr_code_import_sign_and_upload" />
<Button
android:id="@+id/import_from_qr_code_scan_again"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/import_from_qr_code_scan_again" />
<Button
android:id="@+id/import_from_qr_code_finish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/import_from_qr_code_finish" />
</LinearLayout>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/import_from_qr_code_footer"
android:fillViewport="true" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<TextView
android:id="@+id/import_from_qr_code_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
</ScrollView>
</RelativeLayout>

View File

@ -34,7 +34,7 @@
<string name="title_keyServerPreference">Key Server Preference</string> <string name="title_keyServerPreference">Key Server Preference</string>
<string name="title_changePassPhrase">Change Passphrase</string> <string name="title_changePassPhrase">Change Passphrase</string>
<string name="title_setPassPhrase">Set Passphrase</string> <string name="title_setPassPhrase">Set Passphrase</string>
<string name="title_sendEmail">"Send Mail..."</string> <string name="title_sendEmail">"Send Mail"</string>
<string name="title_encryptToFile">Encrypt To File</string> <string name="title_encryptToFile">Encrypt To File</string>
<string name="title_decryptToFile">Decrypt To File</string> <string name="title_decryptToFile">Decrypt To File</string>
<string name="title_importKeys">Import Keys</string> <string name="title_importKeys">Import Keys</string>
@ -100,7 +100,7 @@
<string name="menu_keyServer">Key Server</string> <string name="menu_keyServer">Key Server</string>
<string name="menu_updateKey">Update</string> <string name="menu_updateKey">Update</string>
<string name="menu_exportKeyToServer">Export To Server</string> <string name="menu_exportKeyToServer">Export To Server</string>
<string name="menu_share">Share with QR Code</string> <string name="menu_share">Share public key with QR Code</string>
<string name="menu_scanQRCode">Scan QR Code</string> <string name="menu_scanQRCode">Scan QR Code</string>
<string name="menu_signKey">Sign Key</string> <string name="menu_signKey">Sign Key</string>
@ -173,10 +173,10 @@
<string name="dsa">DSA</string> <string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string> <string name="elgamal">ElGamal</string>
<string name="rsa">RSA</string> <string name="rsa">RSA</string>
<string name="filemanager_titleOpen">Open...</string> <string name="filemanager_titleOpen">Open</string>
<string name="filemanager_titleSave">Save As...</string> <string name="filemanager_titleSave">Save As</string>
<string name="filemanager_titleEncrypt">Select File To Encrypt...</string> <string name="filemanager_titleEncrypt">Select File To Encrypt</string>
<string name="filemanager_titleDecrypt">Select File To Decrypt...</string> <string name="filemanager_titleDecrypt">Select File To Decrypt</string>
<string name="filemanager_btnOpen">Open</string> <string name="filemanager_btnOpen">Open</string>
<string name="filemanager_btnSave">Save</string> <string name="filemanager_btnSave">Save</string>
<string name="warning">Warning</string> <string name="warning">Warning</string>
@ -260,41 +260,41 @@
<string name="error_savingKeys">error saving some key(s)</string> <string name="error_savingKeys">error saving some key(s)</string>
<string name="error_couldNotExtractPrivateKey">could not extract private key</string> <string name="error_couldNotExtractPrivateKey">could not extract private key</string>
<!-- progress_lowerCase: lowercase, phrases, usually ending in '...' --> <!-- progress_lowerCase: lowercase, phrases, usually ending in '' -->
<string name="progress_done">done.</string> <string name="progress_done">done.</string>
<string name="progress_initializing">initializing...</string> <string name="progress_initializing">initializing</string>
<string name="progress_saving">saving...</string> <string name="progress_saving">saving</string>
<string name="progress_importing">importing...</string> <string name="progress_importing">importing</string>
<string name="progress_exporting">exporting...</string> <string name="progress_exporting">exporting</string>
<string name="progress_generating">generating key, this can take a while...</string> <string name="progress_generating">generating key, this can take a while</string>
<string name="progress_buildingKey">building key...</string> <string name="progress_buildingKey">building key</string>
<string name="progress_preparingMasterKey">preparing master key...</string> <string name="progress_preparingMasterKey">preparing master key</string>
<string name="progress_certifyingMasterKey">certifying master key...</string> <string name="progress_certifyingMasterKey">certifying master key</string>
<string name="progress_buildingMasterKeyRing">building master key ring...</string> <string name="progress_buildingMasterKeyRing">building master key ring</string>
<string name="progress_addingSubKeys">adding sub keys...</string> <string name="progress_addingSubKeys">adding sub keys</string>
<string name="progress_savingKeyRing">saving key ring...</string> <string name="progress_savingKeyRing">saving key ring</string>
<string name="progress_importingSecretKeys">importing secret keys...</string> <string name="progress_importingSecretKeys">importing secret keys</string>
<string name="progress_importingPublicKeys">importing public keys...</string> <string name="progress_importingPublicKeys">importing public keys</string>
<string name="progress_reloadingKeys">reloading keys...</string> <string name="progress_reloadingKeys">reloading keys</string>
<string name="progress_exportingKey">exporting key...</string> <string name="progress_exportingKey">exporting key</string>
<string name="progress_exportingKeys">exporting keys...</string> <string name="progress_exportingKeys">exporting keys</string>
<string name="progress_extractingSignatureKey">extracting signature key...</string> <string name="progress_extractingSignatureKey">extracting signature key</string>
<string name="progress_extractingKey">extracting key...</string> <string name="progress_extractingKey">extracting key</string>
<string name="progress_preparingStreams">preparing streams...</string> <string name="progress_preparingStreams">preparing streams</string>
<string name="progress_encrypting">encrypting data...</string> <string name="progress_encrypting">encrypting data</string>
<string name="progress_decrypting">decrypting data...</string> <string name="progress_decrypting">decrypting data</string>
<string name="progress_preparingSignature">preparing signature...</string> <string name="progress_preparingSignature">preparing signature</string>
<string name="progress_generatingSignature">generating signature...</string> <string name="progress_generatingSignature">generating signature</string>
<string name="progress_processingSignature">processing signature...</string> <string name="progress_processingSignature">processing signature</string>
<string name="progress_verifyingSignature">verifying signature...</string> <string name="progress_verifyingSignature">verifying signature</string>
<string name="progress_signing">signing...</string> <string name="progress_signing">signing</string>
<string name="progress_readingData">reading data...</string> <string name="progress_readingData">reading data</string>
<string name="progress_findingKey">finding key...</string> <string name="progress_findingKey">finding key</string>
<string name="progress_decompressingData">decompressing data...</string> <string name="progress_decompressingData">decompressing data</string>
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity</string>
<string name="progress_deletingSecurely">deleting \'%s\' securely...</string> <string name="progress_deletingSecurely">deleting \'%s\' securely</string>
<string name="progress_querying">querying...</string> <string name="progress_querying">querying</string>
<string name="progress_queryingServer">querying %s...</string> <string name="progress_queryingServer">querying %s</string>
<!-- permission strings --> <!-- permission strings -->
<string name="permission_read_key_details_label">Read key details from APG.</string> <string name="permission_read_key_details_label">Read key details from APG.</string>
@ -316,7 +316,7 @@
<string name="slow">slow</string> <string name="slow">slow</string>
<string name="very_slow">very slow</string> <string name="very_slow">very slow</string>
<!-- APG Fork --> <!-- APG 2.0 -->
<!-- Dashboard --> <!-- Dashboard -->
@ -333,4 +333,11 @@
<string name="help_tab_about">About</string> <string name="help_tab_about">About</string>
<string name="help_about_version">Version:</string> <string name="help_about_version">Version:</string>
<!-- Import from QR Code -->
<string name="import_from_qr_code_import">Import key (only locally)</string>
<string name="import_from_qr_code_import_sign_and_upload">Import, Sign, and upload key</string>
<string name="import_from_qr_code_scan_again">Scan qr code again</string>
<string name="import_from_qr_code_finish">Finish</string>
</resources> </resources>

View File

@ -16,6 +16,9 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.security.Security;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.thialfihar.android.apg.helper.PGPMain; import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.service.PassphraseCacheService; import org.thialfihar.android.apg.service.PassphraseCacheService;
@ -23,6 +26,10 @@ import android.app.Application;
public class ApgApplication extends Application { public class ApgApplication extends Application {
static {
Security.addProvider(new BouncyCastleProvider());
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();

View File

@ -35,7 +35,7 @@ public final class Id {
public static final int edit = 0x21070003; public static final int edit = 0x21070003;
public static final int update = 0x21070004; public static final int update = 0x21070004;
public static final int exportToServer = 0x21070005; public static final int exportToServer = 0x21070005;
public static final int share = 0x21070006; public static final int share_qr_code = 0x21070006;
public static final int signKey = 0x21070007; public static final int signKey = 0x21070007;
public static final class option { public static final class option {

View File

@ -86,6 +86,7 @@ public class OtherHelper {
*/ */
public static void logDebugBundle(Bundle bundle, String bundleName) { public static void logDebugBundle(Bundle bundle, String bundleName) {
if (Constants.DEBUG) { if (Constants.DEBUG) {
if (bundle != null) {
Set<String> ks = bundle.keySet(); Set<String> ks = bundle.keySet();
Iterator<String> iterator = ks.iterator(); Iterator<String> iterator = ks.iterator();
@ -102,6 +103,9 @@ public class OtherHelper {
} }
} }
Log.d(Constants.TAG, "------------------------------"); Log.d(Constants.TAG, "------------------------------");
} else {
Log.d(Constants.TAG, "Bundle " + bundleName + ": null");
}
} }
} }
} }

View File

@ -17,6 +17,7 @@
package org.thialfihar.android.apg.helper; package org.thialfihar.android.apg.helper;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Calendar; import java.util.Calendar;
@ -24,6 +25,7 @@ import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Vector; import java.util.Vector;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
@ -34,8 +36,10 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.R; import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.util.IterableIterator; import org.thialfihar.android.apg.util.IterableIterator;
import org.thialfihar.android.apg.util.Log;
import android.content.Context; import android.content.Context;
@ -380,12 +384,42 @@ public class PGPHelper {
} }
public static String getPubkeyAsArmoredString(long keyId) {
PGPPublicKey key = PGPMain.getPublicKey(keyId);
// if it is no public key get it from your own keys...
if (key == null) {
PGPSecretKey secretKey = PGPMain.getSecretKey(keyId);
if (secretKey == null) {
Log.e(Constants.TAG, "Key could not be found!");
return null;
}
key = secretKey.getPublicKey();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
String armouredKey = null;
try {
aos.write(key.getEncoded());
aos.close();
armouredKey = bos.toString("UTF-8");
} catch (IOException e) {
Log.e(Constants.TAG, "Problems while encoding key as armored string", e);
}
Log.d(Constants.TAG, "key:" + armouredKey);
return armouredKey;
}
public static String getFingerPrint(long keyId) { public static String getFingerPrint(long keyId) {
PGPPublicKey key = PGPMain.getPublicKey(keyId); PGPPublicKey key = PGPMain.getPublicKey(keyId);
if (key == null) { if (key == null) {
PGPSecretKey secretKey = PGPMain.getSecretKey(keyId); PGPSecretKey secretKey = PGPMain.getSecretKey(keyId);
if (secretKey == null) { if (secretKey == null) {
return ""; Log.e(Constants.TAG, "Key could not be found!");
return null;
} }
key = secretKey.getPublicKey(); key = secretKey.getPublicKey();
} }

View File

@ -662,7 +662,7 @@ public class PGPMain {
} }
} }
public static Bundle importKeyRings(Activity context, int type, InputData data, public static Bundle importKeyRings(Context context, int type, InputData data,
ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException, ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException,
PGPException, IOException { PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
@ -733,7 +733,7 @@ public class PGPMain {
return returnData; return returnData;
} }
public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds, public static Bundle exportKeyRings(Context context, Vector<Integer> keyRingIds,
OutputStream outStream, ProgressDialogUpdater progress) throws GeneralException, OutputStream outStream, ProgressDialogUpdater progress) throws GeneralException,
FileNotFoundException, PGPException, IOException { FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();

View File

@ -26,6 +26,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Vector;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
@ -66,7 +67,7 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
/* keys for data bundle */ /* keys for data bundle */
// encrypt and decrypt // encrypt, decrypt, import export
public static final String TARGET = "target"; public static final String TARGET = "target";
// possible targets: // possible targets:
public static final int TARGET_BYTES = 1; public static final int TARGET_BYTES = 1;
@ -91,7 +92,7 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
public static final String RETURN_BYTES = "returnBinary"; public static final String RETURN_BYTES = "returnBinary";
public static final String CIPHERTEXT_BYTES = "ciphertextBytes"; public static final String CIPHERTEXT_BYTES = "ciphertextBytes";
public static final String ASSUME_SYMMETRIC = "assumeSymmetric"; public static final String ASSUME_SYMMETRIC = "assumeSymmetric";
public static final String LOOKUP_UNKNOWN_KEY = "lookup_unknown_key"; public static final String LOOKUP_UNKNOWN_KEY = "lookupUnknownKey";
// edit keys // edit keys
public static final String NEW_PASSPHRASE = "newPassphrase"; public static final String NEW_PASSPHRASE = "newPassphrase";
@ -103,13 +104,26 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
// generate key // generate key
public static final String ALGORITHM = "algorithm"; public static final String ALGORITHM = "algorithm";
public static final String KEY_SIZE = "key_size"; public static final String KEY_SIZE = "keySize";
public static final String SYMMETRIC_PASSPHRASE = "passphrase"; public static final String SYMMETRIC_PASSPHRASE = "passphrase";
public static final String MASTER_KEY = "masterKey"; public static final String MASTER_KEY = "masterKey";
// delete file securely // delete file securely
public static final String DELETE_FILE = "deleteFile"; public static final String DELETE_FILE = "deleteFile";
// import key
public static final String IMPORT_INPUT_STREAM = "importInputStream";
public static final String IMPORT_FILENAME = "importFilename";
public static final String IMPORT_BYTES = "importBytes";
public static final String IMPORT_KEY_TYPE = "importKeyType";
// export key
public static final String EXPORT_OUTPUT_STREAM = "exportOutputStream";
public static final String EXPORT_FILENAME = "exportFilename";
public static final String EXPORT_KEY_TYPE = "exportKeyType";
public static final String EXPORT_ALL = "exportAll";
public static final String EXPORT_KEY_RING_ID = "exportKeyRingId";
/* possible EXTRA_ACTIONs */ /* possible EXTRA_ACTIONs */
public static final int ACTION_ENCRYPT_SIGN = 10; public static final int ACTION_ENCRYPT_SIGN = 10;
@ -121,6 +135,9 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
public static final int ACTION_DELETE_FILE_SECURELY = 40; public static final int ACTION_DELETE_FILE_SECURELY = 40;
public static final int ACTION_IMPORT_KEY = 50;
public static final int ACTION_EXPORT_KEY = 51;
/* possible data keys as result send over messenger */ /* possible data keys as result send over messenger */
// keys // keys
public static final String RESULT_NEW_KEY = "newKey"; public static final String RESULT_NEW_KEY = "newKey";
@ -580,6 +597,104 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
break; break;
case ACTION_IMPORT_KEY:
try {
/* Input */
int target = data.getInt(TARGET);
int keyType = Id.type.public_key;
if (data.containsKey(IMPORT_KEY_TYPE)) {
keyType = data.getInt(IMPORT_KEY_TYPE);
}
/* Operation */
InputStream inStream = null;
long inLength = -1;
InputData inputData = null;
switch (target) {
case TARGET_BYTES: /* import key from bytes directly */
byte[] bytes = data.getByteArray(IMPORT_BYTES);
inStream = new ByteArrayInputStream(bytes);
inLength = bytes.length;
inputData = new InputData(inStream, inLength);
break;
case TARGET_FILE: /* import key from file */
String inputFile = data.getString(IMPORT_FILENAME);
inStream = new FileInputStream(inputFile);
File file = new File(inputFile);
inLength = file.length();
inputData = new InputData(inStream, inLength);
break;
case TARGET_STREAM:
// TODO: not implemented
break;
}
Bundle resultData = new Bundle();
resultData = PGPMain.importKeyRings(this, keyType, inputData, this);
sendMessageToHandler(ApgHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
sendErrorToHandler(e);
}
break;
case ACTION_EXPORT_KEY:
try {
/* Input */
int keyType = Id.type.public_key;
if (data.containsKey(EXPORT_KEY_TYPE)) {
keyType = data.getInt(EXPORT_KEY_TYPE);
}
String outputFile = data.getString(EXPORT_FILENAME);
boolean exportAll = data.getBoolean(EXPORT_ALL);
int keyRingId = -1;
if (!exportAll) {
keyRingId = data.getInt(EXPORT_KEY_RING_ID);
}
/* Operation */
// check if storage is ready
if (!FileHelper.isStorageMounted(outputFile)) {
sendErrorToHandler(new GeneralException(
getString(R.string.error_externalStorageNotReady)));
return;
}
// OutputStream
FileOutputStream outStream = new FileOutputStream(outputFile);
Vector<Integer> keyRingIds = new Vector<Integer>();
if (exportAll) {
keyRingIds = PGPMain
.getKeyRingIds(keyType == Id.type.public_key ? Id.database.type_public
: Id.database.type_secret);
} else {
keyRingIds.add(keyRingId);
}
Bundle resultData = new Bundle();
resultData = PGPMain.exportKeyRings(this, keyRingIds, outStream, this);
sendMessageToHandler(ApgHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
sendErrorToHandler(e);
}
break;
default: default:
break; break;
} }

View File

@ -693,7 +693,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
private void decryptStart() { private void decryptStart() {
Log.d(Constants.TAG, "decryptStart"); Log.d(Constants.TAG, "decryptStart");
// Send all information needed to service to edit key in other thread // Send all information needed to service to decrypt in other thread
Intent intent = new Intent(this, ApgService.class); Intent intent = new Intent(this, ApgService.class);
// fill values for this action // fill values for this action

View File

@ -33,6 +33,8 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
import org.thialfihar.android.apg.util.Log; import org.thialfihar.android.apg.util.Log;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentIntegrator;
@ -61,87 +63,99 @@ public class ImportFromQRCodeActivity extends BaseActivity {
new IntentIntegrator(this).initiateScan(); new IntentIntegrator(this).initiateScan();
} }
// private void importAndSignOld(final long keyId, final String expectedFingerprint) {
// if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
//
// Thread t = new Thread() {
// @Override
// public void run() {
// try {
// // TODO: display some sort of spinner here while the user waits
//
// // TODO: there should be only 1
// HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]);
// String encodedKey = server.get(keyId);
//
// PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream(
// encodedKey.getBytes()));
// if (keyring != null && keyring instanceof PGPPublicKeyRing) {
// PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
//
// // make sure the fingerprints match before we cache this thing
// String actualFingerprint = PGPHelper.convertToHex(publicKeyRing
// .getPublicKey().getFingerprint());
// if (expectedFingerprint.equals(actualFingerprint)) {
// // store the signed key in our local cache
// int retval = PGPMain.storeKeyRingInCache(publicKeyRing);
// if (retval != Id.return_value.ok
// && retval != Id.return_value.updated) {
// status.putString(EXTRA_ERROR,
// "Failed to store signed key in local cache");
// } else {
// Intent intent = new Intent(ImportFromQRCodeActivity.this,
// SignKeyActivity.class);
// intent.putExtra(EXTRA_KEY_ID, keyId);
// startActivityForResult(intent, Id.request.sign_key);
// }
// } else {
// status.putString(
// EXTRA_ERROR,
// "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
// }
// }
// } catch (QueryException e) {
// Log.e(TAG, "Failed to query KeyServer", e);
// status.putString(EXTRA_ERROR, "Failed to query KeyServer");
// status.putInt(Constants.extras.STATUS, Id.message.done);
// } catch (IOException e) {
// Log.e(TAG, "Failed to query KeyServer", e);
// status.putString(EXTRA_ERROR, "Failed to query KeyServer");
// status.putInt(Constants.extras.STATUS, Id.message.done);
// }
// }
// };
//
// t.setName("KeyExchange Download Thread");
// t.setDaemon(true);
// t.start();
// }
// }
private void importAndSign(final long keyId, final String expectedFingerprint) { private void importAndSign(final long keyId, final String expectedFingerprint) {
if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
Thread t = new Thread() { // setContentView(R.layout.import_from_qr_code);
@Override
public void run() {
try {
// TODO: display some sort of spinner here while the user waits
// TODO: there should be only 1
HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]);
String encodedKey = server.get(keyId);
PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream(
encodedKey.getBytes()));
if (keyring != null && keyring instanceof PGPPublicKeyRing) {
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
// make sure the fingerprints match before we cache this thing
String actualFingerprint = PGPHelper.convertToHex(publicKeyRing
.getPublicKey().getFingerprint());
if (expectedFingerprint.equals(actualFingerprint)) {
// store the signed key in our local cache
int retval = PGPMain.storeKeyRingInCache(publicKeyRing);
if (retval != Id.return_value.ok
&& retval != Id.return_value.updated) {
status.putString(EXTRA_ERROR,
"Failed to store signed key in local cache");
} else {
Intent intent = new Intent(ImportFromQRCodeActivity.this,
SignKeyActivity.class);
intent.putExtra(EXTRA_KEY_ID, keyId);
startActivityForResult(intent, Id.request.sign_key);
}
} else {
status.putString(
EXTRA_ERROR,
"Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
}
}
} catch (QueryException e) {
Log.e(TAG, "Failed to query KeyServer", e);
status.putString(EXTRA_ERROR, "Failed to query KeyServer");
status.putInt(Constants.extras.STATUS, Id.message.done);
} catch (IOException e) {
Log.e(TAG, "Failed to query KeyServer", e);
status.putString(EXTRA_ERROR, "Failed to query KeyServer");
status.putInt(Constants.extras.STATUS, Id.message.done);
}
}
};
t.setName("KeyExchange Download Thread");
t.setDaemon(true);
t.start();
}
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
case IntentIntegrator.REQUEST_CODE: { case IntentIntegrator.REQUEST_CODE: {
boolean debug = true; // TODO: remove this!!!
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode,
data); data);
if (debug || (scanResult != null && scanResult.getFormatName() != null)) { if (scanResult != null && scanResult.getFormatName() != null) {
String[] bits = debug ? new String[] { "5993515643896327656",
"0816 F68A 6816 68FB 01BF 2CA5 532D 3EB9 1E2F EDE8" } : scanResult // show layout
.getContents().split(","); setContentView(R.layout.import_from_qr_code);
if (bits.length != 2) { TextView contentView = (TextView) findViewById(R.id.import_from_qr_code_content);
return; // dont know how to handle this. Not a valid code
String content = scanResult.getContents();
contentView.setText(content);
// String[] bits = scanResult.getContents().split(",");
// if (bits.length != 2) {
// return; // dont know how to handle this. Not a valid code
// }
//
// long keyId = Long.parseLong(bits[0]);
// String expectedFingerprint = bits[1];
// importAndSign(keyId, expectedFingerprint);
} }
long keyId = Long.parseLong(bits[0]);
String expectedFingerprint = bits[1];
importAndSign(keyId, expectedFingerprint);
break; break;
} }
}
case Id.request.sign_key: { case Id.request.sign_key: {
// signals the end of processing. Signature was either applied, or it wasnt // signals the end of processing. Signature was either applied, or it wasnt

View File

@ -16,9 +16,6 @@
package org.thialfihar.android.apg.ui; package org.thialfihar.android.apg.ui;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.Constants; import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id; import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.helper.PGPHelper; import org.thialfihar.android.apg.helper.PGPHelper;
@ -26,14 +23,19 @@ import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.provider.KeyRings; import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys; import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds; import org.thialfihar.android.apg.provider.UserIds;
import org.thialfihar.android.apg.service.ApgHandler;
import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment;
import org.thialfihar.android.apg.ui.dialog.DeleteKeyDialogFragment;
import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; import org.thialfihar.android.apg.ui.dialog.FileDialogFragment;
import org.thialfihar.android.apg.util.InputData; import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment;
import org.thialfihar.android.apg.R; import org.thialfihar.android.apg.R;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.ProgressDialog;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -59,25 +61,14 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector; import java.util.Vector;
public class KeyListActivity extends BaseActivity { public class KeyListActivity extends SherlockFragmentActivity {
public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT"; public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT";
public static final String EXTRA_TEXT = "text"; public static final String EXTRA_TEXT = "text";
// TODO: remove when using new intentservice:
public static final String EXTRA_ERROR = "error";
protected ExpandableListView mList; protected ExpandableListView mList;
protected KeyListAdapter mListAdapter; protected KeyListAdapter mListAdapter;
protected View mFilterLayout; protected View mFilterLayout;
@ -251,7 +242,7 @@ public class KeyListActivity extends BaseActivity {
case Id.menu.delete: { case Id.menu.delete: {
mSelectedItem = groupPosition; mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key); showDeleteKeyDialog();
return true; return true;
} }
@ -261,177 +252,87 @@ public class KeyListActivity extends BaseActivity {
} }
} }
@Override private void showDeleteKeyDialog() {
protected Dialog onCreateDialog(int id) {
switch (id) {
case Id.dialog.delete_key: {
final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem); final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
mSelectedItem = -1; mSelectedItem = -1;
// TODO: better way to do this?
String userId = "<unknown>";
Object keyRing = PGPMain.getKeyRing(keyRingId);
if (keyRing != null) {
if (keyRing instanceof PGPPublicKeyRing) {
userId = PGPHelper.getMainUserIdSafe(this,
PGPHelper.getMasterKey((PGPPublicKeyRing) keyRing));
} else {
userId = PGPHelper.getMainUserIdSafe(this,
PGPHelper.getMasterKey((PGPSecretKeyRing) keyRing));
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this); // Message is received after key is deleted
builder.setTitle(R.string.warning); Handler returnHandler = new Handler() {
builder.setMessage(getString( @Override
mKeyType == Id.type.public_key ? R.string.keyDeletionConfirmation public void handleMessage(Message message) {
: R.string.secretKeyDeletionConfirmation, userId)); if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
builder.setIcon(android.R.drawable.ic_dialog_alert); refreshList();
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(keyRingId);
removeDialog(Id.dialog.delete_key);
} }
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
} }
};
default: { // Create a new Messenger for the communication back
return super.onCreateDialog(id); Messenger messenger = new Messenger(returnHandler);
}
} DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
keyRingId, mKeyType);
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
} }
public void importKeys() { public void importKeys() {
showDialog(Id.dialog.importing); Log.d(Constants.TAG, "importKeys started");
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() { // Send all information needed to service to import key in other thread
showDialog(Id.dialog.exporting); Intent intent = new Intent(this, ApgService.class);
mTask = Id.task.export_keys;
startThread();
}
@Override intent.putExtra(ApgService.EXTRA_ACTION, ApgService.ACTION_IMPORT_KEY);
public void run() {
String error = null; // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
Message msg = new Message();
try { data.putInt(ApgService.IMPORT_KEY_TYPE, mKeyType);
InputStream importInputStream = null;
OutputStream exportOutputStream = null;
long size = 0;
if (mTask == Id.task.import_keys) {
if (mImportData != null) { if (mImportData != null) {
byte[] bytes = mImportData.getBytes(); data.putInt(ApgService.TARGET, ApgService.TARGET_BYTES);
size = bytes.length; data.putByteArray(ApgService.IMPORT_BYTES, mImportData.getBytes());
importInputStream = new ByteArrayInputStream(bytes);
} else { } else {
File file = new File(mImportFilename); data.putInt(ApgService.TARGET, ApgService.TARGET_FILE);
size = file.length(); data.putString(ApgService.IMPORT_FILENAME, mImportFilename);
importInputStream = new FileInputStream(file);
}
} else {
exportOutputStream = new FileOutputStream(mExportFilename);
} }
if (mTask == Id.task.import_keys) { intent.putExtra(ApgService.EXTRA_DATA, data);
data = PGPMain.importKeyRings(this, mKeyType, new InputData(importInputStream,
size), this);
} else {
Vector<Integer> keyRingIds = new Vector<Integer>();
if (mSelectedItem == -1) {
keyRingIds = PGPMain
.getKeyRingIds(mKeyType == Id.type.public_key ? Id.database.type_public
: Id.database.type_secret);
} else {
int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
keyRingIds.add(keyRingId);
mSelectedItem = -1;
}
data = PGPMain.exportKeyRings(this, keyRingIds, exportOutputStream, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = "" + e;
} catch (PGPException e) {
error = "" + e;
} catch (PGPMain.GeneralException e) {
error = "" + e;
}
mImportData = null; // create progress dialog
ProgressDialogFragment importingDialog = ProgressDialogFragment.newInstance(
R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL);
if (mTask == Id.task.import_keys) { // Message is received after importing is done in ApgService
data.putInt(Constants.extras.STATUS, Id.message.import_done); ApgHandler saveHandler = new ApgHandler(this, importingDialog) {
} else { public void handleMessage(Message message) {
data.putInt(Constants.extras.STATUS, Id.message.export_done); // handle messages by standard ApgHandler first
} super.handleMessage(message);
if (error != null) { if (message.arg1 == ApgHandler.MESSAGE_OKAY) {
data.putString(EXTRA_ERROR, error); // get returned data bundle
} Bundle returnData = message.getData();
msg.setData(data); int added = returnData.getInt("added");
sendMessage(msg); int updated = returnData.getInt("updated");
} int bad = returnData.getInt("bad");
String toastMessage;
protected void deleteKey(int keyRingId) {
PGPMain.deleteKey(keyRingId);
refreshList();
}
protected void refreshList() {
mListAdapter.rebuild(true);
mListAdapter.notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt(Constants.extras.STATUS);
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString(EXTRA_ERROR);
if (error != null) {
Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
int bad = data.getInt("bad");
String message;
if (added > 0 && updated > 0) { if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated); toastMessage = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) { } else if (added > 0) {
message = getString(R.string.keysAdded, added); toastMessage = getString(R.string.keysAdded, added);
} else if (updated > 0) { } else if (updated > 0) {
message = getString(R.string.keysUpdated, updated); toastMessage = getString(R.string.keysUpdated, updated);
} else { } else {
message = getString(R.string.noKeysAddedOrUpdated); toastMessage = getString(R.string.noKeysAddedOrUpdated);
} }
Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
if (bad > 0) { if (bad > 0) {
AlertDialog.Builder alert = new AlertDialog.Builder(this); AlertDialog.Builder alert = new AlertDialog.Builder(KeyListActivity.this);
alert.setIcon(android.R.drawable.ic_dialog_alert); alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.warning); alert.setTitle(R.string.warning);
alert.setMessage(this.getString(R.string.badKeysEncountered, bad)); alert.setMessage(KeyListActivity.this.getString(
R.string.badKeysEncountered, bad));
alert.setPositiveButton(android.R.string.ok, alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@ -443,41 +344,94 @@ public class KeyListActivity extends BaseActivity {
alert.create().show(); alert.create().show();
} else if (mDeleteAfterImport) { } else if (mDeleteAfterImport) {
// everything went well, so now delete, if that was turned on // everything went well, so now delete, if that was turned on
setDeleteFile(mImportFilename); DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
showDialog(Id.dialog.delete_file); .newInstance(mImportFilename);
} deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
} }
refreshList(); refreshList();
break;
}
};
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(ApgService.EXTRA_MESSENGER, messenger);
// show progress dialog
importingDialog.show(getSupportFragmentManager(), "importingDialog");
// start service with intent
startService(intent);
} }
case Id.message.export_done: { public void exportKeys() {
removeDialog(Id.dialog.exporting); Log.d(Constants.TAG, "exportKeys started");
String error = data.getString(EXTRA_ERROR); // Send all information needed to service to export key in other thread
if (error != null) { Intent intent = new Intent(this, ApgService.class);
Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
Toast.LENGTH_SHORT).show(); intent.putExtra(ApgService.EXTRA_ACTION, ApgService.ACTION_EXPORT_KEY);
// fill values for this action
Bundle data = new Bundle();
data.putString(ApgService.EXPORT_FILENAME, mExportFilename);
data.putInt(ApgService.EXPORT_KEY_TYPE, mKeyType);
if (mSelectedItem == -1) {
data.putBoolean(ApgService.EXPORT_ALL, true);
} else { } else {
int exported = data.getInt("exported"); int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
String message; data.putInt(ApgService.EXPORT_KEY_RING_ID, keyRingId);
mSelectedItem = -1;
}
intent.putExtra(ApgService.EXTRA_DATA, data);
// create progress dialog
ProgressDialogFragment exportingDialog = ProgressDialogFragment.newInstance(
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL);
// Message is received after exporting is done in ApgService
ApgHandler exportHandler = new ApgHandler(this, exportingDialog) {
public void handleMessage(Message message) {
// handle messages by standard ApgHandler first
super.handleMessage(message);
if (message.arg1 == ApgHandler.MESSAGE_OKAY) {
// get returned data bundle
Bundle returnData = message.getData();
int exported = returnData.getInt("exported");
String toastMessage;
if (exported == 1) { if (exported == 1) {
message = getString(R.string.keyExported); toastMessage = getString(R.string.keyExported);
} else if (exported > 0) { } else if (exported > 0) {
message = getString(R.string.keysExported, exported); toastMessage = getString(R.string.keysExported, exported);
} else { } else {
message = getString(R.string.noKeysExported); toastMessage = getString(R.string.noKeysExported);
} }
Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
} }
break; };
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(exportHandler);
intent.putExtra(ApgService.EXTRA_MESSENGER, messenger);
// show progress dialog
exportingDialog.show(getSupportFragmentManager(), "exportingDialog");
// start service with intent
startService(intent);
} }
default: { protected void refreshList() {
break; mListAdapter.rebuild(true);
} mListAdapter.notifyDataSetChanged();
}
}
} }
protected class KeyListAdapter extends BaseExpandableListAdapter { protected class KeyListAdapter extends BaseExpandableListAdapter {

View File

@ -1,212 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.ui;
import java.util.Vector;
import java.util.regex.Matcher;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.helper.Preferences;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
public class MailListActivity extends ListActivity {
LayoutInflater mInflater = null;
public static final String EXTRA_ACCOUNT = "account";
private static class Conversation {
public long id;
public String subject;
public Vector<Message> messages;
public Conversation(long id, String subject) {
this.id = id;
this.subject = subject;
}
}
private static class Message {
public Conversation parent;
public long id;
public String subject;
public String fromAddress;
public String data;
public String replyTo;
public boolean signedOnly;
public Message(Conversation parent, long id, String subject, String fromAddress,
String replyTo, String data, boolean signedOnly) {
this.parent = parent;
this.id = id;
this.subject = subject;
this.fromAddress = fromAddress;
this.replyTo = replyTo;
this.data = data;
if (this.replyTo == null || this.replyTo.equals("")) {
this.replyTo = this.fromAddress;
}
this.signedOnly = signedOnly;
}
}
private Vector<Conversation> mConversations;
private Vector<Message> mMessages;
@Override
protected void onCreate(Bundle savedInstanceState) {
Preferences prefs = Preferences.getPreferences(this);
super.onCreate(savedInstanceState);
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mConversations = new Vector<Conversation>();
mMessages = new Vector<Message>();
String account = getIntent().getExtras().getString(EXTRA_ACCOUNT);
// TODO: what if account is null?
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
Cursor cursor = managedQuery(uri, new String[] { "conversation_id", "subject" }, null,
null, null);
for (int i = 0; i < cursor.getCount(); ++i) {
cursor.moveToPosition(i);
int idIndex = cursor.getColumnIndex("conversation_id");
int subjectIndex = cursor.getColumnIndex("subject");
long conversationId = cursor.getLong(idIndex);
Conversation conversation = new Conversation(conversationId,
cursor.getString(subjectIndex));
Uri messageUri = Uri.withAppendedPath(uri, "" + conversationId + "/messages");
Cursor messageCursor = managedQuery(messageUri, new String[] { "messageId", "subject",
"fromAddress", "replyToAddresses", "body" }, null, null, null);
Vector<Message> messages = new Vector<Message>();
for (int j = 0; j < messageCursor.getCount(); ++j) {
messageCursor.moveToPosition(j);
idIndex = messageCursor.getColumnIndex("messageId");
subjectIndex = messageCursor.getColumnIndex("subject");
int fromAddressIndex = messageCursor.getColumnIndex("fromAddress");
int replyToIndex = messageCursor.getColumnIndex("replyToAddresses");
int bodyIndex = messageCursor.getColumnIndex("body");
String data = messageCursor.getString(bodyIndex);
data = Html.fromHtml(data).toString();
boolean signedOnly = false;
Matcher matcher = PGPMain.PGP_MESSAGE.matcher(data);
if (matcher.matches()) {
data = matcher.group(1);
} else {
matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(data);
if (matcher.matches()) {
data = matcher.group(1);
signedOnly = true;
} else {
data = null;
}
}
Message message = new Message(conversation, messageCursor.getLong(idIndex),
messageCursor.getString(subjectIndex),
messageCursor.getString(fromAddressIndex),
messageCursor.getString(replyToIndex), data, signedOnly);
messages.add(message);
mMessages.add(message);
}
conversation.messages = messages;
mConversations.add(conversation);
}
setListAdapter(new MailboxAdapter());
getListView().setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
intent.setAction(DecryptActivity.ACTION_DECRYPT);
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
intent.putExtra(DecryptActivity.EXTRA_TEXT, message.data);
intent.putExtra(DecryptActivity.EXTRA_SUBJECT, message.subject);
intent.putExtra(DecryptActivity.EXTRA_REPLY_TO, message.replyTo);
startActivity(intent);
}
});
}
private class MailboxAdapter extends BaseAdapter implements ListAdapter {
@Override
public boolean isEnabled(int position) {
Message message = (Message) getItem(position);
return message.data != null;
}
@Override
public boolean hasStableIds() {
return true;
}
public int getCount() {
return mMessages.size();
}
public Object getItem(int position) {
return mMessages.get(position);
}
public long getItemId(int position) {
return mMessages.get(position).id;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = mInflater.inflate(R.layout.mailbox_message_item, null);
Message message = (Message) getItem(position);
TextView subject = (TextView) view.findViewById(R.id.subject);
TextView email = (TextView) view.findViewById(R.id.emailAddress);
ImageView status = (ImageView) view.findViewById(R.id.ic_status);
subject.setText(message.subject);
email.setText(message.fromAddress);
if (message.data != null) {
if (message.signedOnly) {
status.setImageResource(R.drawable.signed);
} else {
status.setImageResource(R.drawable.encrypted);
}
status.setVisibility(View.VISIBLE);
} else {
status.setVisibility(View.INVISIBLE);
}
return view;
}
}
}

View File

@ -17,9 +17,6 @@
package org.thialfihar.android.apg.ui; package org.thialfihar.android.apg.ui;
import java.security.Security;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.thialfihar.android.apg.Id; import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R; import org.thialfihar.android.apg.R;
@ -33,9 +30,6 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
public class MainActivity extends SherlockActivity { public class MainActivity extends SherlockActivity {
static {
Security.addProvider(new BouncyCastleProvider());
}
public void manageKeysOnClick(View view) { public void manageKeysOnClick(View view) {
startActivity(new Intent(this, PublicKeyListActivity.class)); startActivity(new Intent(this, PublicKeyListActivity.class));

View File

@ -19,16 +19,19 @@ package org.thialfihar.android.apg.ui;
import org.thialfihar.android.apg.R; import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.Constants; import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id; import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.deprecated.AskForPassphrase;
import org.thialfihar.android.apg.helper.PGPHelper; import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain; import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment;
import org.thialfihar.android.apg.util.Log;
import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.View; import android.view.View;
@ -39,6 +42,7 @@ import android.widget.ExpandableListView.OnChildClickListener;
import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentIntegrator;
public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener { public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
mExportFilename = Constants.path.APP_DIR + "/secexport.asc"; mExportFilename = Constants.path.APP_DIR + "/secexport.asc";
@ -86,7 +90,7 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
menu.add(0, Id.menu.export, 1, R.string.menu_exportKey); menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey); menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
menu.add(0, Id.menu.share, 2, R.string.menu_share); menu.add(0, Id.menu.share_qr_code, 2, R.string.menu_share);
} }
} }
@ -107,12 +111,13 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
return true; return true;
} }
case Id.menu.share: { case Id.menu.share_qr_code: {
mSelectedItem = groupPosition; mSelectedItem = groupPosition;
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()) long keyId = ((KeyListAdapter) mList.getExpandableListAdapter())
.getGroupId(mSelectedItem); .getGroupId(mSelectedItem);
String msg = keyId + "," + PGPHelper.getFingerPrint(keyId); // String msg = keyId + "," + PGPHelper.getFingerPrint(keyId);
String msg = PGPHelper.getPubkeyAsArmoredString(keyId);
new IntentIntegrator(this).shareText(msg); new IntentIntegrator(this).shareText(msg);
} }
@ -130,38 +135,44 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
return true; return true;
} }
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case Id.dialog.pass_phrase: {
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter())
.getGroupId(mSelectedItem);
return AskForPassphrase.createDialog(this, keyId, this);
}
default: {
return super.onCreateDialog(id);
}
}
}
public void checkPassPhraseAndEdit() { public void checkPassPhraseAndEdit() {
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
String passPhrase = PGPMain.getCachedPassPhrase(keyId); String passPhrase = PGPMain.getCachedPassPhrase(keyId);
if (passPhrase == null) { if (passPhrase == null) {
showDialog(Id.dialog.pass_phrase); showPassphraseDialog(keyId);
} else { } else {
PGPMain.setEditPassPhrase(passPhrase); PGPMain.setEditPassPhrase(passPhrase);
editKey(); editKey();
} }
} }
private void showPassphraseDialog(final long secretKeyId) {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override @Override
public void passPhraseCallback(long keyId, String passPhrase) { public void handleMessage(Message message) {
super.passPhraseCallback(keyId, passPhrase); if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
String passPhrase = PGPMain.getCachedPassPhrase(secretKeyId);
PGPMain.setEditPassPhrase(passPhrase); PGPMain.setEditPassPhrase(passPhrase);
editKey(); editKey();
} }
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
messenger, secretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
} catch (PGPMain.GeneralException e) {
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
// send message to handler to start encryption directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
}
}
private void createKey() { private void createKey() {
PGPMain.setEditPassPhrase(""); PGPMain.setEditPassPhrase("");

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.ui.dialog;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.util.Log;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
public class DeleteKeyDialogFragment extends DialogFragment {
private Messenger mMessenger;
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_DELETE_KEY_RING_ID = "delete_file";
private static final String ARG_KEY_TYPE = "key_type";
public static final int MESSAGE_OKAY = 1;
/**
* Creates new instance of this delete file dialog fragment
*/
public static DeleteKeyDialogFragment newInstance(Messenger messenger, int deleteKeyRingId,
int keyType) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putInt(ARG_DELETE_KEY_RING_ID, deleteKeyRingId);
args.putInt(ARG_KEY_TYPE, keyType);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
final int deleteKeyRingId = getArguments().getInt(ARG_DELETE_KEY_RING_ID);
final int keyType = getArguments().getInt(ARG_KEY_TYPE);
// TODO: better way to do this?
String userId = "<unknown>";
Object keyRing = PGPMain.getKeyRing(deleteKeyRingId);
if (keyRing != null) {
if (keyRing instanceof PGPPublicKeyRing) {
userId = PGPHelper.getMainUserIdSafe(activity,
PGPHelper.getMasterKey((PGPPublicKeyRing) keyRing));
} else {
userId = PGPHelper.getMainUserIdSafe(activity,
PGPHelper.getMasterKey((PGPSecretKeyRing) keyRing));
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.warning);
builder.setMessage(getString(
keyType == Id.type.public_key ? R.string.keyDeletionConfirmation
: R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// deleteKey(deleteKeyRingId);
PGPMain.deleteKey(deleteKeyRingId);
dismiss();
sendMessageToHandler(MESSAGE_OKAY);
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
return builder.create();
}
/**
* Send message back to handler which is initialized in a activity
*
* @param what
* Message integer you want to send
*/
private void sendMessageToHandler(Integer what) {
Message msg = Message.obtain();
msg.what = what;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.ui.dialog;
import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import org.thialfihar.android.apg.ui.KeyServerQueryActivity;
import org.thialfihar.android.apg.util.Log;
public class LookupUnknownKeyDialogFragment extends DialogFragment {
private Messenger mMessenger;
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_CANCEL = 2;
/**
* Creates new instance of this dialog fragment
*
* @param messenger
* @param unknownKeyId
* @return
*/
public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) {
LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment();
Bundle args = new Bundle();
args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId);
args.putParcelable(ARG_MESSENGER, messenger);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.title_unknownSignatureKey);
alert.setMessage(getString(R.string.lookupUnknownKey,
PGPHelper.getSmallFingerPrint(unknownKeyId)));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dismiss();
sendMessageToHandler(MESSAGE_OKAY);
Intent intent = new Intent(activity, KeyServerQueryActivity.class);
intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID);
intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, unknownKeyId);
startActivityForResult(intent, Id.request.look_up_key_id);
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dismiss();
sendMessageToHandler(MESSAGE_CANCEL);
}
});
alert.setCancelable(true);
alert.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
sendMessageToHandler(MESSAGE_CANCEL);
}
});
return alert.create();
}
/**
* Send message back to handler which is initialized in a activity
*
* @param what
* Message integer you want to send
*/
private void sendMessageToHandler(Integer what) {
Message msg = Message.obtain();
msg.what = what;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}