aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-04-05 19:30:52 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-04-05 19:30:52 +0200
commitaa6f5118f5b88ed40e1318b59d47465bae6067df (patch)
treec00db3802cd6258073b16d197b1bd2e8d2d7e975 /OpenPGP-Keychain/src/main/java
parent5e4239a7b98a050b4312eee075f2fdac7f2b8af2 (diff)
parentdb25433890cfc5bbf0200eb488076df23cb44866 (diff)
downloadopen-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.tar.gz
open-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.tar.bz2
open-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.zip
Merge remote-tracking branch 'origin/master' into certs
A lot of things are completely broken, but it compiles and doesn't crash right away. Good enough for me. Conflicts: OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
Diffstat (limited to 'OpenPGP-Keychain/src/main/java')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java25
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java15
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java8
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java40
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java85
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java290
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java38
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java85
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java239
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java768
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java45
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java35
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java254
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java288
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java1163
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java796
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java)31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java)142
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java)91
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java)2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java)45
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java201
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java198
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java135
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java108
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java)4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java)83
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java)110
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java537
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java107
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java237
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java75
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java96
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java684
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java261
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java180
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java189
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java149
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java485
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java984
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java30
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java269
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java380
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java259
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java98
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java179
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java9
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java18
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java76
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java42
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java90
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java60
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java174
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java35
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java95
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java29
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java272
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java158
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java28
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java70
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java12
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java64
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java40
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java174
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java59
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java203
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java201
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java175
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java137
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java184
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java22
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java6
106 files changed, 7609 insertions, 5935 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 011cd9663..5b9f53b09 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -19,6 +19,11 @@ package org.sufficientlysecure.keychain;
import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
+import org.sufficientlysecure.keychain.ui.DecryptActivity;
+import org.sufficientlysecure.keychain.ui.EncryptActivity;
+import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
+import org.sufficientlysecure.keychain.ui.KeyListActivity;
public final class Constants {
@@ -42,7 +47,7 @@ public final class Constants {
public static final class Path {
public static final String APP_DIR = Environment.getExternalStorageDirectory()
- + "/OpenPGP-Keychain";
+ + "/OpenKeychain";
public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
}
@@ -50,10 +55,10 @@ public final class Constants {
public static final class Pref {
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
- public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
+ public static final String DEFAULT_ASCII_ARMOR = "defaultAsciiArmor";
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
- public static final String PASS_PHRASE_CACHE_TTL = "passphraseCacheTtl";
+ public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";
public static final String LANGUAGE = "language";
public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
public static final String KEY_SERVERS = "keyServers";
@@ -63,4 +68,18 @@ public final class Constants {
public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
}
+ public static final class DrawerItems {
+ public static final Class KEY_LIST = KeyListActivity.class;
+ public static final Class ENCRYPT = EncryptActivity.class;
+ public static final Class DECRYPT = DecryptActivity.class;
+ public static final Class IMPORT_KEYS = ImportKeysActivity.class;
+ public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
+ public static final Class[] ARRAY = new Class[]{
+ KEY_LIST,
+ ENCRYPT,
+ DECRYPT,
+ IMPORT_KEYS,
+ REGISTERED_APPS_LIST
+ };
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
index 1d79edd43..784ec340e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
@@ -119,6 +119,7 @@ public final class Id {
public static final int secret_key = 0x21070002;
public static final int user_id = 0x21070003;
public static final int key = 0x21070004;
+ public static final int public_secret_key = 0x21070005;
}
public static final class choice {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 98b827542..9b80e76f3 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -17,16 +17,17 @@
package org.sufficientlysecure.keychain;
-import java.io.File;
-import java.security.Provider;
-import java.security.Security;
+import android.app.Application;
+import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
-import android.app.Application;
-import android.os.Environment;
+import java.io.File;
+import java.security.Provider;
+import java.security.Security;
public class KeychainApplication extends Application {
@@ -40,14 +41,14 @@ public class KeychainApplication extends Application {
/*
* Sets Bouncy (Spongy) Castle as preferred security provider
- *
+ *
* insertProviderAt() position starts from 1
*/
Security.insertProviderAt(new BouncyCastleProvider(), 1);
/*
* apply RNG fixes
- *
+ *
* among other things, executes Security.insertProviderAt(new
* LinuxPRNGSecureRandomProvider(), 1) for Android <= SDK 17
*/
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
index 3164de7d1..1cac5762d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
@@ -17,20 +17,20 @@
package org.sufficientlysecure.keychain.compatibility;
-import java.lang.reflect.Method;
-
import android.content.Context;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
+import java.lang.reflect.Method;
+
public class ClipboardReflection {
private static final String clipboardLabel = "Keychain";
/**
* Wrapper around ClipboardManager based on Android version using Reflection API
- *
+ *
* @param context
* @param text
*/
@@ -57,7 +57,7 @@ public class ClipboardReflection {
/**
* Wrapper around ClipboardManager based on Android version using Reflection API
- *
+ *
* @param context
*/
public static CharSequence getClipboardText(Context context) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
index 91e50637e..a26df556d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
@@ -37,7 +37,6 @@ public class ActionBarHelper {
* @param activity
*/
public static void setBackButton(ActionBarActivity activity) {
- // set actionbar without home button if called from another app
final ActionBar actionBar = activity.getSupportActionBar();
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
+ activity.getCallingPackage());
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 557d75dbf..810f22d8e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -30,6 +30,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -47,23 +48,21 @@ public class ExportHelper {
this.mActivity = activity;
}
- public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
+ public void deleteKey(Uri dataUri, Handler deleteHandler) {
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
+ long masterKeyId = ProviderHelper.getMasterKeyId(mActivity, dataUri);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- new long[]{keyRingRowId}, keyType);
-
+ new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
}
/**
* Show dialog where to export keys
*/
- public void showExportKeysDialog(final long[] rowIds, final int keyType,
- final String exportFilename) {
+ public void showExportKeysDialog(final long[] masterKeyIds, final String exportFilename,
+ final boolean showSecretCheckbox) {
mExportFilename = exportFilename;
// Message is received after file is selected
@@ -74,7 +73,7 @@ public class ExportHelper {
Bundle data = message.getData();
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- exportKeys(rowIds, keyType);
+ exportKeys(masterKeyIds, data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
}
}
};
@@ -85,7 +84,7 @@ public class ExportHelper {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
String title = null;
- if (rowIds == null) {
+ if (masterKeyIds == null) {
// export all keys
title = mActivity.getString(R.string.title_export_keys);
} else {
@@ -93,15 +92,12 @@ public class ExportHelper {
title = mActivity.getString(R.string.title_export_key);
}
- String message = null;
- 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);
- }
+ String message = mActivity.getString(R.string.specify_file_to_export_to);
+ String checkMsg = showSecretCheckbox ?
+ mActivity.getString(R.string.also_export_secret_keys) : null;
mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- exportFilename, null);
+ exportFilename, checkMsg);
mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
}
@@ -111,7 +107,7 @@ public class ExportHelper {
/**
* Export keys
*/
- public void exportKeys(long[] rowIds, int keyType) {
+ public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
Log.d(Constants.TAG, "exportKeys started");
// Send all information needed to service to export key in other thread
@@ -123,17 +119,17 @@ public class ExportHelper {
Bundle data = new Bundle();
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
- data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
+ data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret);
- if (rowIds == null) {
+ if (masterKeyIds == null) {
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
} 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);
- // Message is received after exporting is done in ApgService
+ // Message is received after exporting is done in KeychainIntentService
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity,
mActivity.getString(R.string.progress_exporting),
ProgressDialog.STYLE_HORIZONTAL,
@@ -145,7 +141,7 @@ public class ExportHelper {
}
}) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 736bff02d..b31a889f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -17,18 +17,14 @@
package org.sufficientlysecure.keychain.helper;
-import android.graphics.Color;
import android.os.Bundle;
-import android.text.Spannable;
import android.text.SpannableStringBuilder;
-import android.text.style.ForegroundColorSpan;
+import android.text.Spanned;
+import android.text.style.StrikethroughSpan;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.DigestException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Set;
@@ -65,81 +61,10 @@ public class OtherHelper {
}
}
- public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
- SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
- try {
- // for each 4 characters of the fingerprint + 1 space
- for (int i = 0; i < fingerprint.length(); i += 5) {
- int spanEnd = Math.min(i + 4, fingerprint.length());
- String fourChars = fingerprint.substring(i, spanEnd);
-
- int raw = Integer.parseInt(fourChars, 16);
- byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
- int[] color = OtherHelper.getRgbForData(bytes);
- int r = color[0];
- int g = color[1];
- int b = color[2];
-
- // we cannot change black by multiplication, so adjust it to an almost-black grey,
- // which will then be brightened to the minimal brightness level
- if (r == 0 && g == 0 && b == 0) {
- r = 1;
- g = 1;
- b = 1;
- }
-
- // Convert rgb to brightness
- double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-
- // If a color is too dark to be seen on black,
- // then brighten it up to a minimal brightness.
- if (brightness < 80) {
- double factor = 80.0 / brightness;
- r = Math.min(255, (int) (r * factor));
- g = Math.min(255, (int) (g * factor));
- b = Math.min(255, (int) (b * factor));
-
- // If it is too light, then darken it to a respective maximal brightness.
- } else if (brightness > 180) {
- double factor = 180.0 / brightness;
- r = (int) (r * factor);
- g = (int) (g * factor);
- b = (int) (b * factor);
- }
-
- // Create a foreground color with the 3 digest integers as RGB
- // and then converting that int to hex to use as a color
- sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
- i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- }
- } catch (Exception e) {
- Log.e(Constants.TAG, "Colorization failed", e);
- // if anything goes wrong, then just display the fingerprint without colour,
- // instead of partially correct colour or wrong colours
- return new SpannableStringBuilder(fingerprint);
- }
-
+ public static SpannableStringBuilder strikeOutText(CharSequence text) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(text);
+ sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return sb;
}
- /**
- * Converts the given bytes to a unique RGB color using SHA1 algorithm
- *
- * @param bytes
- * @return an integer array containing 3 numeric color representations (Red, Green, Black)
- * @throws NoSuchAlgorithmException
- * @throws DigestException
- */
- public static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
- MessageDigest md = MessageDigest.getInstance("SHA1");
-
- md.update(bytes);
- byte[] digest = md.digest();
-
- int[] result = {((int) digest[0] + 256) % 256,
- ((int) digest[1] + 256) % 256,
- ((int) digest[2] + 256) % 256};
- return result;
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 515201b92..ca5555fea 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
* 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
@@ -30,7 +30,7 @@ import java.util.Vector;
* Singleton Implementation of a Preference Helper
*/
public class Preferences {
- private static Preferences mPreferences;
+ private static Preferences sPreferences;
private SharedPreferences mSharedPreferences;
public static synchronized Preferences getPreferences(Context context) {
@@ -38,10 +38,10 @@ public class Preferences {
}
public static synchronized Preferences getPreferences(Context context, boolean forceNew) {
- if (mPreferences == null || forceNew) {
- mPreferences = new Preferences(context);
+ if (sPreferences == null || forceNew) {
+ sPreferences = new Preferences(context);
}
- return mPreferences;
+ return sPreferences;
}
private Preferences(Context context) {
@@ -58,8 +58,8 @@ public class Preferences {
editor.commit();
}
- public long getPassPhraseCacheTtl() {
- int ttl = mSharedPreferences.getInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, 180);
+ public long getPassphraseCacheTtl() {
+ int ttl = mSharedPreferences.getInt(Constants.Pref.PASSPHRASE_CACHE_TTL, 180);
// fix the value if it was set to "never" in previous versions, which currently is not
// supported
if (ttl == 0) {
@@ -68,9 +68,9 @@ public class Preferences {
return (long) ttl;
}
- public void setPassPhraseCacheTtl(int value) {
+ public void setPassphraseCacheTtl(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, value);
+ editor.putInt(Constants.Pref.PASSPHRASE_CACHE_TTL, value);
editor.commit();
}
@@ -118,13 +118,13 @@ public class Preferences {
editor.commit();
}
- public boolean getDefaultAsciiArmour() {
- return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, false);
+ public boolean getDefaultAsciiArmor() {
+ return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, false);
}
- public void setDefaultAsciiArmour(boolean value) {
+ public void setDefaultAsciiArmor(boolean value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, value);
+ editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, value);
editor.commit();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index fa9fcfccd..c6c62d649 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -61,13 +61,32 @@ public class PgpConversionHelper {
* @return
*/
public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
- PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes);
+ PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
+ Object obj = null;
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
-
- @SuppressWarnings("unchecked")
- Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
- while (itr.hasNext()) {
- keys.add(itr.next());
+ try {
+ while ((obj = factory.nextObject()) != null) {
+ PGPSecretKey secKey = null;
+ if (obj instanceof PGPSecretKey) {
+ secKey = (PGPSecretKey) obj;
+ if (secKey == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ keys.add(secKey);
+ } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
+ PGPSecretKeyRing keyRing = null;
+ keyRing = (PGPSecretKeyRing) obj;
+ if (keyRing == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
+ while (itr.hasNext()) {
+ keys.add(itr.next());
+ }
+ }
+ }
+ } catch (IOException e) {
}
return keys;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 571729bc5..8a0bf99d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,28 +18,58 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPCompressedData;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPOnePassSignature;
+import org.spongycastle.openpgp.PGPOnePassSignatureList;
+import org.spongycastle.openpgp.PGPPBEEncryptedData;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.security.SignatureException;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Set;
/**
* This class uses a Builder pattern!
@@ -50,9 +80,9 @@ public class PgpDecryptVerify {
private OutputStream mOutStream;
private ProgressDialogUpdater mProgressDialogUpdater;
- private boolean mAssumeSymmetric;
+ private boolean mAllowSymmetricDecryption;
private String mPassphrase;
- private long mEnforcedKeyId;
+ private Set<Long> mAllowedKeyIds;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
@@ -61,9 +91,9 @@ public class PgpDecryptVerify {
this.mOutStream = builder.mOutStream;
this.mProgressDialogUpdater = builder.mProgressDialogUpdater;
- this.mAssumeSymmetric = builder.mAssumeSymmetric;
+ this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption;
this.mPassphrase = builder.mPassphrase;
- this.mEnforcedKeyId = builder.mEnforcedKeyId;
+ this.mAllowedKeyIds = builder.mAllowedKeyIds;
}
public static class Builder {
@@ -74,9 +104,9 @@ public class PgpDecryptVerify {
// optional
private ProgressDialogUpdater mProgressDialogUpdater = null;
- private boolean mAssumeSymmetric = false;
- private String mPassphrase = "";
- private long mEnforcedKeyId = 0;
+ private boolean mAllowSymmetricDecryption = true;
+ private String mPassphrase = null;
+ private Set<Long> mAllowedKeyIds = null;
public Builder(Context context, InputData data, OutputStream outStream) {
this.mContext = context;
@@ -89,8 +119,8 @@ public class PgpDecryptVerify {
return this;
}
- public Builder assumeSymmetric(boolean assumeSymmetric) {
- this.mAssumeSymmetric = assumeSymmetric;
+ public Builder allowSymmetricDecryption(boolean allowSymmetricDecryption) {
+ this.mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
}
@@ -100,14 +130,14 @@ public class PgpDecryptVerify {
}
/**
- * Allow this key id alone for decryption.
- * This means only ciphertexts encrypted for this private key can be decrypted.
+ * Allow these key ids alone for decryption.
+ * This means only ciphertexts encrypted for one of these private key can be decrypted.
*
- * @param enforcedKeyId
+ * @param allowedKeyIds
* @return
*/
- public Builder enforcedKeyId(long enforcedKeyId) {
- this.mEnforcedKeyId = enforcedKeyId;
+ public Builder allowedKeyIds(Set<Long> allowedKeyIds) {
+ this.mAllowedKeyIds = allowedKeyIds;
return this;
}
@@ -128,35 +158,6 @@ public class PgpDecryptVerify {
}
}
- public static boolean hasSymmetricEncryption(Context context, InputStream inputStream)
- throws PgpGeneralException, IOException {
- InputStream in = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
- // the first object might be a PGP marker packet.
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
- }
-
- Iterator<?> it = enc.getEncryptedDataObjects();
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Decrypts and/or verifies data based on parameters of class
*
@@ -221,25 +222,82 @@ public class PgpDecryptVerify {
currentProgress += 5;
- // TODO: currently we always only look at the first known key or symmetric encryption,
- // there might be more...
- if (mAssumeSymmetric) {
- PGPPBEEncryptedData pbe = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- pbe = (PGPPBEEncryptedData) obj;
- break;
+ PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
+ PGPPBEEncryptedData encryptedDataSymmetric = null;
+ PGPSecretKey secretKey = null;
+ Iterator<?> it = enc.getEncryptedDataObjects();
+ boolean symmetricPacketFound = false;
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ updateProgress(R.string.progress_finding_key, currentProgress, 100);
+
+ PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
+ long masterKeyId = ProviderHelper.getMasterKeyId(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
+ );
+ PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRing(mContext, masterKeyId);
+ if (secretKeyRing == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
- }
+ secretKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ if (secretKey == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
+ }
+ // secret key exists in database
- if (pbe == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_symmetric_encryption_packet));
+ // allow only a specific key for decryption?
+ if (mAllowedKeyIds != null) {
+ Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
+ Log.d(Constants.TAG, "allowedKeyIds: " + mAllowedKeyIds);
+ Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
+
+ if (!mAllowedKeyIds.contains(masterKeyId)) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_no_secret_key_found));
+ }
+ }
+
+ encryptedDataAsymmetric = encData;
+
+ // if no passphrase was explicitly set try to get it from the cache service
+ if (mPassphrase == null) {
+ // returns "" if key has no passphrase
+ mPassphrase =
+ PassphraseCacheService.getCachedPassphrase(mContext, masterKeyId);
+
+ // if passphrase was not cached, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setKeyIdPassphraseNeeded(masterKeyId);
+ returnData.setStatus(PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED);
+ return returnData;
+ }
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
+ } else if (mAllowSymmetricDecryption && obj instanceof PGPPBEEncryptedData) {
+ symmetricPacketFound = true;
+
+ encryptedDataSymmetric = (PGPPBEEncryptedData) obj;
+
+ // if no passphrase is given, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setStatus(PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED);
+ return returnData;
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
}
+ }
+ if (symmetricPacketFound) {
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
@@ -248,64 +306,11 @@ public class PgpDecryptVerify {
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray());
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataSymmetric;
currentProgress += 5;
} else {
- updateProgress(R.string.progress_finding_key, currentProgress, 100);
-
- PGPPublicKeyEncryptedData pbe = null;
- PGPSecretKey secretKey = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPublicKeyEncryptedData) {
- PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(mContext, encData.getKeyID());
- if (secretKey != null) {
- // secret key exists in database
-
- // allow only a specific key for decryption?
- if (mEnforcedKeyId != 0) {
- // TODO: improve this code! get master key directly!
- PGPSecretKeyRing secretKeyRing =
- ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, encData.getKeyID());
- long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
- Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
- Log.d(Constants.TAG, "enforcedKeyId: " + mEnforcedKeyId);
- Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
-
- if (mEnforcedKeyId != masterKeyId) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_secret_key_found));
- }
- }
-
- pbe = encData;
-
- // if no passphrase was explicitly set try to get it from the cache service
- if (mPassphrase == null) {
- // returns "" if key has no passphrase
- mPassphrase =
- PassphraseCacheService.getCachedPassphrase(mContext, encData.getKeyID());
-
- // if passphrase was not cached, return here
- // indicating that a passphrase is missing!
- if (mPassphrase == null) {
- returnData.setKeyPassphraseNeeded(true);
- return returnData;
- }
- }
-
- break;
- }
-
-
- }
- }
-
if (secretKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
@@ -331,9 +336,9 @@ public class PgpDecryptVerify {
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataAsymmetric;
currentProgress += 5;
}
@@ -363,7 +368,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
signatureKey = ProviderHelper
- .getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
+ .getPGPPublicKeyRing(mContext, signature.getKeyID()).getPublicKey();
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -373,10 +378,10 @@ public class PgpDecryptVerify {
signatureIndex = i;
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(
mContext, signatureKeyId);
if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
+ userId = PgpKeyHelper.getMainUserId(signKeyRing.getPublicKey());
}
signatureResult.setUserId(userId);
break;
@@ -388,7 +393,7 @@ public class PgpDecryptVerify {
if (signature != null) {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
} else {
@@ -546,25 +551,27 @@ public class PgpDecryptVerify {
long signatureKeyId = 0;
PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
+ signature = sigList.get(i);
+ signatureKeyId = signature.getKeyID();
+
+ // find data about this subkey
+ HashMap<String, Object> data = ProviderHelper.getGenericData(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(signature.getKeyID())),
+ new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
+ new int[] { ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_STRING });
+ // any luck? otherwise, try next.
+ if(data.get(KeyRings.MASTER_KEY_ID) == null) {
signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
- signatureKeyId);
- if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
- }
- signatureResult.setUserId(userId);
- break;
+ // do NOT reset signatureKeyId, that one is shown when no known one is found!
+ continue;
}
+
+ // this one can't fail now (yay database constraints)
+ signatureKey = ProviderHelper.getPGPPublicKeyRing(mContext, (Long) data.get(KeyRings.MASTER_KEY_ID)).getPublicKey();
+ signatureResult.setUserId((String) data.get(KeyRings.USER_ID));
+
+ break;
}
signatureResult.setKeyId(signatureKeyId);
@@ -621,11 +628,11 @@ public class PgpDecryptVerify {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(context,
signatureKeyId);
PGPPublicKey mKey = null;
if (signKeyRing != null) {
- mKey = PgpKeyHelper.getMasterKey(signKeyRing);
+ mKey = signKeyRing.getPublicKey();
}
if (signature.getKeyID() != mKey.getKeyID()) {
@@ -685,7 +692,8 @@ public class PgpDecryptVerify {
}
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
- PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ PGPPublicKey masterPublicKey,
+ PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
index d4a4f6075..ad240e834 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
@@ -19,27 +19,33 @@ package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
+
import org.openintents.openpgp.OpenPgpSignatureResult;
public class PgpDecryptVerifyResult implements Parcelable {
- boolean mSymmetricPassphraseNeeded;
- boolean mKeyPassphraseNeeded;
+ public static final int SUCCESS = 1;
+ public static final int KEY_PASSHRASE_NEEDED = 2;
+ public static final int SYMMETRIC_PASSHRASE_NEEDED = 3;
+
+ int mStatus;
+ long mKeyIdPassphraseNeeded;
+
OpenPgpSignatureResult mSignatureResult;
- public boolean isSymmetricPassphraseNeeded() {
- return mSymmetricPassphraseNeeded;
+ public int getStatus() {
+ return mStatus;
}
- public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
- this.mSymmetricPassphraseNeeded = symmetricPassphraseNeeded;
+ public void setStatus(int mStatus) {
+ this.mStatus = mStatus;
}
- public boolean isKeyPassphraseNeeded() {
- return mKeyPassphraseNeeded;
+ public long getKeyIdPassphraseNeeded() {
+ return mKeyIdPassphraseNeeded;
}
- public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
- this.mKeyPassphraseNeeded = keyPassphraseNeeded;
+ public void setKeyIdPassphraseNeeded(long mKeyIdPassphraseNeeded) {
+ this.mKeyIdPassphraseNeeded = mKeyIdPassphraseNeeded;
}
public OpenPgpSignatureResult getSignatureResult() {
@@ -55,8 +61,8 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
- this.mSymmetricPassphraseNeeded = b.mSymmetricPassphraseNeeded;
- this.mKeyPassphraseNeeded = b.mKeyPassphraseNeeded;
+ this.mStatus = b.mStatus;
+ this.mKeyIdPassphraseNeeded = b.mKeyIdPassphraseNeeded;
this.mSignatureResult = b.mSignatureResult;
}
@@ -66,16 +72,16 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (mSymmetricPassphraseNeeded ? 1 : 0));
- dest.writeByte((byte) (mKeyPassphraseNeeded ? 1 : 0));
+ dest.writeInt(mStatus);
+ dest.writeLong(mKeyIdPassphraseNeeded);
dest.writeParcelable(mSignatureResult, 0);
}
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
- vr.mSymmetricPassphraseNeeded = source.readByte() == 1;
- vr.mKeyPassphraseNeeded = source.readByte() == 1;
+ vr.mStatus = source.readInt();
+ vr.mKeyIdPassphraseNeeded = source.readLong();
vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
return vr;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 2680d77af..f884b1776 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -20,7 +20,14 @@ package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -43,10 +50,10 @@ public class PgpHelper {
public static final Pattern PGP_MESSAGE = Pattern.compile(
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
- public static final Pattern PGP_SIGNED_MESSAGE = Pattern
- .compile(
- ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
+ public static final Pattern PGP_CLEARTEXT_SIGNATURE = Pattern
+ .compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----" +
+ "BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
public static final Pattern PGP_PUBLIC_KEY = Pattern.compile(
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
@@ -96,7 +103,7 @@ public class PgpHelper {
if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID());
+ secretKey = ProviderHelper.getPGPSecretKeyRing(context, pbe.getKeyID()).getSecretKey();
if (secretKey != null) {
break;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index dbfa521e5..d03f3ccc2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -20,8 +20,14 @@ package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
+
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -30,8 +36,12 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.*;
+import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
+import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -158,60 +168,69 @@ public class PgpImportExport {
return returnData;
}
- public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
+ public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds,
+ ArrayList<Long> secretKeyRingMasterIds,
OutputStream outStream) throws PgpGeneralException,
PGPException, IOException {
Bundle returnData = new Bundle();
- int rowIdsSize = keyRingRowIds.size();
+ int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
+ int progress = 0;
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- rowIdsSize), 0, 100);
+ masterKeyIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready));
}
- // For each row id
- for (int i = 0; i < rowIdsSize; ++i) {
+ // For each public masterKey id
+ for (long pubKeyMasterId : publicKeyRingMasterIds) {
+ progress++;
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
- // If the keyType is secret get the PGPSecretKeyRing
- // based on the row id and encode it to the output
- if (keyType == Id.type.secret_key) {
- updateProgress(i * 100 / rowIdsSize / 2, 100);
- PGPSecretKeyRing secretKeyRing =
- ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+ PGPPublicKeyRing publicKeyRing =
+ ProviderHelper.getPGPPublicKeyRing(mContext, pubKeyMasterId);
- if (secretKeyRing != null) {
- secretKeyRing.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) {
+ publicKeyRing.encode(arOutStream);
+ }
- if (publicKeyRing != null) {
- publicKeyRing.encode(arOutStream);
- }
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
+ }
- if (mKeychainServiceListener.hasServiceStopped()) {
- arOutStream.close();
- return null;
- }
+ arOutStream.close();
+ }
+
+ // 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.getPGPSecretKeyRing(mContext, secretKeyMasterId);
+
+ if (secretKeyRing != null) {
+ secretKeyRing.encode(arOutStream);
+ }
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
}
arOutStream.close();
}
- returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize);
+ returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
updateProgress(R.string.progress_done, 100, 100);
@@ -241,7 +260,6 @@ public class PgpImportExport {
}
if (save) {
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
@@ -256,6 +274,7 @@ public class PgpImportExport {
if (newPubRing != null) {
ProviderHelper.saveKeyRing(mContext, newPubRing);
}
+ ProviderHelper.saveKeyRing(mContext, secretKeyRing);
// TODO: remove status returns, use exceptions!
status = Id.return_value.ok;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 586eb0776..4c786f555 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -18,8 +18,18 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+import android.graphics.Color;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -27,7 +37,14 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.*;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,34 +61,6 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
- public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
@@ -202,9 +191,8 @@ public class PgpKeyHelper {
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, key.getValidDays());
- Date expiryDate = calendar.getTime();
- return expiryDate;
+ return calendar.getTime();
}
public static Date getExpiryDate(PGPSecretKey key) {
@@ -212,8 +200,7 @@ public class PgpKeyHelper {
}
public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRing(context, masterKeyId);
if (keyRing == null) {
Log.e(Constants.TAG, "keyRing is null!");
return null;
@@ -227,8 +214,7 @@ public class PgpKeyHelper {
}
public static PGPSecretKey getCertificationKey(Context context, long masterKeyId) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
if (keyRing == null) {
return null;
}
@@ -240,8 +226,7 @@ public class PgpKeyHelper {
}
public static PGPSecretKey getSigningKey(Context context, long masterKeyId) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
if (keyRing == null) {
return null;
}
@@ -284,6 +269,33 @@ public class PgpKeyHelper {
return userId;
}
+ public static int getKeyUsage(PGPSecretKey key) {
+ return getKeyUsage(key.getPublicKey());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static int getKeyUsage(PGPPublicKey key) {
+ int usage = 0;
+ if (key.getVersion() >= 4) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (hashed != null) {
+ usage |= hashed.getKeyFlags();
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+ if (unhashed != null) {
+ usage |= unhashed.getKeyFlags();
+ }
+ }
+ }
+ return usage;
+ }
+
@SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
@@ -390,6 +402,36 @@ public class PgpKeyHelper {
return false;
}
+ public static boolean isAuthenticationKey(PGPSecretKey key) {
+ return isAuthenticationKey(key.getPublicKey());
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean isAuthenticationKey(PGPPublicKey key) {
+ if (key.getVersion() <= 3) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}
@@ -403,7 +445,7 @@ public class PgpKeyHelper {
}
public static String getAlgorithmInfo(int algorithm, int keySize) {
- String algorithmStr = null;
+ String algorithmStr;
switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT:
@@ -434,21 +476,6 @@ public class PgpKeyHelper {
return algorithmStr;
}
- public static String getFingerPrint(Context context, long keyId) {
- PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
- // if it is no public key get it from your own keys...
- if (key == null) {
- PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
- if (secretKey == null) {
- Log.e(Constants.TAG, "Key could not be found!");
- return null;
- }
- key = secretKey.getPublicKey();
- }
-
- return convertFingerprintToHex(key.getFingerprint(), true);
- }
-
/**
* Converts fingerprint to hex (optional: with whitespaces after 4 characters)
* <p/>
@@ -456,14 +483,10 @@ public class PgpKeyHelper {
* better differentiate between numbers and letters when letters are lowercase.
*
* @param fingerprint
- * @param split split into 4 character chunks
* @return
*/
- public static String convertFingerprintToHex(byte[] fingerprint, boolean split) {
+ public static String convertFingerprintToHex(byte[] fingerprint) {
String hexString = Hex.toHexString(fingerprint);
- if (split) {
- hexString = hexString.replaceAll("(.{4})(?!$)", "$1 ");
- }
return hexString;
}
@@ -479,9 +502,18 @@ public class PgpKeyHelper {
* @return
*/
public static String convertKeyIdToHex(long keyId) {
+ long upper = keyId >> 32;
+ if (upper == 0) {
+ // this is a short key id
+ return convertKeyIdToHexShort(keyId);
+ }
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
}
+ public static String convertKeyIdToHexShort(long keyId) {
+ return "0x" + convertKeyIdToHex32bit(keyId);
+ }
+
private static String convertKeyIdToHex32bit(long keyId) {
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
while (hexString.length() < 8) {
@@ -490,17 +522,90 @@ public class PgpKeyHelper {
return hexString;
}
+
+ public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
+ // split by 4 characters
+ fingerprint = fingerprint.replaceAll("(.{4})(?!$)", "$1 ");
+
+ // add line breaks to have a consistent "image" that can be recognized
+ char[] chars = fingerprint.toCharArray();
+ chars[24] = '\n';
+ fingerprint = String.valueOf(chars);
+
+ SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
+ try {
+ // for each 4 characters of the fingerprint + 1 space
+ for (int i = 0; i < fingerprint.length(); i += 5) {
+ int spanEnd = Math.min(i + 4, fingerprint.length());
+ String fourChars = fingerprint.substring(i, spanEnd);
+
+ int raw = Integer.parseInt(fourChars, 16);
+ byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
+ int[] color = getRgbForData(bytes);
+ int r = color[0];
+ int g = color[1];
+ int b = color[2];
+
+ // we cannot change black by multiplication, so adjust it to an almost-black grey,
+ // which will then be brightened to the minimal brightness level
+ if (r == 0 && g == 0 && b == 0) {
+ r = 1;
+ g = 1;
+ b = 1;
+ }
+
+ // Convert rgb to brightness
+ double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+
+ // If a color is too dark to be seen on black,
+ // then brighten it up to a minimal brightness.
+ if (brightness < 80) {
+ double factor = 80.0 / brightness;
+ r = Math.min(255, (int) (r * factor));
+ g = Math.min(255, (int) (g * factor));
+ b = Math.min(255, (int) (b * factor));
+
+ // If it is too light, then darken it to a respective maximal brightness.
+ } else if (brightness > 180) {
+ double factor = 180.0 / brightness;
+ r = (int) (r * factor);
+ g = (int) (g * factor);
+ b = (int) (b * factor);
+ }
+
+ // Create a foreground color with the 3 digest integers as RGB
+ // and then converting that int to hex to use as a color
+ sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
+ i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "Colorization failed", e);
+ // if anything goes wrong, then just display the fingerprint without colour,
+ // instead of partially correct colour or wrong colours
+ return new SpannableStringBuilder(fingerprint);
+ }
+
+ return sb;
+ }
+
/**
- * Used in HkpKeyServer to convert hex encoded key ids back to long.
+ * Converts the given bytes to a unique RGB color using SHA1 algorithm
*
- * @param hexString
- * @return
+ * @param bytes
+ * @return an integer array containing 3 numeric color representations (Red, Green, Black)
+ * @throws java.security.NoSuchAlgorithmException
+ * @throws java.security.DigestException
*/
- public static long convertHexToKeyId(String hexString) {
- int len = hexString.length();
- String s2 = hexString.substring(len - 8);
- String s1 = hexString.substring(0, len - 8);
- return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
+ private static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+
+ md.update(bytes);
+ byte[] digest = md.digest();
+
+ int[] result = {((int) digest[0] + 256) % 256,
+ ((int) digest[1] + 256) % 256,
+ ((int) digest[2] + 256) % 256};
+ return result;
}
/**
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 40a0b72ce..48b959738 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -17,33 +17,54 @@
package org.sufficientlysecure.keychain.pgp;
-import android.content.Context;
+import android.util.Pair;
+
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyPair;
+import org.spongycastle.openpgp.PGPKeyRingGenerator;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.*;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -51,8 +72,16 @@ import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
+/** This class is the single place where ALL operations that actually modify a PGP public or secret
+ * key take place.
+ *
+ * Note that no android specific stuff should be done here, ie no imports from com.android.
+ *
+ * All operations support progress reporting to a ProgressDialogUpdater passed on initialization.
+ * This indicator may be null.
+ *
+ */
public class PgpKeyOperation {
- private Context mContext;
private ProgressDialogUpdater mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
@@ -65,19 +94,18 @@ public class PgpKeyOperation {
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP};
- public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
+ public PgpKeyOperation(ProgressDialogUpdater progress) {
super();
- this.mContext = context;
this.mProgress = progress;
}
- public void updateProgress(int message, int current, int total) {
+ void updateProgress(int message, int current, int total) {
if (mProgress != null) {
mProgress.setProgress(message, current, total);
}
}
- public void updateProgress(int current, int total) {
+ void updateProgress(int current, int total) {
if (mProgress != null) {
mProgress.setProgress(current, total);
}
@@ -90,11 +118,11 @@ public class PgpKeyOperation {
* @param keySize
* @param passphrase
* @param isMasterKey
- * @return
+ * @return A newly created PGPSecretKey
* @throws NoSuchAlgorithmException
* @throws PGPException
* @throws NoSuchProviderException
- * @throws PgpGeneralException
+ * @throws PgpGeneralMsgIdException
* @throws InvalidAlgorithmParameterException
*/
@@ -102,18 +130,18 @@ public class PgpKeyOperation {
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- PgpGeneralException, InvalidAlgorithmParameterException {
+ PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
if (keySize < 512) {
- throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
+ throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
}
if (passphrase == null) {
passphrase = "";
}
- int algorithm = 0;
- KeyPairGenerator keyGen = null;
+ int algorithm;
+ KeyPairGenerator keyGen;
switch (algorithmChoice) {
case Id.choice.algorithm.dsa: {
@@ -125,8 +153,7 @@ public class PgpKeyOperation {
case Id.choice.algorithm.elgamal: {
if (isMasterKey) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
}
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
@@ -148,8 +175,7 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_unknown_algorithm_choice));
+ throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
}
}
@@ -165,193 +191,114 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
- sha1Calc, isMasterKey, keyEncryptor);
-
- return secKey;
+ return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
+ sha1Calc, isMasterKey, keyEncryptor);
}
- public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException,
- NoSuchProviderException {
+ public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
+ String newPassphrase)
+ throws IOException, PGPException, NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
- if (oldPassPhrase == null) {
- oldPassPhrase = "";
+ if (oldPassphrase == null) {
+ oldPassphrase = "";
}
- if (newPassPhrase == null) {
- newPassPhrase = "";
+ if (newPassphrase == null) {
+ newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
keyRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()),
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
- .getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray()));
-
- updateProgress(R.string.progress_saving_key_ring, 50, 100);
-
- ProviderHelper.saveKeyRing(mContext, newKeyRing);
+ .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
- updateProgress(R.string.progress_done, 100, 100);
+ return newKeyRing;
}
- public void buildSecretKey(ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
- ArrayList<Integer> keysUsages, ArrayList<GregorianCalendar> keysExpiryDates,
- PGPPublicKey oldPublicKey, String oldPassPhrase,
- String newPassPhrase) throws PgpGeneralException, NoSuchProviderException,
- PGPException, NoSuchAlgorithmException, SignatureException, IOException {
-
- Log.d(Constants.TAG, "userIds: " + userIds.toString());
-
- updateProgress(R.string.progress_building_key, 0, 100);
+ private Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
+ ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
+ ArrayList<GregorianCalendar> keysExpiryDates,
+ ArrayList<Integer> keysUsages,
+ String newPassphrase, String oldPassphrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
- if (oldPassPhrase == null) {
- oldPassPhrase = "";
- }
- if (newPassPhrase == null) {
- newPassPhrase = "";
- }
+ int usageId = keysUsages.get(0);
+ boolean canSign;
+ String mainUserId = userIds.get(0);
- updateProgress(R.string.progress_preparing_master_key, 10, 100);
-
- // prepare keyring generator with given master public and secret key
- PGPKeyRingGenerator keyGen;
- PGPPublicKey masterPublicKey; {
-
- String mainUserId = userIds.get(0);
-
- // prepare the master key pair
- PGPKeyPair masterKeyPair; {
-
- PGPSecretKey masterKey = keys.get(0);
-
- // this removes all userIds and certifications previously attached to the masterPublicKey
- PGPPublicKey tmpKey = masterKey.getPublicKey();
- masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
- tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
-
- // already done by code above:
- // PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- // // Somehow, the PGPPublicKey already has an empty certification attached to it when the
- // // keyRing is generated the first time, we remove that when it exists, before adding the
- // new
- // // ones
- // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey,
- // "");
- // if (masterPublicKeyRmCert != null) {
- // masterPublicKey = masterPublicKeyRmCert;
- // }
-
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray());
- PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
-
- updateProgress(R.string.progress_certifying_master_key, 20, 100);
-
- // re-add old certificates, or create new ones for new uids
- for (String userId : userIds) {
- // re-add certs for this uid, take a note if self-signed cert is in there
- boolean foundSelfSign = false;
- Iterator<PGPSignature> it = tmpKey.getSignaturesForID(userId);
- if(it != null) for(PGPSignature sig : new IterableIterator<PGPSignature>(it)) {
- if(sig.getKeyID() == masterPublicKey.getKeyID()) {
- // already have a self sign? skip this other one, then.
- // note: PGPKeyRingGenerator adds one cert for the main user id, which
- // will lead to duplicates. unfortunately, if we add any other here
- // first, that will change the main user id order...
- if(foundSelfSign)
- continue;
- foundSelfSign = true;
- }
- Log.d(Constants.TAG, "adding old sig for " + userId + " from "
- + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()));
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, sig);
- }
+ PGPSecretKey masterKey = keys.get(0);
- // there was an old self-signed certificate for this uid
- if(foundSelfSign)
- continue;
+ // this removes all userIds and certifications previously attached to the masterPublicKey
+ PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- Log.d(Constants.TAG, "generating self-signed cert for " + userId);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
- sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ for (String userId : userIds) {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
- }
-
- masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
- }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
- PGPSignatureSubpacketGenerator hashedPacketsGen;
- PGPSignatureSubpacketGenerator unhashedPacketsGen; {
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
- hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
- int usageId = keysUsages.get(0);
- boolean canEncrypt =
- (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt);
+ hashedPacketsGen.setKeyFlags(true, usageId);
- int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(true, keyFlags);
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
- hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
- hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
- hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
-
- if (keysExpiryDates.get(0) != null) {
- GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- creationDate.setTime(masterPublicKey.getCreationTime());
- GregorianCalendar expiryDate = keysExpiryDates.get(0);
- //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
- //here we purposefully ignore partial days in each date - long type has no fractional part!
- long numDays =
- (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
- if (numDays <= 0) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_expiry_must_come_after_creation));
- }
- hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
- } else {
- //do this explicitly, although since we're rebuilding,
- hashedPacketsGen.setKeyExpirationTime(false, 0);
- //this happens anyway
- }
+ if (keysExpiryDates.get(0) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(masterPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = keysExpiryDates.get(0);
+ //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ //here we purposefully ignore partial days in each date - long type has no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
- updateProgress(R.string.progress_building_master_key, 30, 100);
-
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
- HashAlgorithmTags.SHA1);
- PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
- masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
+ updateProgress(R.string.progress_building_master_key, 30, 100);
- // Build key encrypter based on passphrase
- PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- newPassPhrase.toCharArray());
+ // define hashing and signing algos
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
+ HashAlgorithmTags.SHA1);
+ PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
+ masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
- keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
- masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
- unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
+ // Build key encrypter based on passphrase
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ newPassphrase.toCharArray());
- }
+ PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
+ unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
updateProgress(R.string.progress_adding_sub_keys, 40, 100);
@@ -361,27 +308,21 @@ public class PgpKeyOperation {
PGPSecretKey subKey = keys.get(i);
PGPPublicKey subPublicKey = subKey.getPublicKey();
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- oldPassPhrase.toCharArray());
- PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor);
+ oldPassphrase.toCharArray());
+ PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
// TODO: now used without algorithm and creation time?! (APG 1)
PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
- int keyFlags = 0;
-
- int usageId = keysUsages.get(i);
- boolean canSign =
- (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt);
- boolean canEncrypt =
- (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt);
+ usageId = keysUsages.get(i);
+ canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this
if (canSign) {
Date todayDate = new Date(); //both sig times the same
- keyFlags |= KeyFlags.SIGN_DATA;
// cross-certify signing keys
hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
@@ -396,10 +337,7 @@ public class PgpKeyOperation {
subPublicKey);
unhashedPacketsGen.setEmbeddedSignature(false, certification);
}
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(false, keyFlags);
+ hashedPacketsGen.setKeyFlags(false, usageId);
if (keysExpiryDates.get(i) != null) {
GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
@@ -407,17 +345,16 @@ public class PgpKeyOperation {
GregorianCalendar expiryDate = keysExpiryDates.get(i);
//note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
//here we purposefully ignore partial days in each date - long type has no fractional part!
- long numDays =
- (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0) {
- throw new PgpGeneralException
- (mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
- //do this explicitly, although since we're rebuilding,
hashedPacketsGen.setKeyExpirationTime(false, 0);
- //this happens anyway
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
}
keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
@@ -426,102 +363,407 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- updateProgress(R.string.progress_re_adding_certs, 80, 100);
-
- // re-add certificates from old public key
- // TODO: this only takes care of user id certificates, what about others?
- PGPPublicKey pubkey = publicKeyRing.getPublicKey();
- for(String uid : new IterableIterator<String>(pubkey.getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(oldPublicKey.getSignaturesForID(uid), true)) {
- // but skip self certificates
- if(sig.getKeyID() == pubkey.getKeyID())
- continue;
- pubkey = PGPPublicKey.addCertification(pubkey, uid, sig);
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
+
+ }
+
+ public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+
+ updateProgress(R.string.progress_building_key, 0, 100);
+ PGPSecretKey masterKey = saveParcel.keys.get(0);
+
+ if (saveParcel.oldPassphrase == null) {
+ saveParcel.oldPassphrase = "";
+ }
+ if (saveParcel.newPassphrase == null) {
+ saveParcel.newPassphrase = "";
+ }
+
+ if (mKR == null) {
+ return buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
+ saveParcel.keysUsages, saveParcel.newPassphrase, saveParcel.oldPassphrase); //new Keyring
+ }
+
+ /*
+ IDs - NB This might not need to happen later, if we change the way the primary ID is chosen
+ remove deleted ids
+ if the primary ID changed we need to:
+ remove all of the IDs from the keyring, saving their certifications
+ add them all in again, updating certs of IDs which have changed
+ else
+ remove changed IDs and add in with new certs
+
+ if the master key changed, we need to remove the primary ID certification, so we can add
+ the new one when it is generated, and they don't conflict
+
+ Keys
+ remove deleted keys
+ if a key is modified, re-sign it
+ do we need to remove and add in?
+
+ Todo
+ identify more things which need to be preserved - e.g. trust levels?
+ user attributes
+ */
+
+ if (saveParcel.deletedKeys != null) {
+ for (PGPSecretKey dKey : saveParcel.deletedKeys) {
+ mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
+ }
+ }
+
+ masterKey = mKR.getSecretKey();
+ PGPPublicKey masterPublicKey = masterKey.getPublicKey();
+
+ int usageId = saveParcel.keysUsages.get(0);
+ boolean canSign;
+ String mainUserId = saveParcel.userIDs.get(0);
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
+
+ boolean anyIDChanged = false;
+ for (String delID : saveParcel.deletedIDs) {
+ anyIDChanged = true;
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID);
+ }
+
+ int userIDIndex = 0;
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ hashedPacketsGen.setKeyFlags(true, usageId);
+
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+
+ if (saveParcel.keysExpiryDates.get(0) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(masterPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(0);
+ //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ //here we purposefully ignore partial days in each date - long type has no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
+
+ if (saveParcel.primaryIDChanged ||
+ !saveParcel.originalIDs.get(0).equals(saveParcel.userIDs.get(0))) {
+ anyIDChanged = true;
+ ArrayList<Pair<String, PGPSignature>> sigList = new ArrayList<Pair<String, PGPSignature>>();
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (origID.equals(userId) && !saveParcel.newIDs[userIDIndex] &&
+ !userId.equals(saveParcel.originalPrimaryID) && userIDIndex != 0) {
+ Iterator<PGPSignature> origSigs = masterPublicKey.getSignaturesForID(origID);
+ // TODO: make sure this iterator only has signatures we are interested in
+ while (origSigs.hasNext()) {
+ PGPSignature origSig = origSigs.next();
+ sigList.add(new Pair<String, PGPSignature>(origID, origSig));
+ }
+ } else {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ if (userIDIndex == 0) {
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+ }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ sigList.add(new Pair<String, PGPSignature>(userId, certification));
+ }
+ if (!saveParcel.newIDs[userIDIndex]) {
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID);
+ }
+ userIDIndex++;
+ }
+ for (Pair<String, PGPSignature> toAdd : sigList) {
+ masterPublicKey =
+ PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second);
+ }
+ } else {
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (!origID.equals(userId) || saveParcel.newIDs[userIDIndex]) {
+ anyIDChanged = true;
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ if (userIDIndex == 0) {
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+ }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ if (!saveParcel.newIDs[userIDIndex]) {
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID);
+ }
+ masterPublicKey =
+ PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
+ userIDIndex++;
+ }
+ }
+
+ ArrayList<Pair<String, PGPSignature>> sigList = new ArrayList<Pair<String, PGPSignature>>();
+ if (saveParcel.moddedKeys[0]) {
+ userIDIndex = 0;
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (!(origID.equals(saveParcel.originalPrimaryID) && !saveParcel.primaryIDChanged)) {
+ Iterator<PGPSignature> sigs = masterPublicKey.getSignaturesForID(userId);
+ // TODO: make sure this iterator only has signatures we are interested in
+ while (sigs.hasNext()) {
+ PGPSignature sig = sigs.next();
+ sigList.add(new Pair<String, PGPSignature>(userId, sig));
+ }
+ }
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, userId);
+ userIDIndex++;
+ }
+ anyIDChanged = true;
+ }
+
+ //update the keyring with the new ID information
+ if (anyIDChanged) {
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey);
+ mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR);
+ }
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+
+ updateProgress(R.string.progress_building_master_key, 30, 100);
+
+ // define hashing and signing algos
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
+ HashAlgorithmTags.SHA1);
+ PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
+ masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
+
+ // Build key encryptor based on old passphrase, as some keys may be unchanged
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.oldPassphrase.toCharArray());
+
+ //this generates one more signature than necessary...
+ PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
+ unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
+
+ for (int i = 1; i < saveParcel.keys.size(); ++i) {
+ updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
+ if (saveParcel.moddedKeys[i]) {
+ PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPPublicKey subPublicKey = subKey.getPublicKey();
+
+ PBESecretKeyDecryptor keyDecryptor2;
+ if (saveParcel.newKeys[i]) {
+ keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ "".toCharArray());
+ } else {
+ keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.oldPassphrase.toCharArray());
+ }
+ PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
+ PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
+
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ usageId = saveParcel.keysUsages.get(i);
+ canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this
+ if (canSign) {
+ Date todayDate = new Date(); //both sig times the same
+ // cross-certify signing keys
+ hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ subPublicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ PGPSignature certification = sGen.generateCertification(masterPublicKey,
+ subPublicKey);
+ unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ }
+ hashedPacketsGen.setKeyFlags(false, usageId);
+
+ if (saveParcel.keysExpiryDates.get(i) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(subPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(i);
+ // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ // here we purposefully ignore partial days in each date - long type has
+ // no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
+
+ keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
+ // certifications will be discarded if the key is changed, because I think, for a start,
+ // they will be invalid. Binding certs are regenerated anyway, and other certs which
+ // need to be kept are on IDs and attributes
+ // TODO: don't let revoked keys be edited, other than removed - changing one would
+ // result in the revocation being wrong?
}
}
- publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, pubkey);
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
+ PGPSecretKeyRing updatedSecretKeyRing = keyGen.generateSecretKeyRing();
+ //finally, update the keyrings
+ Iterator<PGPSecretKey> itr = updatedSecretKeyRing.getSecretKeys();
+ while (itr.hasNext()) {
+ PGPSecretKey theNextKey = itr.next();
+ if ((theNextKey.isMasterKey() && saveParcel.moddedKeys[0]) || !theNextKey.isMasterKey()) {
+ mKR = PGPSecretKeyRing.insertSecretKey(mKR, theNextKey);
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey());
+ }
+ }
+
+ //replace lost IDs
+ if (saveParcel.moddedKeys[0]) {
+ masterPublicKey = mKR.getPublicKey();
+ for (Pair<String, PGPSignature> toAdd : sigList) {
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second);
+ }
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey);
+ mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR);
+ }
+
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.newPassphrase.toCharArray());
+
+ //update the passphrase
+ mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
/* additional handy debug info
+
Log.d(Constants.TAG, " ------- in private key -------");
+
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
- Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
- }
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(
+ secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " +
+ PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ }
+
}
+
Log.d(Constants.TAG, " ------- in public key -------");
+
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
- Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(
+ publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " +
+ PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
}
+
*/
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
- ProviderHelper.saveKeyRing(mContext, publicKeyRing);
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
- updateProgress(R.string.progress_done, 100, 100);
}
/**
* Certify the given pubkeyid with the given masterkeyid.
*
- * @param masterKeyId Certifying key, must be available as secret key
- * @param pubKeyId ID of public key to certify
+ * @param certificationKey Certifying key
+ * @param publicKey public key to certify
* @param userIds User IDs to certify, must not be null or empty
* @param passphrase Passphrase of the secret key
* @return A keyring with added certifications
*/
- public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
- throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
- PGPException, SignatureException {
- if (passphrase == null) {
- throw new PgpGeneralException("Unable to obtain passphrase");
- } else {
+ public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey,
+ List<String> userIds, String passphrase)
+ throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
+ PGPException, SignatureException {
- // create a signatureGenerator from the supplied masterKeyId and passphrase
- PGPSignatureGenerator signatureGenerator; {
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator; {
- PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (certificationKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
+ if (certificationKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_signature_failed);
+ }
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
+ }
- // TODO: SHA256 fixed?
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
- }
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
+ }
- { // supply signatureGenerator with a SubpacketVector
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketVector packetVector = spGen.generate();
- signatureGenerator.setHashedSubpackets(packetVector);
- }
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
- // fetch public key ring, add the certification and return it
- PGPPublicKeyRing pubring = ProviderHelper
- .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
- for(String userId : new IterableIterator<String>(userIds.iterator())) {
- PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
- signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
- }
- pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
+ // fetch public key ring, add the certification and return it
+ for (String userId : new IterableIterator<String>(userIds.iterator())) {
+ PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
+ publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
+ }
+
+ return publicKey;
+ }
- return pubring;
+ /** Simple static subclass that stores two values.
+ *
+ * This is only used to return a pair of values in one function above. We specifically don't use
+ * com.android.Pair to keep this class free from android dependencies.
+ */
+ public static class Pair<K, V> {
+ public final K first;
+ public final V second;
+ public Pair(K first, V second) {
+ this.first = first;
+ this.second = second;
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 737e9c75d..a864a165d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -18,11 +18,28 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPCompressedDataGenerator;
+import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPLiteralDataGenerator;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -32,7 +49,11 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
@@ -50,7 +71,7 @@ public class PgpSignEncrypt {
private boolean mEnableAsciiArmorOutput;
private int mCompressionId;
private long[] mEncryptionKeyIds;
- private String mEncryptionPassphrase;
+ private String mSymmetricPassphrase;
private int mSymmetricEncryptionAlgorithm;
private long mSignatureKeyId;
private int mSignatureHashAlgorithm;
@@ -67,7 +88,7 @@ public class PgpSignEncrypt {
this.mEnableAsciiArmorOutput = builder.mEnableAsciiArmorOutput;
this.mCompressionId = builder.mCompressionId;
this.mEncryptionKeyIds = builder.mEncryptionKeyIds;
- this.mEncryptionPassphrase = builder.mEncryptionPassphrase;
+ this.mSymmetricPassphrase = builder.mSymmetricPassphrase;
this.mSymmetricEncryptionAlgorithm = builder.mSymmetricEncryptionAlgorithm;
this.mSignatureKeyId = builder.mSignatureKeyId;
this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
@@ -85,8 +106,8 @@ public class PgpSignEncrypt {
private ProgressDialogUpdater mProgress = null;
private boolean mEnableAsciiArmorOutput = false;
private int mCompressionId = Id.choice.compression.none;
- private long[] mEncryptionKeyIds = new long[0];
- private String mEncryptionPassphrase = null;
+ private long[] mEncryptionKeyIds = null;
+ private String mSymmetricPassphrase = null;
private int mSymmetricEncryptionAlgorithm = 0;
private long mSignatureKeyId = Id.key.none;
private int mSignatureHashAlgorithm = 0;
@@ -119,8 +140,8 @@ public class PgpSignEncrypt {
return this;
}
- public Builder encryptionPassphrase(String encryptionPassphrase) {
- this.mEncryptionPassphrase = encryptionPassphrase;
+ public Builder symmetricPassphrase(String symmetricPassphrase) {
+ this.mSymmetricPassphrase = symmetricPassphrase;
return this;
}
@@ -181,7 +202,8 @@ public class PgpSignEncrypt {
NoSuchAlgorithmException, SignatureException {
boolean enableSignature = mSignatureKeyId != Id.key.none;
- boolean enableEncryption = (mEncryptionKeyIds.length != 0 || mEncryptionPassphrase != null);
+ boolean enableEncryption = ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0)
+ || mSymmetricPassphrase != null);
boolean enableCompression = (enableEncryption && mCompressionId != Id.choice.compression.none);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
@@ -212,7 +234,7 @@ public class PgpSignEncrypt {
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
if (enableSignature) {
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ signingKeyRing = ProviderHelper.getPGPSecretKeyRingWithKeyId(mContext, mSignatureKeyId);
signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
@@ -246,12 +268,12 @@ public class PgpSignEncrypt {
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
- if (mEncryptionKeyIds.length == 0) {
+ if (mSymmetricPassphrase != null) {
// Symmetric encryption
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
- new JcePBEKeyEncryptionMethodGenerator(mEncryptionPassphrase.toCharArray());
+ new JcePBEKeyEncryptionMethodGenerator(mSymmetricPassphrase.toCharArray());
cPk.addMethod(symmetricEncryptionGenerator);
} else {
// Asymmetric encryption
@@ -284,7 +306,7 @@ public class PgpSignEncrypt {
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, signaturePrivateKey);
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ String userId = PgpKeyHelper.getMainUserId(signingKeyRing.getSecretKey());
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
@@ -442,7 +464,7 @@ public class PgpSignEncrypt {
}
PGPSecretKeyRing signingKeyRing =
- ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ ProviderHelper.getPGPSecretKeyRingWithKeyId(mContext, mSignatureKeyId);
PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
@@ -483,7 +505,7 @@ public class PgpSignEncrypt {
signatureGenerator.init(type, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ String userId = PgpKeyHelper.getMainUserId(signingKeyRing.getSecretKey());
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
index 54601173d..5bb1665b6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
@@ -18,7 +18,13 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.asn1.DERObjectIdentifier;
-import org.spongycastle.asn1.x509.*;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.BasicConstraints;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -29,13 +35,14 @@ import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.*;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
@@ -43,6 +50,11 @@ import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
public class PgpToX509 {
public static final String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
public static final String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
@@ -71,9 +83,10 @@ public class PgpToX509 {
* @throws Exception
* @author Bruno Harbulot
*/
- public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey,
- X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
- throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
+ public static X509Certificate createSelfSignedCert(
+ PublicKey pubKey, PrivateKey privKey, X509Name subject, Date startDate, Date endDate,
+ String subjAltNameURI)
+ throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
SignatureException, CertificateException, NoSuchProviderException {
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
@@ -170,10 +183,10 @@ public class PgpToX509 {
/**
* Creates a self-signed certificate from a PGP Secret Key.
*
- * @param pgpSecKey PGP Secret Key (from which one can extract the public and private keys and other
- * attributes).
- * @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks should be done
- * before calling this method)
+ * @param pgpSecKey PGP Secret Key (from which one can extract the public and private
+ * keys and other attributes).
+ * @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks
+ * should be done before calling this method)
* @param subjAltNameURI optional URI to embed in the subject alternative-name
* @return self-signed certificate
* @throws PGPException
@@ -184,9 +197,9 @@ public class PgpToX509 {
* @throws CertificateException
* @author Bruno Harbulot
*/
- public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey,
- PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
- NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
+ public static X509Certificate createSelfSignedCert(
+ PGPSecretKey pgpSecKey, PGPPrivateKey pgpPrivKey, String subjAltNameURI)
+ throws PGPException, NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
SignatureException, CertificateException {
// get public key from secret key
PGPPublicKey pgpPubKey = pgpSecKey.getPublicKey();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index bb80d27ee..418445367 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -23,4 +23,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message) {
super(message);
}
+ public PgpGeneralException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
new file mode 100644
index 000000000..caa7842db
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 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.sufficientlysecure.keychain.pgp.exception;
+
+import android.content.Context;
+
+public class PgpGeneralMsgIdException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ private final int mMessageId;
+
+ public PgpGeneralMsgIdException(int messageId) {
+ super("msg[" + messageId + "]");
+ mMessageId = messageId;
+ }
+
+ public PgpGeneralException getContextualized(Context context) {
+ return new PgpGeneralException(context.getString(mMessageId), this);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 404c128a1..2b40300d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -19,46 +19,47 @@ package org.sufficientlysecure.keychain.provider;
import android.net.Uri;
import android.provider.BaseColumns;
+
import org.sufficientlysecure.keychain.Constants;
public class KeychainContract {
interface KeyRingsColumns {
String MASTER_KEY_ID = "master_key_id"; // not a database id
- String TYPE = "type"; // see KeyTypes
String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob
}
interface KeysColumns {
+ String MASTER_KEY_ID = "master_key_id"; // not a database id
+ String RANK = "rank";
+
String KEY_ID = "key_id"; // not a database id
- String TYPE = "type"; // see KeyTypes
- String IS_MASTER_KEY = "is_master_key";
String ALGORITHM = "algorithm";
+ String FINGERPRINT = "fingerprint";
+
String KEY_SIZE = "key_size";
- String CAN_CERTIFY = "can_certify";
String CAN_SIGN = "can_sign";
String CAN_ENCRYPT = "can_encrypt";
+ String CAN_CERTIFY = "can_certify";
String IS_REVOKED = "is_revoked";
+
String CREATION = "creation";
String EXPIRY = "expiry";
- String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
- String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob
- String RANK = "rank";
- String FINGERPRINT = "fingerprint";
}
interface UserIdsColumns {
- String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
+ String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String USER_ID = "user_id"; // not a database id
- String RANK = "rank";
+ String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
+ String IS_PRIMARY = "is_primary";
}
interface CertsColumns {
- String KEY_RING_ROW_ID = "key_ring_row_id"; // verified id, foreign key to key_rings._ID
+ String MASTER_KEY_ID = "master_key_id"; // verified id, foreign key to key_rings._ID
String RANK = "rank"; // rank of verified key
- String KEY_ID = "key_id"; // verified id, not a database id
String KEY_ID_CERTIFIER = "key_id_certifier"; // verifying id, not a database id
String CREATION = "creation";
+ String EXPIRY = "expiry";
String VERIFIED = "verified";
String KEY_DATA = "key_data"; // certification blob
}
@@ -66,10 +67,15 @@ public class KeychainContract {
interface ApiAppsColumns {
String PACKAGE_NAME = "package_name";
String PACKAGE_SIGNATURE = "package_signature";
+ }
+
+ interface ApiAppsAccountsColumns {
+ String ACCOUNT_NAME = "account_name";
String KEY_ID = "key_id"; // not a database id
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
String HASH_ALORITHM = "hash_algorithm";
String COMPRESSION = "compression";
+ String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
}
public static final class KeyTypes {
@@ -85,97 +91,81 @@ public class KeychainContract {
public static final String BASE_KEY_RINGS = "key_rings";
public static final String BASE_DATA = "data";
- public static final String PATH_PUBLIC = "public";
- public static final String PATH_SECRET = "secret";
+ public static final String PATH_UNIFIED = "unified";
- public static final String PATH_BY_MASTER_KEY_ID = "master_key_id";
- public static final String PATH_BY_KEY_ID = "key_id";
- public static final String PATH_BY_KEY_ROW_ID = "key_row_id";
- public static final String PATH_BY_CERTIFIER_ID = "certifier_id";
- public static final String PATH_BY_EMAILS = "emails";
- public static final String PATH_BY_LIKE_EMAIL = "like_email";
+ public static final String PATH_FIND = "find";
+ public static final String PATH_BY_EMAIL = "email";
+ public static final String PATH_BY_SUBKEY = "subkey";
+ public static final String PATH_PUBLIC = "public";
+ public static final String PATH_SECRET = "secret";
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_KEYS = "keys";
+ public static final String PATH_CERTS = "certs";
public static final String BASE_API_APPS = "api_apps";
- public static final String PATH_BY_PACKAGE_NAME = "package_name";
+ public static final String PATH_ACCOUNTS = "accounts";
- public static final String BASE_CERTS = "certs";
+ public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns {
+ public static final String MASTER_KEY_ID = "master_key_id";
+ public static final String HAS_SECRET = "has_secret";
- public static class KeyRings implements KeyRingsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- /**
- * Use if multiple items get returned
- */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
-
- /**
- * Use if a single item is returned
- */
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring";
public static Uri buildUnifiedKeyRingsUri() {
- return CONTENT_URI;
+ return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
- public static Uri buildPublicKeyRingsUri() {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
+ public static Uri buildGenericKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
-
- public static Uri buildPublicKeyRingsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build();
+ public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
}
-
- public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC)
- .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build();
+ public static Uri buildUnifiedKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
}
- public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID)
- .appendPath(keyId).build();
+ public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
}
-
- public static Uri buildPublicKeyRingsByEmailsUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS)
- .appendPath(emails).build();
+ public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
}
- public static Uri buildPublicKeyRingsByLikeEmailUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_LIKE_EMAIL)
- .appendPath(emails).build();
- }
+ }
- public static Uri buildSecretKeyRingsUri() {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build();
- }
+ public static class KeyRingData implements KeyRingsColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_KEY_RINGS).build();
- public static Uri buildSecretKeyRingsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build();
- }
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring_data";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring_data";
- public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET)
- .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build();
+ public static Uri buildPublicKeyRingUri() {
+ return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
-
- public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID)
- .appendPath(keyId).build();
+ public static Uri buildPublicKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_PUBLIC).build();
}
-
- public static Uri buildSecretKeyRingsByEmailsUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)
- .appendPath(emails).build();
+ public static Uri buildPublicKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_PUBLIC).build();
}
- public static Uri buildSecretKeyRingsByLikeEmails(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_LIKE_EMAIL)
- .appendPath(emails).build();
+ public static Uri buildSecretKeyRingUri() {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build();
+ }
+ public static Uri buildSecretKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_SECRET).build();
+ }
+ public static Uri buildSecretKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_SECRET).build();
}
+
}
public static class Keys implements KeysColumns, BaseColumns {
@@ -185,131 +175,101 @@ public class KeychainContract {
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key";
-
- public static Uri buildPublicKeysUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).build();
- }
-
- public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).appendPath(keyRowId).build();
- }
-
- public static Uri buildSecretKeysUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).build();
- }
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key";
- public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).appendPath(keyRowId).build();
+ public static Uri buildKeysUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_KEYS).build();
}
-
- public static Uri buildKeysUri(Uri keyRingUri) {
- return keyRingUri.buildUpon().appendPath(PATH_KEYS).build();
+ public static Uri buildKeysUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_KEYS).build();
}
- public static Uri buildKeysUri(Uri keyRingUri, String keyRowId) {
- return keyRingUri.buildUpon().appendPath(PATH_KEYS).appendPath(keyRowId).build();
- }
}
public static class UserIds implements UserIdsColumns, BaseColumns {
+ public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.user_id";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.user_id";
- public static Uri buildPublicUserIdsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).build();
+ public static Uri buildUserIdsUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_USER_IDS).build();
}
-
- public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ public static Uri buildUserIdsUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build();
}
+ }
- public static Uri buildSecretUserIdsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).build();
- }
+ public static class ApiApps implements ApiAppsColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_API_APPS).build();
- public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
- }
+ /**
+ * Use if multiple items get returned
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_apps";
- public static Uri buildUserIdsUri(Uri keyRingUri) {
- return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).build();
- }
+ /**
+ * Use if a single item is returned
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app";
- public static Uri buildUserIdsUri(Uri keyRingUri, String userIdRowId) {
- return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ public static Uri buildByPackageNameUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
}
}
- public static class ApiApps implements ApiAppsColumns, BaseColumns {
+ public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_API_APPS).build();
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_app.accounts";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app.account";
- public static Uri buildIdUri(String rowId) {
- return CONTENT_URI.buildUpon().appendPath(rowId).build();
+ public static Uri buildBaseUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
+ .build();
}
- public static Uri buildByPackageNameUri(String packageName) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName)
- .build();
+ public static Uri buildByPackageAndAccountUri(String packageName, String accountName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
+ .appendEncodedPath(accountName).build();
}
}
public static class Certs implements CertsColumns, BaseColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_CERTS).build();
+ public static final String USER_ID = UserIdsColumns.USER_ID;
+ public static final String SIGNER_UID = "signer_user_id";
- // do we even need this one...? just using it as default for database insert notifications~
- public static Uri buildCertsUri(String rowId) {
- return CONTENT_URI.buildUpon().appendPath(rowId).build();
- }
-
- public static Uri buildCertsByKeyRowIdUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ROW_ID)
- .appendPath(keyRingRowId).build();
- }
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_KEY_RINGS).build();
- public static Uri buildCertsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(keyId)
- .build();
+ public static Uri buildCertsUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).build();
}
-
- public static Uri buildCertsByCertifierKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_CERTIFIER_ID).appendPath(keyId)
- .build();
+ public static Uri buildCertsUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_CERTS).build();
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index cc6a1f1e1..fda1783cb 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -18,97 +18,156 @@
package org.sufficientlysecure.keychain.provider;
import android.content.Context;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
+
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.IOException;
+
public class KeychainDatabase extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "apg.db";
- private static final int DATABASE_VERSION = 8;
+ private static final String DATABASE_NAME = "openkeychain.db";
+ private static final int DATABASE_VERSION = 1;
+ static Boolean apg_hack = false;
public interface Tables {
- String KEY_RINGS = "key_rings";
+ String KEY_RINGS_PUBLIC = "keyrings_public";
+ String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys";
String USER_IDS = "user_ids";
- String API_APPS = "api_apps";
String CERTS = "certs";
+ String API_APPS = "api_apps";
+ String API_ACCOUNTS = "api_accounts";
}
- private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + KeyRingsColumns.MASTER_KEY_ID + " INT64, "
- + KeyRingsColumns.TYPE + " INTEGER, "
- + KeyRingsColumns.KEY_RING_DATA + " BLOB)";
-
- private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
- + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + KeysColumns.KEY_ID + " INT64, "
- + KeysColumns.TYPE + " INTEGER, "
- + KeysColumns.IS_MASTER_KEY + " INTEGER, "
- + KeysColumns.ALGORITHM + " INTEGER, "
- + KeysColumns.KEY_SIZE + " INTEGER, "
- + KeysColumns.CAN_CERTIFY + " INTEGER, "
- + KeysColumns.CAN_SIGN + " INTEGER, "
- + KeysColumns.CAN_ENCRYPT + " INTEGER, "
- + KeysColumns.IS_REVOKED + " INTEGER, "
- + KeysColumns.CREATION + " INTEGER, "
- + KeysColumns.EXPIRY + " INTEGER, "
- + KeysColumns.KEY_DATA + " BLOB,"
- + KeysColumns.RANK + " INTEGER, "
- + KeysColumns.FINGERPRINT + " BLOB, "
- + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
-
- private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + UserIdsColumns.USER_ID + " TEXT, "
- + UserIdsColumns.RANK + " INTEGER, "
- + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
+ private static final String CREATE_KEYRINGS_PUBLIC =
+ "CREATE TABLE IF NOT EXISTS keyrings_public ("
+ + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY,"
+ + KeyRingsColumns.KEY_RING_DATA + " BLOB"
+ + ")";
+
+ private static final String CREATE_KEYRINGS_SECRET =
+ "CREATE TABLE IF NOT EXISTS keyrings_secret ("
+ + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY,"
+ + KeyRingsColumns.KEY_RING_DATA + " BLOB,"
+ + "FOREIGN KEY(" + KeyRingsColumns.MASTER_KEY_ID + ") "
+ + "REFERENCES keyrings_public(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_KEYS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
+ + KeysColumns.MASTER_KEY_ID + " INTEGER, "
+ + KeysColumns.RANK + " INTEGER, "
+
+ + KeysColumns.KEY_ID + " INTEGER, "
+ + KeysColumns.KEY_SIZE + " INTEGER, "
+ + KeysColumns.ALGORITHM + " INTEGER, "
+ + KeysColumns.FINGERPRINT + " BLOB, "
+
+ + KeysColumns.CAN_CERTIFY + " BOOLEAN, "
+ + KeysColumns.CAN_SIGN + " BOOLEAN, "
+ + KeysColumns.CAN_ENCRYPT + " BOOLEAN, "
+ + KeysColumns.IS_REVOKED + " BOOLEAN, "
+
+ + KeysColumns.CREATION + " INTEGER, "
+ + KeysColumns.EXPIRY + " INTEGER, "
+
+ + "PRIMARY KEY(" + KeysColumns.MASTER_KEY_ID + ", " + KeysColumns.RANK + "),"
+ + "FOREIGN KEY(" + KeysColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_USER_IDS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "("
+ + UserIdsColumns.MASTER_KEY_ID + " INTEGER, "
+ + UserIdsColumns.USER_ID + " CHARMANDER, "
+
+ + UserIdsColumns.IS_PRIMARY + " BOOLEAN, "
+ + UserIdsColumns.RANK+ " INTEGER, "
+
+ + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), "
+ + "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), "
+ + "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_CERTS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.CERTS + "("
+ + CertsColumns.MASTER_KEY_ID + " INTEGER,"
+ + CertsColumns.RANK + " INTEGER, " // rank of certified uid
+
+ + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key
+ + CertsColumns.CREATION + " INTEGER, "
+ + CertsColumns.EXPIRY + " INTEGER, "
+ + CertsColumns.VERIFIED + " INTEGER, "
+
+ + CertsColumns.KEY_DATA + " BLOB,"
+ + "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", "
+ + CertsColumns.KEY_ID_CERTIFIER + "), "
+ + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE,"
+ + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES "
+ + Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE"
+ + ")";
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
- + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
- + ApiAppsColumns.KEY_ID + " INT64, "
- + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
- + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
- + ApiAppsColumns.COMPRESSION + " INTEGER)";
-
- private static final String CREATE_CERTS = "CREATE TABLE IF NOT EXISTS " + Tables.CERTS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + CertsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL "
- + " REFERENCES " + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE, "
- + CertsColumns.KEY_ID + " INTEGER, " // certified key
- + CertsColumns.RANK + " INTEGER, " // key rank of certified uid
- + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key
- + CertsColumns.CREATION + " INTEGER, "
- + CertsColumns.VERIFIED + " INTEGER, "
- + CertsColumns.KEY_DATA + " BLOB)";
+ + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, "
+ + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)";
+ private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS
+ + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, "
+ + ApiAppsAccountsColumns.KEY_ID + " INT64, "
+ + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, "
+ + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + "), "
+ + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES "
+ + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)";
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
+
+ // make sure this is only done once, on the first instance!
+ boolean iAmIt = false;
+ synchronized(apg_hack) {
+ if(!apg_hack) {
+ iAmIt = true;
+ apg_hack = true;
+ }
+ }
+ // if it's us, do the import
+ if(iAmIt)
+ checkAndImportApg(context);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.w(Constants.TAG, "Creating database...");
- db.execSQL(CREATE_KEY_RINGS);
+ db.execSQL(CREATE_KEYRINGS_PUBLIC);
+ db.execSQL(CREATE_KEYRINGS_SECRET);
db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_IDS);
- db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_CERTS);
+ db.execSQL(CREATE_API_APPS);
+ db.execSQL(CREATE_API_APPS_ACCOUNTS);
}
@Override
@@ -117,47 +176,100 @@ public class KeychainDatabase extends SQLiteOpenHelper {
if (!db.isReadOnly()) {
// Enable foreign key constraints
db.execSQL("PRAGMA foreign_keys=ON;");
+ // TODO remove, once we remove the "always migrate" debug stuff
+ // db.execSQL("DROP TABLE certs;");
+ // db.execSQL("DROP TABLE user_ids;");
+ db.execSQL(CREATE_USER_IDS);
+ db.execSQL(CREATE_CERTS);
}
}
@Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
-
- // Upgrade from oldVersion through all cases to newest one
- for (int version = oldVersion; version < newVersion; ++version) {
- Log.w(Constants.TAG, "Upgrading database to version " + version);
-
- switch (version) {
- case 3:
- db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY
- + " INTEGER DEFAULT 0;");
- db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY
- + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
- break;
- case 4:
- db.execSQL(CREATE_API_APPS);
- break;
- case 5:
- // new column: package_signature
- db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
- db.execSQL(CREATE_API_APPS);
- break;
- case 6:
- // new column: fingerprint
- db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
- + " BLOB;");
- break;
- case 7:
- // new table: certs
- db.execSQL(CREATE_CERTS);
+ public void onUpgrade(SQLiteDatabase db, int old, int nu) {
+ // don't care (this is version 1)
+ }
+ /** This method tries to import data from a provided database.
+ *
+ * The sole assumptions made on this db are that there is a key_rings table
+ * with a key_ring_data and a type column, the latter of which should be bigger
+ * for secret keys.
+ */
+ public void checkAndImportApg(Context context) {
+
+ boolean hasApgDb = false; {
+ // It's the Java way =(
+ String[] dbs = context.databaseList();
+ for(String db : dbs) {
+ if(db.equals("apg.db")) {
+ hasApgDb = true;
break;
- default:
- break;
+ }
+ }
+ }
+
+ if(!hasApgDb)
+ return;
+ Log.d(Constants.TAG, "apg.db exists! Importing...");
+
+ SQLiteDatabase db = new SQLiteOpenHelper(context, "apg.db", null, 1) {
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // should never happen
+ assert false;
+ }
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int old, int nu) {
+ // don't care
+ }
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int old, int nu) {
+ // don't care either
}
+ }.getReadableDatabase();
+
+ // kill current!
+ { // TODO don't kill current.
+ Log.d(Constants.TAG, "Truncating db...");
+ SQLiteDatabase d = getWritableDatabase();
+ d.execSQL("DELETE FROM keyrings_public");
+ d.close();
+ Log.d(Constants.TAG, "Ok.");
}
+
+ Cursor c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY type ASC", null);
+ try {
+ // import from old database
+ Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db...");
+ for(int i = 0; i < c.getCount(); i++) {
+ c.moveToPosition(i);
+ byte[] data = c.getBlob(0);
+ PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
+ if(ring instanceof PGPPublicKeyRing)
+ ProviderHelper.saveKeyRing(context, (PGPPublicKeyRing) ring);
+ else if(ring instanceof PGPSecretKeyRing)
+ ProviderHelper.saveKeyRing(context, (PGPSecretKeyRing) ring);
+ else {
+ Log.e(Constants.TAG, "Unknown blob data type!");
+ }
+ }
+
+ } catch(IOException e) {
+ Log.e(Constants.TAG, "Error importing apg db!", e);
+ return;
+ } finally {
+ if(c != null)
+ c.close();
+ if(db != null)
+ db.close();
+ }
+
+ // TODO delete old db, if we are sure this works
+ // context.deleteDatabase("apg.db");
+ Log.d(Constants.TAG, "All done, (not) deleting apg.db");
+
+
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index f34423a71..83b3dd744 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,10 +26,16 @@ import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
-import android.provider.BaseColumns;
import android.text.TextUtils;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.KeychainContract.*;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -37,54 +43,29 @@ import java.util.Arrays;
import java.util.HashMap;
public class KeychainProvider extends ContentProvider {
- // public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME
- // + ".action.DATABASE_CHANGE";
- //
- // public static final String EXTRA_BROADCAST_KEY_TYPE = "key_type";
- // public static final String EXTRA_BROADCAST_CONTENT_ITEM_TYPE = "contentItemType";
-
- private static final int PUBLIC_KEY_RING = 101;
- private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102;
- private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103;
- private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104;
- private static final int PUBLIC_KEY_RING_BY_EMAILS = 105;
- private static final int PUBLIC_KEY_RING_BY_LIKE_EMAIL = 106;
-
- private static final int PUBLIC_KEY_RING_KEY = 111;
- private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112;
-
- private static final int PUBLIC_KEY_RING_USER_ID = 121;
- private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122;
- private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123;
-
- private static final int SECRET_KEY_RING = 201;
- private static final int SECRET_KEY_RING_BY_ROW_ID = 202;
- private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203;
- private static final int SECRET_KEY_RING_BY_KEY_ID = 204;
- private static final int SECRET_KEY_RING_BY_EMAILS = 205;
- private static final int SECRET_KEY_RING_BY_LIKE_EMAIL = 206;
-
- private static final int SECRET_KEY_RING_KEY = 211;
- private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212;
-
- private static final int SECRET_KEY_RING_USER_ID = 221;
- private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
+
+ private static final int KEY_RINGS_UNIFIED = 101;
+ private static final int KEY_RINGS_PUBLIC = 102;
+ private static final int KEY_RINGS_SECRET = 103;
+
+ private static final int KEY_RING_UNIFIED = 200;
+ private static final int KEY_RING_KEYS = 201;
+ private static final int KEY_RING_USER_IDS = 202;
+ private static final int KEY_RING_PUBLIC = 203;
+ private static final int KEY_RING_SECRET = 204;
+ private static final int KEY_RING_CERTS = 205;
private static final int API_APPS = 301;
- private static final int API_APPS_BY_ROW_ID = 302;
private static final int API_APPS_BY_PACKAGE_NAME = 303;
+ private static final int API_ACCOUNTS = 304;
+ private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
- private static final int UNIFIED_KEY_RING = 401;
+ private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
+ private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
- private static final int CERTS = 401;
- private static final int CERTS_BY_KEY_ID = 402;
- private static final int CERTS_BY_ROW_ID = 403;
- private static final int CERTS_BY_KEY_ROW_ID = 404;
- private static final int CERTS_BY_KEY_ROW_ID_ALL = 405;
- private static final int CERTS_BY_CERTIFIER_ID = 406;
- private static final int CERTS_BY_KEY_ROW_ID_HAS_SECRET = 407;
+ private static final int CERTS_FIND_BY_CERTIFIER_ID = 501;
- // private static final int DATA_STREAM = 401;
+ // private static final int DATA_STREAM = 501;
protected UriMatcher mUriMatcher;
@@ -98,171 +79,85 @@ public class KeychainProvider extends ContentProvider {
String authority = KeychainContract.CONTENT_AUTHORITY;
/**
- * unified key rings
- *
- * <pre>
- * key_rings
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING);
-
- /**
- * public key rings
+ * list key_rings
*
* <pre>
+ * key_rings/unified
* key_rings/public
- * key_rings/public/#
- * key_rings/public/master_key_id/_
- * key_rings/public/key_id/_
- * key_rings/public/emails/_
- * key_rings/public/like_email/_
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC, PUBLIC_KEY_RING);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#", PUBLIC_KEY_RING_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID
- + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_KEY_ID + "/*",
- PUBLIC_KEY_RING_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS + "/*",
- PUBLIC_KEY_RING_BY_EMAILS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS,
- PUBLIC_KEY_RING_BY_EMAILS); // without emails specified
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*",
- PUBLIC_KEY_RING_BY_LIKE_EMAIL);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_UNIFIED,
+ KEY_RINGS_UNIFIED);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_PUBLIC,
+ KEY_RINGS_PUBLIC);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_SECRET,
+ KEY_RINGS_SECRET);
/**
- * public keys
+ * find by criteria other than master key id
*
- * <pre>
- * key_rings/public/#/keys
- * key_rings/public/#/keys/#
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS,
- PUBLIC_KEY_RING_KEY);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS + "/#",
- PUBLIC_KEY_RING_KEY_BY_ROW_ID);
-
- /**
- * public user ids
+ * key_rings/find/email/_
+ * key_rings/find/subkey/_
*
- * <pre>
- * key_rings/public/#/user_ids
- * key_rings/public/#/user_ids/#
- * key_rings/public/master_key_id/#/user_ids
- * </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS,
- PUBLIC_KEY_RING_USER_ID);
+ + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_EMAIL + "/*",
+ KEY_RINGS_FIND_BY_EMAIL);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
- PUBLIC_KEY_RING_USER_ID_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/"
- + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS,
- PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID);
+ + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*",
+ KEY_RINGS_FIND_BY_SUBKEY);
/**
- * secret key rings
+ * list key_ring specifics
*
* <pre>
- * key_rings/secret
- * key_rings/secret/#
- * key_rings/secret/master_key_id/_
- * key_rings/secret/key_id/_
- * key_rings/secret/emails/_
- * key_rings/secret/like_email/_
+ * key_rings/_/unified
+ * key_rings/_/keys
+ * key_rings/_/user_ids
+ * key_rings/_/public
+ * key_rings/_/secret
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET, SECRET_KEY_RING);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#", SECRET_KEY_RING_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID
- + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_KEY_ID + "/*",
- SECRET_KEY_RING_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS + "/*",
- SECRET_KEY_RING_BY_EMAILS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS,
- SECRET_KEY_RING_BY_EMAILS); // without emails specified
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*",
- SECRET_KEY_RING_BY_LIKE_EMAIL);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_UNIFIED,
+ KEY_RING_UNIFIED);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_KEYS,
+ KEY_RING_KEYS);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_USER_IDS,
+ KEY_RING_USER_IDS);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_PUBLIC,
+ KEY_RING_PUBLIC);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_SECRET,
+ KEY_RING_SECRET);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_CERTS,
+ KEY_RING_CERTS);
/**
- * secret keys
+ * API apps
*
* <pre>
- * key_rings/secret/#/keys
- * key_rings/secret/#/keys/#
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS,
- SECRET_KEY_RING_KEY);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS + "/#",
- SECRET_KEY_RING_KEY_BY_ROW_ID);
-
- /**
- * secret user ids
+ * api_apps
+ * api_apps/_ (package name)
*
- * <pre>
- * key_rings/secret/#/user_ids
- * key_rings/secret/#/user_ids/#
+ * api_apps/_/accounts
+ * api_apps/_/accounts/_ (account name)
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS,
- SECRET_KEY_RING_USER_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
- SECRET_KEY_RING_USER_ID_BY_ROW_ID);
-
- /**
- * API apps
- */
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
- + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
- /**
- /**
- * certifications
- *
- * <pre>
- * </pre>
- *
- */
- matcher.addURI(authority, KeychainContract.BASE_CERTS, CERTS);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/has_secret", CERTS_BY_KEY_ROW_ID_HAS_SECRET);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_CERTIFIER_ID + "/#", CERTS_BY_CERTIFIER_ID);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
/**
* data stream
@@ -276,7 +171,7 @@ public class KeychainProvider extends ContentProvider {
return matcher;
}
- private KeychainDatabase mApgDatabase;
+ private KeychainDatabase mKeychainDatabase;
/**
* {@inheritDoc}
@@ -284,10 +179,15 @@ public class KeychainProvider extends ContentProvider {
@Override
public boolean onCreate() {
mUriMatcher = buildUriMatcher();
- mApgDatabase = new KeychainDatabase(getContext());
return true;
}
+ public KeychainDatabase getDb() {
+ if(mKeychainDatabase == null)
+ mKeychainDatabase = new KeychainDatabase(getContext());
+ return mKeychainDatabase;
+ }
+
/**
* {@inheritDoc}
*/
@@ -295,247 +195,44 @@ public class KeychainProvider extends ContentProvider {
public String getType(Uri uri) {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- return KeyRings.CONTENT_TYPE;
-
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
+ case KEY_RING_PUBLIC:
return KeyRings.CONTENT_ITEM_TYPE;
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
+ case KEY_RING_KEYS:
return Keys.CONTENT_TYPE;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- return Keys.CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case SECRET_KEY_RING_USER_ID:
+ case KEY_RING_USER_IDS:
return UserIds.CONTENT_TYPE;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- return UserIds.CONTENT_ITEM_TYPE;
+ case KEY_RING_SECRET:
+ return KeyRings.CONTENT_ITEM_TYPE;
case API_APPS:
return ApiApps.CONTENT_TYPE;
- case API_APPS_BY_ROW_ID:
case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE;
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
- }
+ case API_ACCOUNTS:
+ return ApiAccounts.CONTENT_TYPE;
- /**
- * Returns type of the query (secret/public)
- *
- * @param match
- * @return
- */
- private int getKeyType(int match) {
- int type;
- switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_KEY:
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.PUBLIC;
- break;
-
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.SECRET;
- break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ return ApiAccounts.CONTENT_ITEM_TYPE;
default:
- Log.e(Constants.TAG, "Unknown match " + match);
- type = -1;
- break;
- }
-
- return type;
- }
-
- /**
- * Set result of query to specific columns, don't show blob column
- *
- * @return
- */
- private HashMap<String, String> getProjectionMapForKeyRings() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
- projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
- + KeyRingsColumns.KEY_RING_DATA);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
- + KeyRingsColumns.MASTER_KEY_ID);
- // TODO: deprecated master key id
- //projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
-
- projectionMap.put(KeysColumns.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM);
- projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE);
- projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION);
- projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY);
- projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_ID);
- projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
- projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED);
-
- projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
-
- // type attribute is special: if there is any grouping, choose secret over public type
- projectionMap.put(KeyRingsColumns.TYPE,
- "MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE);
-
- return projectionMap;
- }
-
- /**
- * Set result of query to specific columns, don't show blob column
- *
- * @return
- */
- private HashMap<String, String> getProjectionMapForKeys() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, BaseColumns._ID);
- projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID);
- projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY);
- projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM);
- projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE);
- projectionMap.put(KeysColumns.CAN_CERTIFY, KeysColumns.CAN_CERTIFY);
- projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN);
- projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT);
- projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED);
- projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION);
- projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY);
- projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID);
- projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA);
- projectionMap.put(KeysColumns.RANK, KeysColumns.RANK);
- projectionMap.put(KeysColumns.FINGERPRINT, KeysColumns.FINGERPRINT);
-
- return projectionMap;
- }
-
- private HashMap<String, String> getProjectionMapForUserIds() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID);
- projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
- projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
- + KeyRingsColumns.MASTER_KEY_ID);
-
- // this is the count of known secret keys who certified this uid
- projectionMap.put("verified", "COUNT(" + Tables.KEYS + "." + Keys._ID + ") AS verified");
-
- return projectionMap;
- }
- private HashMap<String, String> getProjectionMapForCerts() {
-
- HashMap<String, String> pmap = new HashMap<String, String>();
- pmap.put(Certs._ID, Tables.CERTS + "." + Certs._ID);
- pmap.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID);
- pmap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
- pmap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
- pmap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
- pmap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
- pmap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
- // verified key data
- pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- // verifying key data
- pmap.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid");
-
- return pmap;
- }
-
- /**
- * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
- */
- private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
- if (match != UNIFIED_KEY_RING) {
- // public or secret keyring
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
-
- // join keyrings with keys and userIds
- // Only get user id and key with rank 0 (main user id and main key)
- qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
- + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "."
- + KeysColumns.RANK + " = '0') " + " INNER JOIN " + Tables.USER_IDS + " ON "
- + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
- + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
- + UserIdsColumns.RANK + " = '0')");
-
- qb.setProjectionMap(getProjectionMapForKeyRings());
-
- return qb;
- }
-
- /**
- * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
- * <p/>
- * Here only one key should be selected in the query to return a single keyring!
- */
- private SQLiteQueryBuilder buildKeyRingQueryWithSpecificKey(SQLiteQueryBuilder qb, int match) {
- // public or secret keyring
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
-
- // join keyrings with keys and userIds to every keyring
- qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
- + KeysColumns.KEY_RING_ROW_ID + ") " + " INNER JOIN " + Tables.USER_IDS + " ON "
- + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
- + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
- + UserIdsColumns.RANK + " = '0')");
-
- qb.setProjectionMap(getProjectionMapForKeyRings());
-
- return qb;
}
/**
* {@inheritDoc}
*/
- @SuppressWarnings("deprecation")
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- SQLiteDatabase db = mApgDatabase.getReadableDatabase();
int match = mUriMatcher.match(uri);
@@ -543,227 +240,224 @@ public class KeychainProvider extends ContentProvider {
String groupBy = null, having = null;
switch (match) {
- case UNIFIED_KEY_RING:
- qb = buildKeyRingQuery(qb, match);
-
- // GROUP BY so we don't get duplicates
- groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID;
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = KeyRings.TYPE + " DESC, " +
- Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ case KEY_RING_UNIFIED:
+ case KEY_RINGS_UNIFIED:
+ case KEY_RINGS_FIND_BY_EMAIL:
+ case KEY_RINGS_FIND_BY_SUBKEY: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
+ projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
+ projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID);
+ projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(KeyRings.IS_REVOKED, Keys.IS_REVOKED);
+ projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY);
+ projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(KeyRings.CREATION, Keys.CREATION);
+ projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY);
+ projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM);
+ projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
+ projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
+ projectionMap.put(KeyRings.HAS_SECRET, "(" + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL) AS " + KeyRings.HAS_SECRET);
+ qb.setProjectionMap(projectionMap);
+
+ qb.setTables(
+ Tables.KEYS
+ + " INNER JOIN " + Tables.USER_IDS + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0"
+ + ") LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID
+ + ")"
+ );
+ qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
+
+ switch(match) {
+ case KEY_RING_UNIFIED: {
+ qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ break;
+ }
+ case KEY_RINGS_FIND_BY_SUBKEY: {
+ try {
+ String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
+ qb.appendWhere(" AND EXISTS ("
+ + " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
+ + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ + ")");
+ } catch(NumberFormatException e) {
+ Log.e(Constants.TAG, "Malformed find by subkey query!", e);
+ qb.appendWhere(" AND 0");
+ }
+ break;
+ }
+ case KEY_RINGS_FIND_BY_EMAIL: {
+ String chunks[] = uri.getLastPathSegment().split(" *, *");
+ boolean gotCondition = false;
+ String emailWhere = "";
+ // JAVA ♥
+ for (int i = 0; i < chunks.length; ++i) {
+ if (chunks[i].length() == 0) {
+ continue;
+ }
+ if (i != 0) {
+ emailWhere += " OR ";
+ }
+ emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
+ // match '*<email>', so it has to be at the *end* of the user id
+ emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
+ gotCondition = true;
+ }
+ if(gotCondition) {
+ qb.appendWhere(" AND EXISTS ("
+ + " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp"
+ + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND (" + emailWhere + ")"
+ + ")");
+ } else {
+ // TODO better way to do this?
+ Log.e(Constants.TAG, "Malformed find by email query!");
+ qb.appendWhere(" AND 0");
+ }
+ break;
+ }
}
- break;
-
- case PUBLIC_KEY_RING:
- case SECRET_KEY_RING:
- qb = buildKeyRingQuery(qb, match);
-
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ sortOrder =
+ Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NULL ASC, "
+ + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
}
- break;
-
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- qb = buildKeyRingQuery(qb, match);
-
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ // uri to watch is all /key_rings/
+ uri = KeyRings.CONTENT_URI;
break;
+ }
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- qb = buildKeyRingQuery(qb, match);
-
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ case KEY_RING_KEYS: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(Keys._ID, Tables.KEYS + ".oid AS _id");
+ projectionMap.put(Keys.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
+ projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK);
+ projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);
+ projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
+ projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);
+ projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ projectionMap.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(Keys.CREATION, Keys.CREATION);
+ projectionMap.put(Keys.EXPIRY, Keys.EXPIRY);
+ projectionMap.put(Keys.ALGORITHM, Keys.ALGORITHM);
+ projectionMap.put(Keys.FINGERPRINT, Keys.FINGERPRINT);
+ qb.setProjectionMap(projectionMap);
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ qb.setTables(Tables.KEYS);
+ qb.appendWhere(Keys.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
+ }
- case SECRET_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- qb = buildKeyRingQueryWithSpecificKey(qb, match);
+ case KEY_RING_USER_IDS: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id");
+ projectionMap.put(UserIds.MASTER_KEY_ID, UserIds.MASTER_KEY_ID);
+ projectionMap.put(UserIds.USER_ID, UserIds.USER_ID);
+ projectionMap.put(UserIds.RANK, UserIds.RANK);
+ projectionMap.put(UserIds.IS_PRIMARY, UserIds.IS_PRIMARY);
+ projectionMap.put(UserIds.VERIFIED, "0 AS " + UserIds.VERIFIED);
+ qb.setProjectionMap(projectionMap);
- qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.setTables(Tables.USER_IDS);
+ qb.appendWhere(UserIds.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ sortOrder = UserIds.RANK + " ASC";
}
break;
- case SECRET_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_EMAILS:
- qb = buildKeyRingQuery(qb, match);
-
- String emails = uri.getLastPathSegment();
- String chunks[] = emails.split(" *, *");
- boolean gotCondition = false;
- String emailWhere = "";
- for (int i = 0; i < chunks.length; ++i) {
- if (chunks[i].length() == 0) {
- continue;
- }
- if (i != 0) {
- emailWhere += " OR ";
- }
- emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
- // match '*<email>', so it has to be at the *end* of the user id
- emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
- gotCondition = true;
- }
-
- if (gotCondition) {
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere
- + "))");
- }
-
- break;
-
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- qb = buildKeyRingQuery(qb, match);
-
- String likeEmail = uri.getLastPathSegment();
-
- String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE "
- + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
-
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
- + "))");
-
- break;
+ }
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ case KEY_RINGS_PUBLIC:
+ case KEY_RING_PUBLIC: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_PUBLIC + ".oid AS _id");
+ projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
+ projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
+ qb.setProjectionMap(projectionMap);
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ qb.setTables(Tables.KEY_RINGS_PUBLIC);
- qb.setProjectionMap(getProjectionMapForKeys());
+ if(match == KEY_RING_PUBLIC) {
+ qb.appendWhere(KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ }
break;
+ }
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
-
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
-
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- qb.setProjectionMap(getProjectionMapForKeys());
+ case KEY_RINGS_SECRET:
+ case KEY_RING_SECRET: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_SECRET + ".oid AS _id");
+ projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
+ projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
+ qb.setProjectionMap(projectionMap);
- break;
+ qb.setTables(Tables.KEY_RINGS_SECRET);
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case PUBLIC_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID:
- qb.setTables(Tables.USER_IDS
- + " INNER JOIN " + Tables.KEY_RINGS + " ON ("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "
- + Tables.USER_IDS + "." + KeysColumns.KEY_RING_ROW_ID
- + ") LEFT JOIN " + Tables.CERTS + " ON ("
- + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + " = "
- + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = "
- + Tables.CERTS + "." + Certs.RANK
- + ") LEFT JOIN " + Tables.KEYS + " ON ("
- + Tables.KEYS + "." + Keys.KEY_ID + " = "
- + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER
- // might introduce a "trusted" flag later? for now, we simply assume
- // every private key's signature is good.
- + " AND " + Tables.KEYS + "." + Keys.TYPE
- + " == " + KeyTypes.SECRET
- + ")");
-
- groupBy = Tables.USER_IDS + "." + UserIds.RANK;
-
- qb.setProjectionMap(getProjectionMapForUserIds());
-
- if(match == PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID) {
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(3));
- } else {
- qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ if(match == KEY_RING_SECRET) {
+ qb.appendWhere(KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
}
break;
+ }
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- qb.setTables(Tables.USER_IDS);
- qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
-
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- break;
+ case KEY_RING_CERTS:
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID);
+ projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID);
+ projectionMap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
+ projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
+ projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
+ projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
+ projectionMap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
+ // verified key data
+ projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
+ // verifying key data
+ projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
+ qb.setProjectionMap(projectionMap);
- case CERTS_BY_ROW_ID:
- case CERTS_BY_KEY_ROW_ID_ALL:
- case CERTS_BY_KEY_ROW_ID_HAS_SECRET:
- case CERTS_BY_KEY_ROW_ID:
qb.setTables(Tables.CERTS
+ " JOIN " + Tables.USER_IDS + " ON ("
- + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "
- + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID
+ + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "
+ + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ " AND "
+ Tables.CERTS + "." + Certs.RANK + " = "
+ Tables.USER_IDS + "." + UserIds.RANK
- // noooooooot sure about this~ database design
- + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "")
- + " JOIN " + Tables.KEYS + " ON ("
- + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
- + Tables.KEYS + "." + Keys.KEY_ID
- + (match == CERTS_BY_KEY_ROW_ID_HAS_SECRET ?
- " AND " + Tables.KEYS + "." + Keys.TYPE + " = " + KeyTypes.SECRET : ""
- )
+ ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON ("
- + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = "
- + "signer." + UserIds.KEY_RING_ROW_ID
+ + Tables.CERTS + "." + Keys.MASTER_KEY_ID + " = "
+ + "signer." + UserIds.MASTER_KEY_ID
+ " AND "
+ "signer." + Keys.RANK + " = 0"
+ ")");
- qb.setProjectionMap(getProjectionMapForCerts());
-
groupBy = Tables.CERTS + "." + Certs.RANK + ", "
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER;
- if(match == CERTS_BY_ROW_ID) {
- qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(1));
- } else {
- qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- }
+ qb.appendWhere(Tables.CERTS + "." + KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
@@ -771,22 +465,29 @@ public class KeychainProvider extends ContentProvider {
qb.setTables(Tables.API_APPS);
break;
- case API_APPS_BY_ROW_ID:
+ case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
-
- qb.appendWhere(BaseColumns._ID + " = ");
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
- case API_APPS_BY_PACKAGE_NAME:
- qb.setTables(Tables.API_APPS);
- qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ case API_ACCOUNTS:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+
+ qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
+ break;
default:
- throw new IllegalArgumentException("Unknown URI " + uri);
+ throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
}
@@ -798,6 +499,7 @@ public class KeychainProvider extends ContentProvider {
orderBy = sortOrder;
}
+ SQLiteDatabase db = getDb().getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data changes
@@ -821,78 +523,72 @@ public class KeychainProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = getDb().getWritableDatabase();
Uri rowUri = null;
- long rowId = -1;
+ Long keyId = null;
try {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.PUBLIC);
-
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
+ case KEY_RING_PUBLIC:
+ db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values);
+ keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
break;
- case PUBLIC_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.PUBLIC);
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_SECRET:
+ db.insertOrThrow(Tables.KEY_RINGS_SECRET, null, values);
+ keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
+ break;
+ case KEY_RING_KEYS:
+ Log.d(Constants.TAG, "keys");
+ db.insertOrThrow(Tables.KEYS, null, values);
+ keyId = values.getAsLong(Keys.MASTER_KEY_ID);
break;
- case PUBLIC_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_USER_IDS:
+ db.insertOrThrow(Tables.USER_IDS, null, values);
+ keyId = values.getAsLong(UserIds.MASTER_KEY_ID);
break;
- case SECRET_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.SECRET);
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_CERTS:
+ db.insertOrThrow(Tables.CERTS, null, values);
+ keyId = values.getAsLong(Certs.MASTER_KEY_ID);
+ break;
+ case API_APPS:
+ db.insertOrThrow(Tables.API_APPS, null, values);
break;
- case SECRET_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.SECRET);
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case API_ACCOUNTS:
+ // set foreign key automatically based on given uri
+ // e.g., api_apps/com.example.app/accounts/
+ String packageName = uri.getPathSegments().get(1);
+ values.put(ApiAccounts.PACKAGE_NAME, packageName);
- break;
- case SECRET_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
+ Log.d(Constants.TAG, "provider packageName: " + packageName);
- break;
- case API_APPS:
- rowId = db.insertOrThrow(Tables.API_APPS, null, values);
- rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+ db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
+ // TODO: this is wrong:
+// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId));
break;
- case CERTS_BY_KEY_ROW_ID:
- rowId = db.insertOrThrow(Tables.CERTS, null, values);
- // kinda useless.. should this be buildCertsByKeyRowIdUri?
- // rowUri = Certs.buildCertsUri(Long.toString(rowId));
- rowUri = uri;
- break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
+ if(keyId != null) {
+ uri = KeyRings.buildGenericKeyRingUri(keyId.toString());
+ rowUri = uri;
+ }
+
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
} catch (SQLiteConstraintException e) {
- Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?");
+ Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?", e);
}
return rowUri;
@@ -902,51 +598,43 @@ public class KeychainProvider extends ContentProvider {
* {@inheritDoc}
*/
@Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
+ public int delete(Uri uri, String additionalSelection, String[] selectionArgs) {
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = getDb().getWritableDatabase();
int count;
final int match = mUriMatcher.match(uri);
- String defaultSelection = null;
switch (match) {
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
- // corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
+ case KEY_RING_PUBLIC: {
+ @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above
+ String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1);
+ if (!TextUtils.isEmpty(additionalSelection)) {
+ selection += " AND (" + additionalSelection + ")";
+ }
// corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db.delete(Tables.KEYS,
- buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs);
+ uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1));
break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
- selectionArgs);
+ }
+ case KEY_RING_SECRET: {
+ @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above
+ String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1);
+ if (!TextUtils.isEmpty(additionalSelection)) {
+ selection += " AND (" + additionalSelection + ")";
+ }
+ count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs);
+ uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1));
break;
- case API_APPS_BY_ROW_ID:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
+ }
+
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection),
selectionArgs);
break;
- case API_APPS_BY_PACKAGE_NAME:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection),
selectionArgs);
break;
default:
@@ -966,58 +654,20 @@ public class KeychainProvider extends ContentProvider {
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
String defaultSelection = null;
int count = 0;
try {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db
- .update(Tables.KEYS, values,
- buildDefaultKeysSelection(uri, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.update(Tables.USER_IDS, values,
- buildDefaultUserIdsSelection(uri, selection), selectionArgs);
- break;
- case API_APPS_BY_ROW_ID:
- count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
- break;
case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ buildDefaultApiAppsSelection(uri, selection), selectionArgs);
+ break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.update(Tables.API_ACCOUNTS, values,
+ buildDefaultApiAccountsSelection(uri, selection), selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
@@ -1034,125 +684,36 @@ public class KeychainProvider extends ContentProvider {
}
/**
- * Build default selection statement for KeyRings. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param defaultSelection
- * @param keyType
- * @param selection
- * @return
- */
- private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType,
- String selection) {
- String andType = "";
- if (keyType != null) {
- andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType;
- }
-
- String andSelection = "";
- if (!TextUtils.isEmpty(selection)) {
- andSelection = " AND (" + selection + ")";
- }
-
- return defaultSelection + andType + andSelection;
- }
-
- /**
- * Build default selection statement for Keys. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param uri
- * @param selection
- * @return
- */
- private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) {
- String rowId = uri.getLastPathSegment();
-
- String foreignKeyRingRowId = uri.getPathSegments().get(2);
- String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = "
- + foreignKeyRingRowId;
-
- String andType = "";
- if (keyType != null) {
- andType = " AND " + KeysColumns.TYPE + "=" + keyType;
- }
-
- String andSelection = "";
- if (!TextUtils.isEmpty(selection)) {
- andSelection = " AND (" + selection + ")";
- }
-
- return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection;
- }
-
- /**
- * Build default selection statement for UserIds. If no extra selection is specified only build
+ * Build default selection statement for API apps. If no extra selection is specified only build
* where clause with rowId
*
* @param uri
* @param selection
* @return
*/
- private String buildDefaultUserIdsSelection(Uri uri, String selection) {
- String rowId = uri.getLastPathSegment();
-
- String foreignKeyRingRowId = uri.getPathSegments().get(2);
- String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = "
- + foreignKeyRingRowId;
+ private String buildDefaultApiAppsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
String andSelection = "";
if (!TextUtils.isEmpty(selection)) {
andSelection = " AND (" + selection + ")";
}
- return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection;
+ return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection;
}
- /**
- * Build default selection statement for API apps. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param uri
- * @param selection
- * @return
- */
- private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) {
- String lastPathSegment = uri.getLastPathSegment();
+ private String buildDefaultApiAccountsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
+ String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
String andSelection = "";
if (!TextUtils.isEmpty(selection)) {
andSelection = " AND (" + selection + ")";
}
- if (packageSelection) {
- return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection;
- } else {
- return BaseColumns._ID + "=" + lastPathSegment + andSelection;
- }
+ return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND "
+ + ApiAccounts.ACCOUNT_NAME + "=" + accountName
+ + andSelection;
}
- // @Override
- // public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- // int match = mUriMatcher.match(uri);
- // if (match != DATA_STREAM) {
- // throw new FileNotFoundException();
- // }
- // String fileName = uri.getLastPathSegment();
- // File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName);
- // return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- // }
-
- /**
- * This broadcast is send system wide to inform other application that a keyring was inserted,
- * updated, or deleted
- */
- private void sendBroadcastDatabaseChange(int keyType, String contentItemType) {
- // TODO: Disabled, old API
- // Intent intent = new Intent();
- // intent.setAction(ACTION_BROADCAST_DATABASE_CHANGE);
- // intent.putExtra(EXTRA_BROADCAST_KEY_TYPE, keyType);
- // intent.putExtra(EXTRA_BROADCAST_CONTENT_ITEM_TYPE, contentItemType);
- //
- // getContext().sendBroadcast(intent, Constants.PERMISSION_ACCESS_API);
- }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
index da1bcb2d9..bc7de0b37 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
@@ -25,7 +25,7 @@ import android.provider.BaseColumns;
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
public class KeychainServiceBlobDatabase extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "apg_blob.db";
+ private static final String DATABASE_NAME = "openkeychain_blob.db";
private static final int DATABASE_VERSION = 2;
public static final String TABLE = "data";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
index 6ac61e157..aa30e845d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
@@ -38,7 +38,7 @@ import java.util.List;
import java.util.UUID;
public class KeychainServiceBlobProvider extends ContentProvider {
- private static final String STORE_PATH = Constants.Path.APP_DIR + "/ApgBlobs";
+ private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs";
private KeychainServiceBlobDatabase mBlobDatabase = null;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 1d249c67e..503fed3c9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -19,27 +19,36 @@ package org.sufficientlysecure.keychain.provider;
import java.security.SignatureException;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.UserAttributePacket;
-import org.spongycastle.bcpg.UserAttributeSubpacket;
-import android.content.*;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.service.remote.AppSettings;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -49,27 +58,82 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
-
+import java.util.HashSet;
+import java.util.Set;
public class ProviderHelper {
- /**
- * Private helper method to get PGPKeyRing from database
- */
+ // If we ever switch to api level 11, we can ditch this whole mess!
+ public static final int FIELD_TYPE_NULL = 1;
+ // this is called integer to stay coherent with the constants in Cursor (api level 11)
+ public static final int FIELD_TYPE_INTEGER = 2;
+ public static final int FIELD_TYPE_FLOAT = 3;
+ public static final int FIELD_TYPE_STRING = 4;
+ public static final int FIELD_TYPE_BLOB = 5;
+
+ public static Object getGenericData(Context context, Uri uri, String column, int type) {
+ return getGenericData(context, uri, new String[] { column }, new int[] { type }).get(column);
+ }
+ public static HashMap<String,Object> getGenericData(Context context, Uri uri, String[] proj, int[] types) {
+ Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
+
+ HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
+ if (cursor != null && cursor.moveToFirst()) {
+ int pos = 0;
+ for(String p : proj) {
+ switch(types[pos]) {
+ case FIELD_TYPE_NULL: result.put(p, cursor.isNull(pos)); break;
+ case FIELD_TYPE_INTEGER: result.put(p, cursor.getLong(pos)); break;
+ case FIELD_TYPE_FLOAT: result.put(p, cursor.getFloat(pos)); break;
+ case FIELD_TYPE_STRING: result.put(p, cursor.getString(pos)); break;
+ case FIELD_TYPE_BLOB: result.put(p, cursor.getBlob(pos)); break;
+ }
+ pos += 1;
+ }
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return result;
+ }
+
+ public static Object getUnifiedData(Context context, long masterKeyId, String column, int type) {
+ return getUnifiedData(context, masterKeyId, new String[] { column }, new int[] { type }).get(column);
+ }
+ public static HashMap<String,Object> getUnifiedData(Context context, long masterKeyId, String[] proj, int[] types) {
+ return getGenericData(context, KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types);
+ }
+
+ public static long getMasterKeyId(Context context, Uri queryUri) {
+ // try extracting from the uri first
+ String firstSegment = queryUri.getPathSegments().get(1);
+ if(!firstSegment.equals("find")) try {
+ return Long.parseLong(firstSegment);
+ } catch(NumberFormatException e) {
+ // didn't work? oh well.
+ Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
+ }
+ Object data = getGenericData(context, queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
+ if(data != null)
+ return (Long) data;
+ // TODO better error handling?
+ return 0L;
+ }
+
public static Map<Long, PGPKeyRing> getPGPKeyRings(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA}, null, null, null);
+ new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA },
+ null, null, null);
Map<Long, PGPKeyRing> result = new HashMap<Long, PGPKeyRing>(cursor.getCount());
if (cursor != null && cursor.moveToFirst()) do {
- int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
- int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
-
- byte[] data = cursor.getBlob(keyRingDataCol);
+ long masterKeyId = cursor.getLong(0);
+ byte[] data = cursor.getBlob(1);
if (data != null) {
- result.put(cursor.getLong(masterKeyIdCol), PgpConversionHelper.BytesToPGPKeyRing(data));
+ result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
}
-
} while(cursor.moveToNext());
if (cursor != null) {
@@ -79,82 +143,46 @@ public class ProviderHelper {
return result;
}
public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
- return getPGPKeyRings(context, queryUri).values().iterator().next();
+ Map<Long, PGPKeyRing> result = getPGPKeyRings(context, queryUri);
+ if(result.isEmpty())
+ return null;
+ return result.values().iterator().next();
}
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId
- */
- public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
- return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
+ public static PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(Context context, long keyId) {
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
+ long masterKeyId = getMasterKeyId(context, uri);
+ if(masterKeyId != 0)
+ return getPGPPublicKeyRing(context, masterKeyId);
+ return null;
+ }
+ public static PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(Context context, long keyId) {
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
+ long masterKeyId = getMasterKeyId(context, uri);
+ if(masterKeyId != 0)
+ return getPGPSecretKeyRing(context, masterKeyId);
+ return null;
}
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
- public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
+ public static PGPPublicKeyRing getPGPPublicKeyRing(Context context,
long masterKeyId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
- return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key
- * with this keyId
- */
- public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId));
+ Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
}
/**
- * Retrieves the actual PGPPublicKey object from the database blob associated with a key with
- * this keyId
- */
- public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
- PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
-
- return (keyRing == null) ? null : keyRing.getPublicKey(keyId);
- }
-
- /**
- * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId
- */
- public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
- return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
- public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context,
+ public static PGPSecretKeyRing getPGPSecretKeyRing(Context context,
long masterKeyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
+ Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
}
/**
- * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key
- * with this keyId
- */
- public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId));
- return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
- * Retrieves the actual PGPSecretKey object from the database blob associated with a key with
- * this keyId
- */
- public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
- PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
-
- return (keyRing == null) ? null : keyRing.getSecretKey(keyId);
- }
-
- /**
* Saves PGPPublicKeyRing with its keys and userIds in DB
*/
@SuppressWarnings("unchecked")
@@ -162,21 +190,12 @@ public class ProviderHelper {
PGPPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyID();
- Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
-
- // get current _ID of key
- long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver()
- .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
- if (oldQuery != null && oldQuery.moveToFirst()) {
- currentRowId = oldQuery.getLong(0);
- } else {
- Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!");
- }
+ // IF there is a secret key, preserve it!
+ PGPSecretKeyRing secretRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
// delete old version of this keyRing, which also deletes all keys and userIds on cascade
try {
- context.getContentResolver().delete(deleteUri, null, null);
+ context.getContentResolver().delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null);
} catch (UnsupportedOperationException e) {
Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e);
}
@@ -186,32 +205,28 @@ public class ProviderHelper {
// NOTE: If we would not use the same _ID again,
// getting back to the ViewKeyActivity would result in Nullpointer,
// because the currently loaded key would be gone from the database
- if (currentRowId != -1) {
- values.put(KeyRings._ID, currentRowId);
- }
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+ values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
- Uri uri = KeyRings.buildPublicKeyRingsUri();
+ Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
Uri insertedUri = context.getContentResolver().insert(uri, values);
- long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment());
// save all keys and userIds included in keyRing object in database
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
+ operations.add(buildPublicKeyOperations(context, masterKeyId, key, rank));
++rank;
}
// get a list of owned secret keys, for verification filtering
- Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRings.buildSecretKeyRingsUri());
+ Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRingData.buildSecretKeyRingUri());
int userIdRank = 0;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank));
+ operations.add(buildUserIdOperations(context, masterKeyId, userId, userIdRank));
// look through signatures for this specific key
for (PGPSignature cert : new IterableIterator<PGPSignature>(
@@ -244,8 +259,8 @@ public class ProviderHelper {
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID())
);
// regardless of verification, save the certification
- operations.add(buildPublicCertOperations(
- context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified));
+ operations.add(buildCertOperations(
+ context, masterKeyId, userIdRank, masterKey.getKeyID(), cert, verified));
}
++userIdRank;
@@ -258,99 +273,74 @@ public class ProviderHelper {
} catch (OperationApplicationException e) {
Log.e(Constants.TAG, "applyBatch failed!", e);
}
+
+ // Save the saved keyring (if any)
+ if(secretRing != null) {
+ saveKeyRing(context, secretRing);
+ }
+
}
/**
- * Saves PGPSecretKeyRing with its keys and userIds in DB
+ * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
+ * is already in the database!
*/
@SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException {
- PGPSecretKey masterKey = keyRing.getSecretKey();
- long masterKeyId = masterKey.getKeyID();
-
- Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
-
- // get current _ID of key
- long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver()
- .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
- if (oldQuery != null && oldQuery.moveToFirst()) {
- currentRowId = oldQuery.getLong(0);
- } else {
- Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!");
- }
-
- // delete old version of this keyRing, which also deletes all keys and userIds on cascade
- try {
- context.getContentResolver().delete(deleteUri, null, null);
- } catch (UnsupportedOperationException e) {
- Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e);
- }
+ long masterKeyId = keyRing.getPublicKey().getKeyID();
+ // save secret keyring
ContentValues values = new ContentValues();
- // use exactly the same _ID again to replace key in-place.
- // NOTE: If we would not use the same _ID again,
- // getting back to the ViewKeyActivity would result in Nullpointer,
- // because the currently loaded key would be gone from the database
- if (currentRowId != -1) {
- values.put(KeyRings._ID, currentRowId);
- }
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
-
+ values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
- Uri uri = KeyRings.buildSecretKeyRingsUri();
- Uri insertedUri = context.getContentResolver().insert(uri, values);
- long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment());
+ Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
+ context.getContentResolver().insert(uri, values);
- // save all keys and userIds included in keyRing object in database
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ }
- int rank = 0;
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank));
- ++rank;
- }
+ /**
+ * Saves (or updates) a pair of public and secret KeyRings in the database
+ */
+ @SuppressWarnings("unchecked")
+ public static void saveKeyRing(Context context, PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException {
+ long masterKeyId = pubRing.getPublicKey().getKeyID();
- int userIdRank = 0;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank));
- ++userIdRank;
- }
+ // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below)
+ context.getContentResolver().delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
- try {
- context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "applyBatch failed!", e);
- } catch (OperationApplicationException e) {
- Log.e(Constants.TAG, "applyBatch failed!", e);
- }
+ // save public keyring
+ saveKeyRing(context, pubRing);
+ saveKeyRing(context, privRing);
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private static ContentProviderOperation buildPublicKeyOperations(Context context,
- long keyRingRowId, PGPPublicKey key, int rank) throws IOException {
+ long masterKeyId, PGPPublicKey key, int rank) throws IOException {
+
ContentValues values = new ContentValues();
+ values.put(Keys.MASTER_KEY_ID, masterKeyId);
+ values.put(Keys.RANK, rank);
+
values.put(Keys.KEY_ID, key.getKeyID());
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.KEY_SIZE, key.getBitStrength());
- values.put(Keys.CAN_SIGN, PgpKeyHelper.isSigningKey(key));
+ values.put(Keys.ALGORITHM, key.getAlgorithm());
+ values.put(Keys.FINGERPRINT, key.getFingerprint());
+
+ values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
+ values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
values.put(Keys.IS_REVOKED, key.isRevoked());
+
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
- values.put(Keys.KEY_RING_ROW_ID, keyRingRowId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
- values.put(Keys.FINGERPRINT, key.getFingerprint());
- Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId));
+ Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -358,23 +348,23 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
- private static ContentProviderOperation buildPublicCertOperations(Context context,
- long keyRingRowId,
+ private static ContentProviderOperation buildCertOperations(Context context,
+ long masterKeyId,
int rank,
long keyId,
PGPSignature cert,
boolean verified)
throws IOException {
ContentValues values = new ContentValues();
- values.put(Certs.KEY_RING_ROW_ID, keyRingRowId);
+ values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank);
- values.put(Certs.KEY_ID, keyId);
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
+ values.put(Certs.EXPIRY, (String) null); // TODO
values.put(Certs.VERIFIED, verified);
values.put(Certs.KEY_DATA, cert.getEncoded());
- Uri uri = Certs.buildCertsByKeyRowIdUri(Long.toString(keyRingRowId));
+ Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -382,351 +372,28 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
*/
- private static ContentProviderOperation buildPublicUserIdOperations(Context context,
- long keyRingRowId, String userId, int rank) {
- ContentValues values = new ContentValues();
- values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);
- values.put(UserIds.USER_ID, userId);
- values.put(UserIds.RANK, rank);
-
- Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId));
-
- return ContentProviderOperation.newInsert(uri).withValues(values).build();
- }
-
- /**
- * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing
- */
- private static ContentProviderOperation buildSecretKeyOperations(Context context,
- long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
- ContentValues values = new ContentValues();
-
- boolean hasPrivate = true;
- if (key.isMasterKey()) {
- if (key.isPrivateKeyEmpty()) {
- hasPrivate = false;
- }
- }
-
- values.put(Keys.KEY_ID, key.getKeyID());
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
- values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
- values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
- values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
- values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
- values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
- if (expiryDate != null) {
- values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
- }
- values.put(Keys.KEY_RING_ROW_ID, keyRingRowId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
- values.put(Keys.FINGERPRINT, key.getPublicKey().getFingerprint());
-
- Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId));
-
- return ContentProviderOperation.newInsert(uri).withValues(values).build();
- }
-
- /**
- * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing
- */
- private static ContentProviderOperation buildSecretUserIdOperations(Context context,
- long keyRingRowId, String userId, int rank) {
+ private static ContentProviderOperation buildUserIdOperations(Context context,
+ long masterKeyId, String userId, int rank) {
ContentValues values = new ContentValues();
- values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);
+ values.put(UserIds.MASTER_KEY_ID, masterKeyId);
values.put(UserIds.USER_ID, userId);
values.put(UserIds.RANK, rank);
- Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId));
+ Uri uri = UserIds.buildUserIdsUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
- /**
- * Private helper method
- */
- private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) {
- Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings.MASTER_KEY_ID}, null, null, null);
-
- ArrayList<Long> masterKeyIds = new ArrayList<Long>();
- if (cursor != null) {
- int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- if (cursor.moveToFirst()) {
- do {
- masterKeyIds.add(cursor.getLong(masterKeyIdCol));
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return masterKeyIds;
- }
-
- /**
- * Private helper method
- */
- private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
- Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings._ID}, null, null, null);
-
- ArrayList<Long> rowIds = new ArrayList<Long>();
- if (cursor != null) {
- int idCol = cursor.getColumnIndex(KeyRings._ID);
- if (cursor.moveToFirst()) {
- do {
- rowIds.add(cursor.getLong(idCol));
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return rowIds;
- }
-
- /**
- * Retrieves ids of all SecretKeyRings
- */
- public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri();
- return getKeyRingsMasterKeyIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all PublicKeyRings
- */
- public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri();
- return getKeyRingsMasterKeyIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all SecretKeyRings
- */
- public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri();
- return getKeyRingsRowIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all PublicKeyRings
- */
- public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri();
- return getKeyRingsRowIds(context, queryUri);
- }
-
- public static void deletePublicKeyRing(Context context, long rowId) {
- ContentResolver cr = context.getContentResolver();
- cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
- }
-
- public static void deleteSecretKeyRing(Context context, long rowId) {
- ContentResolver cr = context.getContentResolver();
- cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);
- }
-
- /**
- * Get master key id of keyring by its row id
- */
- public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri);
- }
-
- /**
- * Get empty status of master key of keyring by its row id
- */
- public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyCanSign(context, queryUri);
- }
-
- /**
- * Private helper method to get master key private empty status of keyring by its row id
- */
- public static boolean getMasterKeyCanSign(Context context, Uri queryUri) {
- String[] projection = new String[]{
- KeyRings.MASTER_KEY_ID,
- "(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND sign_keys." + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY
- + " = 1) AS sign", };
-
- ContentResolver cr = context.getContentResolver();
- Cursor cursor = cr.query(queryUri, projection, null, null, null);
-
- long masterKeyId = -1;
- if (cursor != null && cursor.moveToFirst()) {
- int masterKeyIdCol = cursor.getColumnIndex("sign");
-
- masterKeyId = cursor.getLong(masterKeyIdCol);
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return (masterKeyId > 0);
- }
-
- public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
- // see if we can get our master key id back from the uri
- return getMasterKeyId(context, queryUri) == masterKeyId;
- }
-
- /**
- * Get master key id of keyring by its row id
- */
- public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri);
- }
-
- /**
- * Get master key id of key
- */
- public static long getMasterKeyId(Context context, Uri queryUri) {
- String[] projection = new String[]{KeyRings.MASTER_KEY_ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- long masterKeyId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int masterKeyIdCol = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
-
- masterKeyId = cursor.getLong(masterKeyIdCol);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return masterKeyId;
- }
-
- public static long getRowId(Context context, Uri queryUri) {
- String[] projection = new String[]{KeyRings._ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- long rowId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID);
-
- rowId = cursor.getLong(idCol);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return rowId;
- }
-
- /**
- * Get fingerprint of key
- */
- public static byte[] getFingerprint(Context context, Uri queryUri) {
- String[] projection = new String[]{Keys.FINGERPRINT};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- byte[] fingerprint = null;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(Keys.FINGERPRINT);
-
- fingerprint = cursor.getBlob(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- // FALLBACK: If fingerprint is not in database, get it from key blob!
- // this could happen if the key was saved by a previous version of Keychain!
- if (fingerprint == null) {
- Log.d(Constants.TAG, "FALLBACK: fingerprint is not in database, get it from key blob!");
-
- // get master key id
- projection = new String[]{KeyRings.MASTER_KEY_ID};
- cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
- long masterKeyId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
-
- masterKeyId = cursor.getLong(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, masterKeyId);
- // if it is no public key get it from your own keys...
- if (key == null) {
- PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, masterKeyId);
- if (secretKey == null) {
- Log.e(Constants.TAG, "Key could not be found!");
- return null;
- }
- key = secretKey.getPublicKey();
- }
-
- fingerprint = key.getFingerprint();
- }
-
- return fingerprint;
- }
-
- public static String getUserId(Context context, Uri queryUri) {
- String[] projection = new String[]{UserIds.USER_ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- String userId = null;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
-
- userId = cursor.getString(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return userId;
- }
-
- public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
- long[] masterKeyIds) {
+ public static ArrayList<String> getKeyRingsAsArmoredString(Context context, long[] masterKeyIds) {
ArrayList<String> output = new ArrayList<String>();
if (masterKeyIds != null && masterKeyIds.length > 0) {
- Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds);
+ Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, masterKeyIds);
if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
+ int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
+ int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
if (cursor.moveToFirst()) {
do {
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
@@ -776,48 +443,11 @@ public class ProviderHelper {
return null;
}
}
-
- public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- if (masterKeyIds != null && masterKeyIds.length > 0) {
-
- Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds);
-
- if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
- if (cursor.moveToFirst()) {
- do {
- Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
-
- // get actual keyring data blob and write it to ByteArrayOutputStream
- try {
- bos.write(cursor.getBlob(dataCol));
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- }
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- } else {
- Log.e(Constants.TAG, "No master keys given!");
- }
-
- return bos.toByteArray();
- }
-
- private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, Uri baseUri,
- long[] masterKeyIds) {
+ private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, long[] masterKeyIds) {
Cursor cursor = null;
if (masterKeyIds != null && masterKeyIds.length > 0) {
- String inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN (";
+ String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
for (int i = 0; i < masterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
@@ -826,9 +456,9 @@ public class ProviderHelper {
}
inMasterKeyList += ")";
- cursor = context.getContentResolver().query(baseUri,
- new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA},
- inMasterKeyList, null, null);
+ cursor = context.getContentResolver().query(KeyRingData.buildPublicKeyRingUri(), new String[] {
+ KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
+ }, inMasterKeyList, null, null);
}
return cursor;
@@ -859,19 +489,28 @@ public class ProviderHelper {
ContentValues values = new ContentValues();
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
- values.put(ApiApps.KEY_ID, appSettings.getKeyId());
- values.put(ApiApps.COMPRESSION, appSettings.getCompression());
- values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm());
- values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm());
+ return values;
+ }
+ private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
+ ContentValues values = new ContentValues();
+ values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
+ values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
+ values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression());
+ values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm());
+ values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm());
return values;
}
public static void insertApiApp(Context context, AppSettings appSettings) {
- context.getContentResolver().insert(ApiApps.CONTENT_URI,
+ context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI,
contentValueForApiApps(appSettings));
}
+ public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) {
+ context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings));
+ }
+
public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null,
null) <= 0) {
@@ -879,30 +518,73 @@ public class ProviderHelper {
}
}
+ public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) {
+ if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null,
+ null) <= 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Must be an uri pointing to an account
+ *
+ * @param context
+ * @param uri
+ * @return
+ */
public static AppSettings getApiAppSettings(Context context, Uri uri) {
AppSettings settings = null;
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
if (cur != null && cur.moveToFirst()) {
settings = new AppSettings();
- settings.setPackageName(cur.getString(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageSignature(cur.getBlob(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
- settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
- settings.setCompression(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION)));
- settings.setHashAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM)));
- settings.setEncryptionAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM)));
+ settings.setPackageName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ settings.setPackageSignature(cur.getBlob(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
}
return settings;
}
+ public static AccountSettings getApiAccountSettings(Context context, Uri accountUri) {
+ AccountSettings settings = null;
+
+ Cursor cur = context.getContentResolver().query(accountUri, null, null, null, null);
+ if (cur != null && cur.moveToFirst()) {
+ settings = new AccountSettings();
+
+ settings.setAccountName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
+ settings.setKeyId(cur.getLong(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
+ settings.setCompression(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
+ settings.setHashAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
+ settings.setEncryptionAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
+ }
+
+ return settings;
+ }
+
+ public static Set<Long> getAllKeyIdsForApp(Context context, Uri uri) {
+ Set<Long> keyIds = new HashSet<Long>();
+
+ Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+
+ return keyIds;
+ }
+
public static byte[] getApiAppSignature(Context context, String packageName) {
- Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
+ Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
index 6f2d67efb..832cbc752 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
@@ -15,48 +15,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Id;
-public class AppSettings {
- private String mPackageName;
- private byte[] mPackageSignature;
+public class AccountSettings {
+ private String mAccountName;
private long mKeyId = Id.key.none;
private int mEncryptionAlgorithm;
private int mHashAlgorithm;
private int mCompression;
- public AppSettings() {
+ public AccountSettings() {
}
- public AppSettings(String packageName, byte[] packageSignature) {
+ public AccountSettings(String accountName) {
super();
- this.mPackageName = packageName;
- this.mPackageSignature = packageSignature;
+ this.mAccountName = accountName;
+
// defaults:
this.mEncryptionAlgorithm = PGPEncryptedData.AES_256;
this.mHashAlgorithm = HashAlgorithmTags.SHA512;
this.mCompression = Id.choice.compression.zlib;
}
- public String getPackageName() {
- return mPackageName;
- }
-
- public void setPackageName(String packageName) {
- this.mPackageName = packageName;
- }
-
- public byte[] getPackageSignature() {
- return mPackageSignature;
+ public String getAccountName() {
+ return mAccountName;
}
- public void setPackageSignature(byte[] packageSignature) {
- this.mPackageSignature = packageSignature;
+ public void setAccountName(String mAccountName) {
+ this.mAccountName = mAccountName;
}
public long getKeyId() {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
new file mode 100644
index 000000000..a3f9f84c9
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote;
+
+public class AppSettings {
+ private String mPackageName;
+ private byte[] mPackageSignature;
+
+ public AppSettings() {
+
+ }
+
+ public AppSettings(String packageName, byte[] packageSignature) {
+ super();
+ this.mPackageName = packageName;
+ this.mPackageSignature = packageSignature;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public void setPackageName(String packageName) {
+ this.mPackageName = packageName;
+ }
+
+ public byte[] getPackageSignature() {
+ return mPackageSignature;
+ }
+
+ public void setPackageSignature(byte[] packageSignature) {
+ this.mPackageSignature = packageSignature;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 95dc897f0..b38fea5a9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.content.Intent;
@@ -23,6 +23,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
@@ -34,22 +35,22 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Set;
public class OpenPgpService extends RemoteService {
- private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
- private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
- private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553;
-
/**
* Search database for key ids based on emails.
*
@@ -61,15 +62,15 @@ public class OpenPgpService extends RemoteService {
ArrayList<Long> keyIds = new ArrayList<Long>();
boolean missingUserIdsCheck = false;
- boolean dublicateUserIdsCheck = false;
+ boolean duplicateUserIdsCheck = false;
ArrayList<String> missingUserIds = new ArrayList<String>();
- ArrayList<String> dublicateUserIds = new ArrayList<String>();
+ ArrayList<String> duplicateUserIds = new ArrayList<String>();
for (String email : encryptionUserIds) {
- Uri uri = KeychainContract.KeyRings.buildPublicKeyRingsByEmailsUri(email);
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
Cursor cur = getContentResolver().query(uri, null, null, null, null);
if (cur.moveToFirst()) {
- long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID));
+ long id = cur.getLong(cur.getColumnIndex(KeyRings.MASTER_KEY_ID));
keyIds.add(id);
} else {
missingUserIdsCheck = true;
@@ -77,8 +78,8 @@ public class OpenPgpService extends RemoteService {
Log.d(Constants.TAG, "user id missing");
}
if (cur.moveToNext()) {
- dublicateUserIdsCheck = true;
- dublicateUserIds.add(email);
+ duplicateUserIdsCheck = true;
+ duplicateUserIds.add(email);
Log.d(Constants.TAG, "more than one user id with the same email");
}
}
@@ -90,17 +91,18 @@ public class OpenPgpService extends RemoteService {
}
// allow the user to verify pub key selection
- if (missingUserIdsCheck || dublicateUserIdsCheck) {
+ if (missingUserIdsCheck || duplicateUserIdsCheck) {
// build PendingIntent
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, duplicateUserIds);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity
- (getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -126,8 +128,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity
- (getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -137,7 +140,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent signImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, AccountSettings accSettings) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -146,11 +149,11 @@ public class OpenPgpService extends RemoteService {
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
@@ -164,9 +167,9 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ .signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
builder.build().execute();
} finally {
@@ -187,7 +190,8 @@ public class OpenPgpService extends RemoteService {
}
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings, boolean sign) {
+ ParcelFileDescriptor output, AccountSettings accSettings,
+ boolean sign) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -210,14 +214,14 @@ public class OpenPgpService extends RemoteService {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR,
- "Missing parameter user_ids or key_ids!"));
+ "Missing parameter user_ids or key_ids!"));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
}
// add own key for encryption
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
- keyIds[keyIds.length - 1] = appSettings.getKeyId();
+ keyIds[keyIds.length - 1] = accSettings.getKeyId();
// build InputData and write into OutputStream
// Get Input- and OutputStream from ParcelFileDescriptor
@@ -229,8 +233,8 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .compressionId(appSettings.getCompression())
- .symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
+ .compressionId(accSettings.getCompression())
+ .symmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
.encryptionKeyIds(keyIds);
if (sign) {
@@ -239,18 +243,18 @@ public class OpenPgpService extends RemoteService {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- appSettings.getKeyId());
+ accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
// sign and encrypt
- builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ builder.signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
} else {
// encrypt only
@@ -276,7 +280,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, Set<Long> allowedKeyIds) {
try {
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
@@ -290,19 +294,21 @@ public class OpenPgpService extends RemoteService {
InputData inputData = new InputData(is, inputLength);
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
- builder.assumeSymmetric(false) // no support for symmetric encryption
- // allow only the private key for this app for decryption
- .enforcedKeyId(appSettings.getKeyId())
+ builder.allowSymmetricDecryption(false) // no support for symmetric encryption
+ .allowedKeyIds(allowedKeyIds) // allow only private keys associated with
+ // accounts of this app
.passphrase(passphrase);
// TODO: currently does not support binary signed-only content
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
- if (decryptVerifyResult.isKeyPassphraseNeeded()) {
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle =
+ getPassphraseBundleIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
return passphraseBundle;
- } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
}
@@ -311,14 +317,14 @@ public class OpenPgpService extends RemoteService {
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) {
// If signature is unknown we return an _additional_ PendingIntent
// to retrieve the missing key
- // TODO!!!
- Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
- intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
- intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
- intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+ Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
+ intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
@@ -346,19 +352,19 @@ public class OpenPgpService extends RemoteService {
try {
long keyId = data.getLongExtra(OpenPgpApi.EXTRA_KEY_ID, 0);
- if (ProviderHelper.getPGPPublicKeyByKeyId(this, keyId) == null) {
+ if (ProviderHelper.getPGPPublicKeyRing(this, keyId) == null) {
Intent result = new Intent();
// If keys are not in db we return an additional PendingIntent
// to retrieve the missing key
- // TODO!!!
- Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
- intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
- intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
- intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+ Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, keyId);
+ intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
@@ -366,6 +372,9 @@ public class OpenPgpService extends RemoteService {
} else {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+
+ // TODO: also return PendingIntent that opens the key view activity
+
return result;
}
} catch (Exception e) {
@@ -407,7 +416,7 @@ public class OpenPgpService extends RemoteService {
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
- (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
+ (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
@@ -432,17 +441,30 @@ public class OpenPgpService extends RemoteService {
return errorResult;
}
- final AppSettings appSettings = getAppSettings();
+ String accName;
+ if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) {
+ accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
+ } else {
+ accName = "default";
+ }
+ final AccountSettings accSettings = getAccSettings(accName);
+ if (accSettings == null) {
+ return getCreateAccountIntent(data, accName);
+ }
String action = data.getAction();
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
- return signImpl(data, input, output, appSettings);
+ return signImpl(data, input, output, accSettings);
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, false);
+ return encryptAndSignImpl(data, input, output, accSettings, false);
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, true);
+ return encryptAndSignImpl(data, input, output, accSettings, true);
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, appSettings);
+ String currentPkg = getCurrentCallingPackage();
+ Set<Long> allowedKeyIds =
+ ProviderHelper.getAllKeyIdsForApp(mContext,
+ ApiAccounts.buildBaseUri(currentPkg));
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds);
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
return getKeyImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index 6a883316a..16a800022 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.app.Service;
@@ -27,12 +27,14 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
+
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -44,10 +46,6 @@ import java.util.Arrays;
public abstract class RemoteService extends Service {
Context mContext;
- private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
- private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
-
-
public Context getContext() {
return mContext;
}
@@ -55,13 +53,10 @@ public abstract class RemoteService extends Service {
protected Intent isAllowed(Intent data) {
try {
if (isCallerAllowed(false)) {
-
return null;
} else {
- String[] callingPackages = getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
- // TODO: currently simply uses first entry
- String packageName = callingPackages[0];
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "isAllowed packageName: " + packageName);
byte[] packageSignature;
try {
@@ -83,8 +78,9 @@ public abstract class RemoteService extends Service {
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -99,11 +95,12 @@ public abstract class RemoteService extends Service {
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
- getString(R.string.api_error_wrong_signature));
+ getString(R.string.api_error_wrong_signature));
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_ERROR, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -125,27 +122,57 @@ public abstract class RemoteService extends Service {
}
/**
- * Retrieves AppSettings from database for the application calling this remote service
+ * Returns package name associated with the UID, which is assigned to the process that sent you the
+ * current transaction that is being processed :)
*
- * @return
+ * @return package name
*/
- protected AppSettings getAppSettings() {
+ protected String getCurrentCallingPackage() {
+ // TODO:
+ // callingPackages contains more than one entry when sharedUserId has been used...
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ String currentPkg = callingPackages[0];
+ Log.d(Constants.TAG, "currentPkg: " + currentPkg);
- // get app settings for this package
- for (int i = 0; i < callingPackages.length; i++) {
- String currentPkg = callingPackages[i];
+ return currentPkg;
+ }
+
+ /**
+ * Retrieves AccountSettings from database for the application calling this remote service
+ *
+ * @return
+ */
+ protected AccountSettings getAccSettings(String accountName) {
+ String currentPkg = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
- Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
+ Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
- AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri);
- if (settings != null) {
- return settings;
- }
- }
+ return settings; // can be null!
+ }
+
+ protected Intent getCreateAccountIntent(Intent data, String accountName) {
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
+
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
+ intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT);
+ intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- return null;
+ return result;
}
/**
@@ -180,7 +207,7 @@ public abstract class RemoteService extends Service {
}
}
- Log.d(Constants.TAG, "Caller is NOT allowed!");
+ Log.d(Constants.TAG, "Uid is NOT allowed!");
return false;
}
@@ -192,7 +219,7 @@ public abstract class RemoteService extends Service {
* @throws WrongPackageSignatureException
*/
private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
- Log.d(Constants.TAG, "packageName: " + packageName);
+ Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
@@ -216,10 +243,12 @@ public abstract class RemoteService extends Service {
return true;
} else {
throw new WrongPackageSignatureException(
- "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)");
+ "PACKAGE NOT ALLOWED! Signature wrong! (Signature not " +
+ "equals signature from database)");
}
}
+ Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
return false;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
index 0b642086a..6f44a65e9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
public class WrongPackageSignatureException extends Exception {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 2ef170dec..671a3e0aa 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.net.Uri;
@@ -24,16 +24,18 @@ import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.util.Log;
-public class AppSettingsActivity extends ActionBarActivity {
- private Uri mAppUri;
+public class AccountSettingsActivity extends ActionBarActivity {
+ private Uri mAccountUri;
- private AppSettingsFragment mSettingsFragment;
+ private AccountSettingsFragment mAccountSettingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -50,57 +52,58 @@ public class AppSettingsActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_settings_activity);
+ setContentView(R.layout.api_account_settings_activity);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
Intent intent = getIntent();
- mAppUri = intent.getData();
- if (mAppUri == null) {
+ mAccountUri = intent.getData();
+ if (mAccountUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
- Log.d(Constants.TAG, "uri: " + mAppUri);
- loadData(mAppUri);
+ Log.d(Constants.TAG, "uri: " + mAccountUri);
+ loadData(savedInstanceState, mAccountUri);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ getMenuInflater().inflate(R.menu.api_account_settings, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_api_settings_revoke:
- revokeAccess();
+ case R.id.menu_account_settings_delete:
+ deleteAccount();
return true;
- case R.id.menu_api_settings_cancel:
+ case R.id.menu_account_settings_cancel:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
- private void loadData(Uri appUri) {
- AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
- mSettingsFragment.setAppSettings(settings);
+ private void loadData(Bundle savedInstanceState, Uri accountUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, accountUri);
+ mAccountSettingsFragment.setAccSettings(settings);
}
- private void revokeAccess() {
- if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ private void deleteAccount() {
+ if (getContentResolver().delete(mAccountUri, null, null) <= 0) {
throw new RuntimeException();
}
finish();
}
private void save() {
- ProviderHelper.updateApiApp(this, mSettingsFragment.getAppSettings(), mAppUri);
+ ProviderHelper.updateApiAccount(this, mAccountSettingsFragment.getAccSettings(), mAccountUri);
finish();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
new file mode 100644
index 000000000..992aa7c95
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.ui.EditKeyActivity;
+import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
+import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
+import org.sufficientlysecure.keychain.util.AlgorithmNames;
+
+public class AccountSettingsFragment extends Fragment implements
+ SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
+
+ private static final int REQUEST_CODE_CREATE_KEY = 0x00008884;
+
+ // model
+ private AccountSettings mAccSettings;
+
+ // view
+ private TextView mAccNameView;
+ private Spinner mEncryptionAlgorithm;
+ private Spinner mHashAlgorithm;
+ private Spinner mCompression;
+
+ private SelectSecretKeyLayoutFragment mSelectKeyFragment;
+ private BootstrapButton mCreateKeyButton;
+
+ KeyValueSpinnerAdapter mEncryptionAdapter;
+ KeyValueSpinnerAdapter mHashAdapter;
+ KeyValueSpinnerAdapter mCompressionAdapter;
+
+ public AccountSettings getAccSettings() {
+ return mAccSettings;
+ }
+
+ public void setAccSettings(AccountSettings accountSettings) {
+ this.mAccSettings = accountSettings;
+
+ mAccNameView.setText(accountSettings.getAccountName());
+ mSelectKeyFragment.selectKey(accountSettings.getKeyId());
+ mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(accountSettings
+ .getEncryptionAlgorithm()));
+ mHashAlgorithm.setSelection(mHashAdapter.getPosition(accountSettings.getHashAlgorithm()));
+ mCompression.setSelection(mCompressionAdapter.getPosition(accountSettings.getCompression()));
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false);
+ initView(view);
+ return view;
+ }
+
+ /**
+ * Set error String on key selection
+ *
+ * @param error
+ */
+ public void setErrorOnSelectKeyFragment(String error) {
+ mSelectKeyFragment.setError(error);
+ }
+
+ private void initView(View view) {
+ mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
+ R.id.api_account_settings_select_key_fragment);
+ mSelectKeyFragment.setCallback(this);
+
+ mAccNameView = (TextView) view.findViewById(R.id.api_account_settings_acc_name);
+ mEncryptionAlgorithm = (Spinner) view
+ .findViewById(R.id.api_account_settings_encryption_algorithm);
+ mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm);
+ mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression);
+ mCreateKeyButton = (BootstrapButton) view.findViewById(R.id.api_account_settings_create_key);
+
+ mCreateKeyButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+
+ AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
+
+ mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getEncryptionNames());
+ mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
+ mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setEncryptionAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
+ mHashAlgorithm.setAdapter(mHashAdapter);
+ mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setHashAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getCompressionNames());
+ mCompression.setAdapter(mCompressionAdapter);
+ mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setCompression((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ // set default user id to account name TODO: not working currently in EditKey
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, mAccSettings.getAccountName());
+ startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_CREATE_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // select newly created key
+ long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), data.getData());
+ mSelectKeyFragment.selectKey(masterKeyId);
+ }
+ break;
+ }
+
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+
+ /**
+ * callback from select secret key fragment
+ */
+ @Override
+ public void onKeySelected(long secretKeyId) {
+ mAccSettings.setKeyId(secretKeyId);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
new file mode 100644
index 000000000..cfc9c92ad
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.widget.FixedListView;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AccountsListFragment extends ListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ private static final String ARG_DATA_URI = "uri";
+
+ // This is the Adapter being used to display the list's data.
+ AccountsAdapter mAdapter;
+
+ private Uri mDataUri;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static AccountsListFragment newInstance(Uri dataUri) {
+ AccountsListFragment frag = new AccountsListFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View layout = super.onCreateView(inflater, container,
+ savedInstanceState);
+ ListView lv = (ListView) layout.findViewById(android.R.id.list);
+ ViewGroup parent = (ViewGroup) lv.getParent();
+
+ /*
+ * http://stackoverflow.com/a/15880684
+ * Remove ListView and add FixedListView in its place.
+ * This is done here programatically to be still able to use the progressBar of ListFragment.
+ *
+ * We want FixedListView to be able to put this ListFragment inside a ScrollView
+ */
+ int lvIndex = parent.indexOfChild(lv);
+ parent.removeViewAt(lvIndex);
+ FixedListView newLv = new FixedListView(getActivity());
+ newLv.setId(android.R.id.list);
+ parent.addView(newLv, lvIndex, lv.getLayoutParams());
+ return layout;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mDataUri = getArguments().getParcelable(ARG_DATA_URI);
+
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedAccountName = mAdapter.getItemAccountName(position);
+ Uri accountUri = mDataUri.buildUpon().appendEncodedPath(selectedAccountName).build();
+ Log.d(Constants.TAG, "accountUri: " + accountUri);
+
+ // edit account settings
+ Intent intent = new Intent(getActivity(), AccountSettingsActivity.class);
+ intent.setData(accountUri);
+ startActivity(intent);
+ }
+ });
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.api_settings_accounts_empty));
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new AccountsAdapter(getActivity(), null, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ // These are the Contacts rows that we will retrieve.
+ static final String[] PROJECTION = new String[]{
+ KeychainContract.ApiAccounts._ID, // 0
+ KeychainContract.ApiAccounts.ACCOUNT_NAME // 1
+ };
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null,
+ KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC");
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mAdapter.swapCursor(data);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ // This is called when the last Cursor provided to onLoadFinished()
+ // above is about to be closed. We need to make sure we are no
+ // longer using it.
+ mAdapter.swapCursor(null);
+ }
+
+ private class AccountsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public AccountsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemAccountName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_accounts_adapter_item_name);
+
+ String accountName = cursor.getString(1);
+ text.setText(accountName);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_accounts_adapter_list_item, null);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
new file mode 100644
index 000000000..f6f9631cb
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote.ui;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AppSettingsActivity extends ActionBarActivity {
+ private Uri mAppUri;
+
+ private AppSettingsFragment mSettingsFragment;
+ private AccountsListFragment mAccountsListFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // let the actionbar look like Android's contact app
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setIcon(android.R.color.transparent);
+ actionBar.setHomeButtonEnabled(true);
+
+ setContentView(R.layout.api_app_settings_activity);
+
+ mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ Intent intent = getIntent();
+ mAppUri = intent.getData();
+ if (mAppUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mAppUri);
+ loadData(savedInstanceState, mAppUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_api_settings_revoke:
+ revokeAccess();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Bundle savedInstanceState, Uri appUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
+ mSettingsFragment.setAppSettings(settings);
+
+ String appName;
+ PackageManager pm = getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
+ appName = (String) pm.getApplicationLabel(ai);
+ } catch (PackageManager.NameNotFoundException e) {
+ // fallback
+ appName = settings.getPackageName();
+ }
+ setTitle(appName);
+
+ Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
+ Log.d(Constants.TAG, "accountsUri: " + accountsUri);
+ startListFragment(savedInstanceState, accountsUri);
+ }
+
+ private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
+ // However, if we're being restored from a previous state,
+ // then we don't need to do anything and should return or else
+ // we could end up with overlapping fragments.
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ // Create an instance of the fragment
+ mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
+
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
+ private void revokeAccess() {
+ if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ throw new RuntimeException();
+ }
+ finish();
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
new file mode 100644
index 000000000..a6db02708
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote.ui;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class AppSettingsFragment extends Fragment {
+
+ // model
+ private AppSettings mAppSettings;
+
+ // view
+ private TextView mAppNameView;
+ private ImageView mAppIconView;
+ private TextView mPackageName;
+ private TextView mPackageSignature;
+
+ public AppSettings getAppSettings() {
+ return mAppSettings;
+ }
+
+ public void setAppSettings(AppSettings appSettings) {
+ this.mAppSettings = appSettings;
+ updateView(appSettings);
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
+ mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
+ mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
+ mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
+ mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
+ return view;
+ }
+
+ private void updateView(AppSettings appSettings) {
+ // get application name and icon from package manager
+ String appName;
+ Drawable appIcon = null;
+ PackageManager pm = getActivity().getApplicationContext().getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0);
+
+ appName = (String) pm.getApplicationLabel(ai);
+ appIcon = pm.getApplicationIcon(ai);
+ } catch (NameNotFoundException e) {
+ // fallback
+ appName = appSettings.getPackageName();
+ }
+ mAppNameView.setText(appName);
+ mAppIconView.setImageDrawable(appIcon);
+
+ // advanced info: package name
+ mPackageName.setText(appSettings.getPackageName());
+
+ // advanced info: package signature SHA-256
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(appSettings.getPackageSignature());
+ byte[] digest = md.digest();
+ String signature = new String(Hex.encode(digest));
+
+ mPackageSignature.setText(signature);
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.TAG, "Should not happen!", e);
+ }
+ }
+
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
index f6f216efd..f86d279f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
@@ -15,13 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.os.Bundle;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.DrawerActivity;
-public class RegisteredAppsListActivity extends DrawerActivity {
+public class AppsListActivity extends DrawerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
index 25d0c7593..22082e913 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
@@ -15,10 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
-import android.content.ContentUris;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -26,14 +28,20 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-public class RegisteredAppsListFragment extends ListFragment implements
+public class AppsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
@@ -46,9 +54,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedPackageName = mAdapter.getItemPackageName(position);
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
- intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
+ intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
startActivity(intent);
}
});
@@ -70,7 +79,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
}
// These are the Contacts rows that we will retrieve.
- static final String[] PROJECTION = new String[]{ApiApps._ID, ApiApps.PACKAGE_NAME};
+ static final String[] PROJECTION = new String[]{
+ ApiApps._ID, // 0
+ ApiApps.PACKAGE_NAME // 1
+ };
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -98,4 +110,65 @@ public class RegisteredAppsListFragment extends ListFragment implements
mAdapter.swapCursor(null);
}
+ private class RegisteredAppsAdapter extends CursorAdapter {
+
+ private LayoutInflater mInflater;
+ private PackageManager mPM;
+
+ public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ mPM = context.getApplicationContext().getPackageManager();
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemPackageName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
+ ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
+
+ String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
+ if (packageName != null) {
+ // get application name
+ try {
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
+
+ text.setText(mPM.getApplicationLabel(ai));
+ icon.setImageDrawable(mPM.getApplicationIcon(ai));
+ } catch (final PackageManager.NameNotFoundException e) {
+ // fallback
+ text.setText(packageName);
+ }
+ } else {
+ // fallback
+ text.setText(packageName);
+ }
+
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
+ }
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index e20114853..307c9c61a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.os.Bundle;
@@ -24,6 +24,7 @@ import android.os.Message;
import android.os.Messenger;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
+
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
@@ -31,7 +32,10 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -41,6 +45,8 @@ import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
+ public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX
+ + "API_ACTIVITY_CREATE_ACCOUNT";
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
+ "API_ACTIVITY_CACHE_PASSPHRASE";
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
@@ -57,6 +63,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
// register action
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
+ // create acc action
+ public static final String EXTRA_ACC_NAME = "acc_name";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
@@ -65,7 +73,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
public static final String EXTRA_ERROR_MESSAGE = "error_message";
// register view
- private AppSettingsFragment mSettingsFragment;
+ private AppSettingsFragment mAppSettingsFragment;
+ // create acc view
+ private AccountSettingsFragment mAccSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
@@ -85,6 +95,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
+ Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
@@ -94,13 +105,52 @@ public class RemoteServiceActivity extends ActionBarActivity {
public void onClick(View v) {
// Allow
+ ProviderHelper.insertApiApp(RemoteServiceActivity.this,
+ mAppSettingsFragment.getAppSettings());
+
+ // give data through for new service call
+ Intent resultData = extras.getParcelable(EXTRA_DATA);
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ RemoteServiceActivity.this.finish();
+ }
+ }, R.string.api_register_disallow, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Disallow
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
+ }
+ );
+
+ setContentView(R.layout.api_remote_register_app);
+
+ mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ AppSettings settings = new AppSettings(packageName, packageSignature);
+ mAppSettingsFragment.setAppSettings(settings);
+ } else if (ACTION_CREATE_ACCOUNT.equals(action)) {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final String accName = extras.getString(EXTRA_ACC_NAME);
+
+ // Inflate a "Done"/"Cancel" custom action bar view
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(),
+ R.string.api_settings_save, R.drawable.ic_action_done,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save
+
// user needs to select a key!
- if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
- mSettingsFragment.setErrorOnSelectKeyFragment(
+ if (mAccSettingsFragment.getAccSettings().getKeyId() == Id.key.none) {
+ mAccSettingsFragment.setErrorOnSelectKeyFragment(
getString(R.string.api_register_error_select_key));
} else {
- ProviderHelper.insertApiApp(RemoteServiceActivity.this,
- mSettingsFragment.getAppSettings());
+ ProviderHelper.insertApiAccount(RemoteServiceActivity.this,
+ KeychainContract.ApiAccounts.buildBaseUri(packageName),
+ mAccSettingsFragment.getAccSettings());
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -108,29 +158,43 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.finish();
}
}
- }, R.string.api_register_disallow, R.drawable.ic_action_cancel,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Disallow
- RemoteServiceActivity.this.setResult(RESULT_CANCELED);
- RemoteServiceActivity.this.finish();
- }
+ }, R.string.api_settings_cancel, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Cancel
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
}
);
- setContentView(R.layout.api_app_register_activity);
+ setContentView(R.layout.api_remote_create_account);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
- AppSettings settings = new AppSettings(packageName, packageSignature);
- mSettingsFragment.setAppSettings(settings);
+ AccountSettings settings = new AccountSettings(accName);
+ mAccSettingsFragment.setAccSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
- Intent resultData = extras.getParcelable(EXTRA_DATA);
+ final Intent resultData = extras.getParcelable(EXTRA_DATA);
+
+ PassphraseDialogFragment.show(this, secretKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ // return given params again, for calling the service method again
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ } else {
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ }
+
+ RemoteServiceActivity.this.finish();
+ }
+ });
- showPassphraseDialog(resultData, secretKeyId);
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
ArrayList<String> missingUserIds = intent
@@ -185,7 +249,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
);
- setContentView(R.layout.api_app_select_pub_keys_activity);
+ setContentView(R.layout.api_remote_select_pub_keys);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
@@ -227,7 +291,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_error_message);
+ setContentView(R.layout.api_remote_error_message);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index daaff5d54..1c6aa7971 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -18,30 +18,61 @@
package org.sufficientlysecure.keychain.service;
import android.app.IntentService;
-import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.*;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.pgp.PgpImportExport;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.*;
-
-import java.io.*;
+import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+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.ArrayList;
-import java.util.GregorianCalendar;
import java.util.List;
/**
@@ -84,35 +115,26 @@ public class KeychainIntentService extends IntentService
// possible targets:
public static final int TARGET_BYTES = 1;
public static final int TARGET_URI = 2;
- public static final int TARGET_STREAM = 3;
// encrypt
- public static final String ENCRYPT_SECRET_KEY_ID = "secret_key_id";
+ public static final String ENCRYPT_SIGNATURE_KEY_ID = "secret_key_id";
public static final String ENCRYPT_USE_ASCII_ARMOR = "use_ascii_armor";
public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryption_keys_ids";
public static final String ENCRYPT_COMPRESSION_ID = "compression_id";
- public static final String ENCRYPT_GENERATE_SIGNATURE = "generate_signature";
- public static final String ENCRYPT_SIGN_ONLY = "sign_only";
public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes";
public static final String ENCRYPT_INPUT_FILE = "input_file";
public static final String ENCRYPT_OUTPUT_FILE = "output_file";
- public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
+ public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
// decrypt/verify
- public static final String DECRYPT_RETURN_BYTES = "return_binary";
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
- public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric";
+ public static final String DECRYPT_PASSPHRASE = "passphrase";
// save keyring
- public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
- public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "current_passphrase";
- public static final String SAVE_KEYRING_USER_IDS = "user_ids";
- public static final String SAVE_KEYRING_KEYS = "keys";
- public static final String SAVE_KEYRING_KEYS_USAGES = "keys_usages";
- public static final String SAVE_KEYRING_KEYS_EXPIRY_DATES = "keys_expiry_dates";
- public static final String SAVE_KEYRING_MASTER_KEY_ID = "master_key_id";
+ public static final String SAVE_KEYRING_PARCEL = "save_parcel";
public static final String SAVE_KEYRING_CAN_SIGN = "can_sign";
+
// generate key
public static final String GENERATE_KEY_ALGORITHM = "algorithm";
public static final String GENERATE_KEY_KEY_SIZE = "key_size";
@@ -128,10 +150,9 @@ public class KeychainIntentService extends IntentService
// export key
public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";
public static final String EXPORT_FILENAME = "export_filename";
- public static final String EXPORT_KEY_TYPE = "export_key_type";
+ public static final String EXPORT_SECRET = "export_secret";
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_ROW_ID = "export_key_rind_row_id";
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
@@ -150,17 +171,12 @@ public class KeychainIntentService extends IntentService
*/
// keys
public static final String RESULT_NEW_KEY = "new_key";
- public static final String RESULT_NEW_KEY2 = "new_key2";
+ public static final String RESULT_KEY_USAGES = "new_key_usages";
// encrypt
- public static final String RESULT_SIGNATURE_BYTES = "signature_data";
- public static final String RESULT_SIGNATURE_STRING = "signature_text";
- public static final String RESULT_ENCRYPTED_STRING = "encrypted_message";
- public static final String RESULT_ENCRYPTED_BYTES = "encrypted_data";
- public static final String RESULT_URI = "result_uri";
+ public static final String RESULT_BYTES = "encrypted_data";
// decrypt/verify
- public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature";
@@ -172,10 +188,6 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
- // query
- public static final String RESULT_QUERY_KEY_DATA = "query_key_data";
- public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "query_key_search_result";
-
Messenger mMessenger;
private boolean mIsCanceled;
@@ -225,20 +237,17 @@ public class KeychainIntentService extends IntentService
/* Input */
int target = data.getInt(TARGET);
- long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
- String encryptionPassphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
+ long signatureKeyId = data.getLong(ENCRYPT_SIGNATURE_KEY_ID);
+ String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
- boolean generateSignature = data.getBoolean(ENCRYPT_GENERATE_SIGNATURE);
- boolean signOnly = data.getBoolean(ENCRYPT_SIGN_ONLY);
-
- InputStream inStream = null;
- long inLength = -1;
- InputData inputData = null;
- OutputStream outStream = null;
- String streamFilename = null;
+ InputStream inStream;
+ long inLength;
+ InputData inputData;
+ OutputStream outStream;
+// String streamFilename = null;
switch (target) {
case TARGET_BYTES: /* encrypting bytes directly */
byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES);
@@ -270,29 +279,30 @@ public class KeychainIntentService extends IntentService
break;
- case TARGET_STREAM: /* Encrypting stream from content uri */
- Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-
- // InputStream
- InputStream in = getContentResolver().openInputStream(providerUri);
- inLength = PgpHelper.getLengthOfStream(in);
- inputData = new InputData(in, inLength);
-
- // OutputStream
- try {
- while (true) {
- streamFilename = PgpHelper.generateRandomFilename(32);
- if (streamFilename == null) {
- throw new PgpGeneralException("couldn't generate random file name");
- }
- openFileInput(streamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-
- break;
+ // TODO: not used currently
+// case TARGET_STREAM: /* Encrypting stream from content uri */
+// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
+//
+// // InputStream
+// InputStream in = getContentResolver().openInputStream(providerUri);
+// inLength = PgpHelper.getLengthOfStream(in);
+// inputData = new InputData(in, inLength);
+//
+// // OutputStream
+// try {
+// while (true) {
+// streamFilename = PgpHelper.generateRandomFilename(32);
+// if (streamFilename == null) {
+// throw new PgpGeneralException("couldn't generate random file name");
+// }
+// openFileInput(streamFilename).close();
+// }
+// } catch (FileNotFoundException e) {
+// // found a name that isn't used yet
+// }
+// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
+//
+// break;
default:
throw new PgpGeneralException("No target choosen!");
@@ -304,45 +314,20 @@ public class KeychainIntentService extends IntentService
new PgpSignEncrypt.Builder(this, inputData, outStream);
builder.progress(this);
- if (generateSignature) {
- Log.d(Constants.TAG, "generating signature...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().generateSignature();
- } else if (signOnly) {
- Log.d(Constants.TAG, "sign only...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().execute();
- } else {
- Log.d(Constants.TAG, "encrypt...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .compressionId(compressionId)
- .symmetricEncryptionAlgorithm(
- Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .encryptionKeyIds(encryptionKeyIds)
- .encryptionPassphrase(encryptionPassphrase)
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().execute();
- }
+ builder.enableAsciiArmorOutput(useAsciiArmor)
+ .compressionId(compressionId)
+ .symmetricEncryptionAlgorithm(
+ Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
+ .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
+ .encryptionKeyIds(encryptionKeyIds)
+ .symmetricPassphrase(symmetricPassphrase)
+ .signatureKeyId(signatureKeyId)
+ .signatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .signaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
+
+ builder.build().execute();
outStream.close();
@@ -352,33 +337,20 @@ public class KeychainIntentService extends IntentService
switch (target) {
case TARGET_BYTES:
- if (useAsciiArmor) {
- String output = new String(
- ((ByteArrayOutputStream) outStream).toByteArray());
- if (generateSignature) {
- resultData.putString(RESULT_SIGNATURE_STRING, output);
- } else {
- resultData.putString(RESULT_ENCRYPTED_STRING, output);
- }
- } else {
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- if (generateSignature) {
- resultData.putByteArray(RESULT_SIGNATURE_BYTES, output);
- } else {
- resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output);
- }
- }
+ byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
+
+ resultData.putByteArray(RESULT_BYTES, output);
break;
case TARGET_URI:
// nothing, file was written, just send okay
break;
- case TARGET_STREAM:
- String uri = DataStream.buildDataStreamUri(streamFilename).toString();
- resultData.putString(RESULT_URI, uri);
-
- break;
+// case TARGET_STREAM:
+// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
+// resultData.putString(RESULT_URI, uri);
+//
+// break;
}
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -392,15 +364,13 @@ public class KeychainIntentService extends IntentService
/* Input */
int target = data.getInt(TARGET);
- long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
- boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
- boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
- InputStream inStream = null;
- long inLength = -1;
- InputData inputData = null;
- OutputStream outStream = null;
+ InputStream inStream;
+ long inLength;
+ InputData inputData;
+ OutputStream outStream;
String streamFilename = null;
switch (target) {
case TARGET_BYTES: /* decrypting bytes directly */
@@ -435,29 +405,30 @@ public class KeychainIntentService extends IntentService
break;
- case TARGET_STREAM: /* decrypting stream from content uri */
- Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-
- // InputStream
- InputStream in = getContentResolver().openInputStream(providerUri);
- inLength = PgpHelper.getLengthOfStream(in);
- inputData = new InputData(in, inLength);
-
- // OutputStream
- try {
- while (true) {
- streamFilename = PgpHelper.generateRandomFilename(32);
- if (streamFilename == null) {
- throw new PgpGeneralException("couldn't generate random file name");
- }
- openFileInput(streamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-
- break;
+ // TODO: not used, maybe contains code useful for new decrypt method for files?
+// case TARGET_STREAM: /* decrypting stream from content uri */
+// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
+//
+// // InputStream
+// InputStream in = getContentResolver().openInputStream(providerUri);
+// inLength = PgpHelper.getLengthOfStream(in);
+// inputData = new InputData(in, inLength);
+//
+// // OutputStream
+// try {
+// while (true) {
+// streamFilename = PgpHelper.generateRandomFilename(32);
+// if (streamFilename == null) {
+// throw new PgpGeneralException("couldn't generate random file name");
+// }
+// openFileInput(streamFilename).close();
+// }
+// } catch (FileNotFoundException e) {
+// // found a name that isn't used yet
+// }
+// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
+//
+// break;
default:
throw new PgpGeneralException("No target choosen!");
@@ -473,8 +444,8 @@ public class KeychainIntentService extends IntentService
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
builder.progressDialogUpdater(this);
- builder.assumeSymmetric(assumeSymmetricEncryption)
- .passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ builder.allowSymmetricDecryption(true)
+ .passphrase(passphrase);
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
@@ -486,25 +457,18 @@ public class KeychainIntentService extends IntentService
switch (target) {
case TARGET_BYTES:
- if (returnBytes) {
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
- } else {
- String output = new String(
- ((ByteArrayOutputStream) outStream).toByteArray());
- resultData.putString(RESULT_DECRYPTED_STRING, output);
- }
-
+ byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
+ resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
break;
case TARGET_URI:
// nothing, file was written, just send okay and verification bundle
break;
- case TARGET_STREAM:
- String uri = DataStream.buildDataStreamUri(streamFilename).toString();
- resultData.putString(RESULT_URI, uri);
-
- break;
+// case TARGET_STREAM:
+// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
+// resultData.putString(RESULT_URI, uri);
+//
+// break;
}
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -516,38 +480,42 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
- String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE);
- String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE);
+ SaveKeyringParcel saveParams = data.getParcelable(SAVE_KEYRING_PARCEL);
+ String oldPassphrase = saveParams.oldPassphrase;
+ String newPassphrase = saveParams.newPassphrase;
boolean canSign = true;
if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) {
canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN);
}
- if (newPassPhrase == null) {
- newPassPhrase = oldPassPhrase;
+ if (newPassphrase == null) {
+ newPassphrase = oldPassphrase;
}
- ArrayList<String> userIds = data.getStringArrayList(SAVE_KEYRING_USER_IDS);
- ArrayList<PGPSecretKey> keys = PgpConversionHelper.BytesToPGPSecretKeyList(data
- .getByteArray(SAVE_KEYRING_KEYS));
- ArrayList<Integer> keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES);
- ArrayList<GregorianCalendar> keysExpiryDates =
- (ArrayList<GregorianCalendar>) data.getSerializable(SAVE_KEYRING_KEYS_EXPIRY_DATES);
- long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID);
+ long masterKeyId = saveParams.keys.get(0).getKeyID();
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
/* Operation */
if (!canSign) {
- keyOperations.changeSecretKeyPassphrase(
- ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
- oldPassPhrase, newPassPhrase);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
+ keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
+ oldPassphrase, newPassphrase);
+ setProgress(R.string.progress_saving_key_ring, 50, 100);
+ ProviderHelper.saveKeyRing(this, keyRing);
+ setProgress(R.string.progress_done, 100, 100);
} else {
- PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId);
- keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates,
- pubkey, oldPassPhrase, newPassPhrase);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
+ PGPSecretKeyRing privkey = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
+ PGPPublicKeyRing pubkey = ProviderHelper.getPGPPublicKeyRing(this, masterKeyId);
+ PgpKeyOperation.Pair<PGPSecretKeyRing,PGPPublicKeyRing> pair =
+ keyOperations.buildSecretKey(privkey, pubkey, saveParams);
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ // save the pair
+ ProviderHelper.saveKeyRing(this, pair.second, pair.first);
+ setProgress(R.string.progress_done, 100, 100);
}
- PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
+ PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
/* Output */
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@@ -563,7 +531,7 @@ public class KeychainIntentService extends IntentService
boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY);
/* Operation */
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
passphrase, masterKey);
@@ -583,24 +551,37 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
+ ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
+ ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */
- int keysTotal = 2;
+ int keysTotal = 3;
int keysCreated = 0;
setProgress(
getApplicationContext().getResources().
getQuantityString(R.plurals.progress_generating, keysTotal),
keysCreated,
keysTotal);
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, true);
+ newKeys.add(masterKey);
+ keyUsageList.add(KeyFlags.CERTIFY_OTHER);
keysCreated++;
setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, false);
+ newKeys.add(subKey);
+ keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
+ keysCreated++;
+ setProgress(keysCreated, keysTotal);
+
+ subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
+ 4096, passphrase, false);
+ newKeys.add(subKey);
+ keyUsageList.add(KeyFlags.SIGN_DATA);
keysCreated++;
setProgress(keysCreated, keysTotal);
@@ -608,11 +589,11 @@ public class KeychainIntentService extends IntentService
// for sign
/* Output */
+
Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyToBytes(masterKey));
- resultData.putByteArray(RESULT_NEW_KEY2,
- PgpConversionHelper.PGPSecretKeyToBytes(subKey));
+ PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
+ resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -657,53 +638,50 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_EXPORT_KEYRING.equals(action)) {
try {
- /* Input */
- int keyType = Id.type.public_key;
- if (data.containsKey(EXPORT_KEY_TYPE)) {
- keyType = data.getInt(EXPORT_KEY_TYPE);
- }
-
+ boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
+ long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
String outputFile = data.getString(EXPORT_FILENAME);
- long[] rowIds = new long[0];
-
- // If not exporting all keys get the rowIds of the keys to export from the intent
+ // If not exporting all keys get the masterKeyIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL);
- if (!exportAll) {
- rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
- }
-
- /* Operation */
// check if storage is ready
if (!FileHelper.isStorageMounted(outputFile)) {
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
}
- // OutputStream
- FileOutputStream outStream = new FileOutputStream(outputFile);
-
- ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
- if (exportAll) {
+ ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
+ ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
- // get all key ring row ids based on export type
- if (keyType == Id.type.public_key) {
- keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this);
- } else {
- keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this);
- }
- } else {
- for (long rowId : rowIds) {
- keyRingRowIds.add(rowId);
+ String selection = null;
+ if(!exportAll) {
+ selection = KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN( ";
+ for(long l : masterKeyIds) {
+ selection += Long.toString(l) + ",";
}
+ selection = selection.substring(0, selection.length()-1) + " )";
}
- Bundle resultData;
+ Cursor cursor = getContentResolver().query(KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{ KeyRings.MASTER_KEY_ID, KeyRings.HAS_SECRET },
+ selection, null, null);
+ try {
+ cursor.moveToFirst();
+ do {
+ // export public either way
+ publicMasterKeyIds.add(cursor.getLong(0));
+ // add secret if available (and requested)
+ if(exportSecret && cursor.getInt(1) != 0)
+ secretMasterKeyIds.add(cursor.getLong(0));
+ } while(cursor.moveToNext());
+ } finally {
+ cursor.close();
+ }
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
-
- resultData = pgpImportExport
- .exportKeyRings(keyRingRowIds, keyType, outStream);
+ Bundle resultData = pgpImportExport
+ .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
+ new FileOutputStream(outputFile));
if (mIsCanceled) {
boolean isDeleted = new File(outputFile).delete();
@@ -747,45 +725,54 @@ public class KeychainIntentService extends IntentService
HkpKeyServer server = new HkpKeyServer(keyServer);
for (ImportKeysListEntry entry : entries) {
- byte[] downloadedKey = server.get(entry.getKeyId()).getBytes();
-
- /**
- * TODO: copied from ImportKeysListLoader
- *
- *
- * this parses the downloaded key
- */
- // need to have access to the bufferedInput, so we can reuse it for the possible
- // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
- // armor blocks
+ // if available use complete fingerprint for get request
+ byte[] downloadedKeyBytes;
+ if (entry.getFingerPrintHex() != null) {
+ downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
+ } else {
+ downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
+ }
+
+ // create PGPKeyRing object based on downloaded armored key
+ PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
- try {
-
- // read all available blocks... (asc files can contain many blocks with BEGIN END)
- while (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing newKeyring = (PGPKeyRing) obj;
-
- entry.setBytes(newKeyring.getEncoded());
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- }
+ new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
+ if (bufferedInput.available() > 0) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+
+ // get first object in block
+ Object obj;
+ if ((obj = objectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+
+ if (obj instanceof PGPKeyRing) {
+ downloadedKey = (PGPKeyRing) obj;
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
}
+
+ // verify downloaded key by comparing fingerprints
+ if (entry.getFingerPrintHex() != null) {
+ String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
+ downloadedKey.getPublicKey().getFingerprint());
+ if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
+ Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
+ "the requested fingerprint!");
+ } else {
+ throw new PgpGeneralException("fingerprint of downloaded key is " +
+ "NOT the same as the requested fingerprint!");
+ }
+ }
+
+ // save key bytes in entry object for doing the
+ // actual import afterwards
+ entry.setBytes(downloadedKey.getEncoded());
}
+
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
@@ -809,16 +796,24 @@ public class KeychainIntentService extends IntentService
ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);
/* Operation */
- String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
+ String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
+ if (signaturePassphrase == null) {
+ throw new PgpGeneralException("Unable to obtain passphrase");
+ }
- PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
- PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
- userIds, signaturePassPhrase);
+ PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
+ PGPPublicKeyRing publicRing = ProviderHelper.getPGPPublicKeyRing(this, pubKeyId);
+ PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
+ PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(this,
+ masterKeyId);
+ publicKey = keyOperation.certifyKey(certificationKey, publicKey,
+ userIds, signaturePassphrase);
+ publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
// store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- int retval = pgpImportExport.storeKeyRingInCache(signedPubKeyRing);
+ int retval = pgpImportExport.storeKeyRingInCache(publicRing);
if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
throw new PgpGeneralException("Failed to store signed key in local cache");
}
@@ -835,6 +830,10 @@ public class KeychainIntentService extends IntentService
if (this.mIsCanceled) {
return;
}
+ // contextualize the exception, if necessary
+ if (e instanceof PgpGeneralMsgIdException) {
+ e = ((PgpGeneralMsgIdException) e).getContextualized(this);
+ }
Log.e(Constants.TAG, "ApgService Exception: ", e);
e.printStackTrace();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 176d09c1a..962b304c7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -24,19 +24,29 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.*;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.util.LongSparseArray;
import android.util.Log;
-import android.util.LongSparseArray;
+
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import java.util.Date;
@@ -86,7 +96,7 @@ public class PassphraseCacheService extends Service {
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_ADD);
- intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl());
+ intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
intent.putExtra(EXTRA_KEY_ID, keyId);
@@ -161,15 +171,11 @@ public class PassphraseCacheService extends Service {
// try to get master key id which is used as an identifier for cached passphrases
long masterKeyId = keyId;
if (masterKeyId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(this, keyId);
- if (keyRing == null) {
+ masterKeyId = ProviderHelper.getMasterKeyId(this,
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
+ // Failure
+ if(masterKeyId == 0)
return null;
- }
- PGPSecretKey masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey == null) {
- return null;
- }
- masterKeyId = masterKey.getKeyID();
}
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
@@ -202,8 +208,7 @@ public class PassphraseCacheService extends Service {
public static boolean hasPassphrase(Context context, long secretKeyId) {
// check if the key has no passphrase
try {
- PGPSecretKeyRing secRing = ProviderHelper
- .getPGPSecretKeyRingByKeyId(context, secretKeyId);
+ PGPSecretKeyRing secRing = ProviderHelper.getPGPSecretKeyRing(context, secretKeyId);
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = secRing.getSecretKeys(); keys.hasNext(); ) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
new file mode 100644
index 000000000..7c2dcf2c1
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+
+public class SaveKeyringParcel implements Parcelable {
+
+ public ArrayList<String> userIDs;
+ public ArrayList<String> originalIDs;
+ public ArrayList<String> deletedIDs;
+ public boolean[] newIDs;
+ public boolean primaryIDChanged;
+ public boolean[] moddedKeys;
+ public ArrayList<PGPSecretKey> deletedKeys;
+ public ArrayList<GregorianCalendar> keysExpiryDates;
+ public ArrayList<Integer> keysUsages;
+ public String newPassphrase;
+ public String oldPassphrase;
+ public boolean[] newKeys;
+ public ArrayList<PGPSecretKey> keys;
+ public String originalPrimaryID;
+
+ public SaveKeyringParcel() {}
+
+ private SaveKeyringParcel(Parcel source) {
+ userIDs = (ArrayList<String>) source.readSerializable();
+ originalIDs = (ArrayList<String>) source.readSerializable();
+ deletedIDs = (ArrayList<String>) source.readSerializable();
+ newIDs = source.createBooleanArray();
+ primaryIDChanged = source.readByte() != 0;
+ moddedKeys = source.createBooleanArray();
+ byte[] tmp = source.createByteArray();
+ if (tmp == null) {
+ deletedKeys = null;
+ } else {
+ deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ }
+ keysExpiryDates = (ArrayList<GregorianCalendar>) source.readSerializable();
+ keysUsages = source.readArrayList(Integer.class.getClassLoader());
+ newPassphrase = source.readString();
+ oldPassphrase = source.readString();
+ newKeys = source.createBooleanArray();
+ keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
+ originalPrimaryID = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeSerializable(userIDs); //might not be the best method to store.
+ destination.writeSerializable(originalIDs);
+ destination.writeSerializable(deletedIDs);
+ destination.writeBooleanArray(newIDs);
+ destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
+ destination.writeBooleanArray(moddedKeys);
+ byte[] tmp = null;
+ if (deletedKeys.size() != 0) {
+ tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
+ }
+ destination.writeByteArray(tmp);
+ destination.writeSerializable(keysExpiryDates);
+ destination.writeList(keysUsages);
+ destination.writeString(newPassphrase);
+ destination.writeString(oldPassphrase);
+ destination.writeBooleanArray(newKeys);
+ destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
+ destination.writeString(originalPrimaryID);
+ }
+
+ public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
+ public SaveKeyringParcel createFromParcel(final Parcel source) {
+ return new SaveKeyringParcel(source);
+ }
+
+ public SaveKeyringParcel[] newArray(final int size) {
+ return new SaveKeyringParcel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
deleted file mode 100644
index 837295018..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.service.remote;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.widget.*;
-import android.widget.AdapterView.OnItemSelectedListener;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
-import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
-import org.sufficientlysecure.keychain.util.AlgorithmNames;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class AppSettingsFragment extends Fragment implements
- SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
-
- // model
- private AppSettings mAppSettings;
-
- // view
- private LinearLayout mAdvancedSettingsContainer;
- private BootstrapButton mAdvancedSettingsButton;
- private TextView mAppNameView;
- private ImageView mAppIconView;
- private Spinner mEncryptionAlgorithm;
- private Spinner mHashAlgorithm;
- private Spinner mCompression;
- private TextView mPackageName;
- private TextView mPackageSignature;
-
- private SelectSecretKeyLayoutFragment mSelectKeyFragment;
-
- KeyValueSpinnerAdapter mEncryptionAdapter;
- KeyValueSpinnerAdapter mHashAdapter;
- KeyValueSpinnerAdapter mCompressionAdapter;
-
- public AppSettings getAppSettings() {
- return mAppSettings;
- }
-
- public void setAppSettings(AppSettings appSettings) {
- this.mAppSettings = appSettings;
- setPackage(appSettings.getPackageName());
- mPackageName.setText(appSettings.getPackageName());
-
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(appSettings.getPackageSignature());
- byte[] digest = md.digest();
- String signature = new String(Hex.encode(digest));
-
- mPackageSignature.setText(signature);
- } catch (NoSuchAlgorithmException e) {
- Log.e(Constants.TAG, "Should not happen!", e);
- }
-
- mSelectKeyFragment.selectKey(appSettings.getKeyId());
- mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(appSettings
- .getEncryptionAlgorithm()));
- mHashAlgorithm.setSelection(mHashAdapter.getPosition(appSettings.getHashAlgorithm()));
- mCompression.setSelection(mCompressionAdapter.getPosition(appSettings.getCompression()));
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
- initView(view);
- return view;
- }
-
- /**
- * Set error String on key selection
- *
- * @param error
- */
- public void setErrorOnSelectKeyFragment(String error) {
- mSelectKeyFragment.setError(error);
- }
-
- private void initView(View view) {
- mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
- R.id.api_app_settings_select_key_fragment);
- mSelectKeyFragment.setCallback(this);
-
- mAdvancedSettingsButton = (BootstrapButton) view
- .findViewById(R.id.api_app_settings_advanced_button);
- mAdvancedSettingsContainer = (LinearLayout) view
- .findViewById(R.id.api_app_settings_advanced);
-
- mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
- mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
- mEncryptionAlgorithm = (Spinner) view
- .findViewById(R.id.api_app_settings_encryption_algorithm);
- mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
- mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
- mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
- mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
-
- AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
-
- mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getEncryptionNames());
- mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
- mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setEncryptionAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
- mHashAlgorithm.setAdapter(mHashAdapter);
- mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setHashAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getCompressionNames());
- mCompression.setAdapter(mCompressionAdapter);
- mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setCompression((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
- visibleAnimation.setDuration(250);
- final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
- invisibleAnimation.setDuration(250);
-
- // TODO: Better: collapse/expand animation
- // final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f);u
- // animation2.setDuration(150);
-
- mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
- mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.GONE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
- } else {
- mAdvancedSettingsContainer.startAnimation(visibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
- }
- }
- });
- }
-
- private void setPackage(String packageName) {
- PackageManager pm = getActivity().getApplicationContext().getPackageManager();
-
- // get application name and icon from package manager
- String appName = null;
- Drawable appIcon = null;
- try {
- ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
-
- appName = (String) pm.getApplicationLabel(ai);
- appIcon = pm.getApplicationIcon(ai);
- } catch (final NameNotFoundException e) {
- // fallback
- appName = packageName;
- }
- mAppNameView.setText(appName);
- mAppIconView.setImageDrawable(appIcon);
- }
-
- /**
- * callback from select secret key fragment
- */
- @Override
- public void onKeySelected(long secretKeyId) {
- mAppSettings.setKeyId(secretKeyId);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
deleted file mode 100644
index e0dc4162f..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.service.remote;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.support.v4.widget.CursorAdapter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-
-public class RegisteredAppsAdapter extends CursorAdapter {
-
- private LayoutInflater mInflater;
- private PackageManager mPM;
-
- public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
- super(context, c, flags);
-
- mInflater = LayoutInflater.from(context);
- mPM = context.getApplicationContext().getPackageManager();
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
- ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
-
- String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
- if (packageName != null) {
- // get application name
- try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
-
- text.setText(mPM.getApplicationLabel(ai));
- icon.setImageDrawable(mPM.getApplicationIcon(ai));
- } catch (final NameNotFoundException e) {
- // fallback
- text.setText(packageName);
- }
- } else {
- // fallback
- text.setText(packageName);
- }
-
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index f2e6c4bd9..2efa8a69c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011 Senecaso
- *
+ *
* 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
@@ -32,16 +32,20 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -140,8 +144,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
Log.e(Constants.TAG, "uri: " + mDataUri);
- PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);
-
mUserIds = (ListView) findViewById(R.id.user_ids);
mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
@@ -150,20 +152,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements
getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
- if (signKey != null) {
- mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
- }
- if (mPubKeyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- finish();
- return;
- }
}
static final String[] KEYRING_PROJECTION =
new String[] {
KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.Keys.MASTER_KEY_ID,
KeychainContract.Keys.FINGERPRINT,
KeychainContract.UserIds.USER_ID,
};
@@ -184,11 +178,13 @@ public class CertifyKeyActivity extends ActionBarActivity implements
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch(id) {
- case LOADER_ID_KEYRING:
- return new CursorLoader(this, mDataUri, KEYRING_PROJECTION, null, null, null);
+ case LOADER_ID_KEYRING: {
+ Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(this, uri, KEYRING_PROJECTION, null, null, null);
+ }
case LOADER_ID_USER_IDS: {
- Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
- return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
+ Uri uri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
+ return new CursorLoader(this, uri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
}
}
return null;
@@ -200,20 +196,18 @@ public class CertifyKeyActivity extends ActionBarActivity implements
case LOADER_ID_KEYRING:
// the first key here is our master key
if (data.moveToFirst()) {
- long keyId = data.getLong(INDEX_MASTER_KEY_ID);
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
+ // TODO: put findViewById in onCreate!
+ mPubKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(mPubKeyId);
((TextView) findViewById(R.id.key_id)).setText(keyIdStr);
String mainUserId = data.getString(INDEX_USER_ID);
((TextView) findViewById(R.id.main_user_id)).setText(mainUserId);
byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
- if (fingerprintBlob == null) {
- // FALLBACK for old database entries
- fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri);
- }
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
- ((TextView) findViewById(R.id.fingerprint)).setText(OtherHelper.colorizeFingerprint(fingerprint));
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ ((TextView) findViewById(R.id.fingerprint))
+ .setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
}
break;
case LOADER_ID_USER_IDS:
@@ -231,37 +225,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
}
- private void showPassphraseDialog(final long secretKeyId) {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- startSigning();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
- messenger, secretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key!");
- // send message to handler to start certification directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
/**
* handles the UI bits of the signing process on the UI thread
*/
private void initiateSigning() {
- PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId);
+ PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRing(this, mPubKeyId);
if (pubring != null) {
// if we have already signed this key, dont bother doing it again
boolean alreadySigned = false;
@@ -284,7 +252,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
*/
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
- showPassphraseDialog(mMasterKeyId);
+ PassphraseDialogFragment.show(this, mMasterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ startSigning();
+ }
+ }
+ });
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
@@ -307,7 +283,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// Bail out if there is not at least one user id selected
ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
- if(userIds.isEmpty()) {
+ if (userIds.isEmpty()) {
Toast.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
Toast.LENGTH_SHORT).show();
return;
@@ -327,11 +303,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after signing is done in ApgService
+ // Message is received after signing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -380,11 +356,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 3e389c034..8533e9072 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,180 +17,48 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.SuppressLint;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AnimationUtils;
-import android.widget.*;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.devspark.appmsg.AppMsg;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.widget.Toast;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.*;
import java.util.regex.Matcher;
-@SuppressLint("NewApi")
public class DecryptActivity extends DrawerActivity {
/* Intents */
- // without permission
public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT";
/* EXTRA keys for input */
public static final String EXTRA_TEXT = "text";
- private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
- private static final int RESULT_CODE_FILE = 0x00007003;
-
- private long mSignatureKeyId = 0;
-
- private boolean mReturnResult = false;
-
- // TODO: replace signed only checks with something more intelligent
- // PgpDecryptVerify should handle all automatically!!!
- private boolean mSignedOnly = false;
- private boolean mAssumeSymmetricEncryption = false;
-
- private EditText mMessage = null;
- private RelativeLayout mSignatureLayout = null;
- private ImageView mSignatureStatusImage = null;
- private TextView mUserId = null;
- private TextView mUserIdRest = null;
-
- private ViewFlipper mSource = null;
- private TextView mSourceLabel = null;
- private ImageView mSourcePrevious = null;
- private ImageView mSourceNext = null;
-
- private int mDecryptTarget;
-
- private EditText mFilename = null;
- private CheckBox mDeleteAfter = null;
- private BootstrapButton mBrowse = null;
- private BootstrapButton mLookupKey = null;
-
- private String mInputFilename = null;
- private String mOutputFilename = null;
-
- private Uri mContentUri = null;
- private boolean mReturnBinary = false;
-
- private long mSecretKeyId = Id.key.none;
+ ViewPager mViewPager;
+ PagerTabStrip mPagerTabStrip;
+ PagerTabStripAdapter mTabsAdapter;
- private FileDialogFragment mFileDialog;
+ Bundle mMessageFragmentBundle = new Bundle();
+ Bundle mFileFragmentBundle = new Bundle();
+ int mSwitchToTab = PAGER_TAB_MESSAGE;
- private boolean mDecryptImmediately = false;
-
- private BootstrapButton mDecryptButton;
+ private static final int PAGER_TAB_MESSAGE = 0;
+ private static final int PAGER_TAB_FILE = 1;
private void initView() {
- mSource = (ViewFlipper) findViewById(R.id.source);
- mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
- mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
- mSourceNext = (ImageView) findViewById(R.id.sourceNext);
-
- mSourcePrevious.setClickable(true);
- mSourcePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_right_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_right_out));
- mSource.showPrevious();
- updateSource();
- }
- });
-
- mSourceNext.setClickable(true);
- OnClickListener nextSourceClickListener = new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_left_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_left_out));
- mSource.showNext();
- updateSource();
- }
- };
- mSourceNext.setOnClickListener(nextSourceClickListener);
-
- mSourceLabel.setClickable(true);
- mSourceLabel.setOnClickListener(nextSourceClickListener);
+ mViewPager = (ViewPager) findViewById(R.id.decrypt_pager);
+ mPagerTabStrip = (PagerTabStrip) findViewById(R.id.decrypt_pager_tab_strip);
- mMessage = (EditText) findViewById(R.id.message);
- mSignatureLayout = (RelativeLayout) findViewById(R.id.signature);
- mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
- mUserId = (TextView) findViewById(R.id.mainUserId);
- mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
-
- // measure the height of the source_file view and set the message view's min height to that,
- // so it fills mSource fully... bit of a hack.
- View tmp = findViewById(R.id.sourceFile);
- tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- int height = tmp.getMeasuredHeight();
- mMessage.setMinimumHeight(height);
-
- mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
- }
- });
-
- mLookupKey = (BootstrapButton) findViewById(R.id.lookup_key);
- mLookupKey.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- lookupUnknownKey(mSignatureKeyId);
- }
- });
-
- mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption);
-
- // default: message source
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
- mSource.showNext();
- }
-
- mDecryptButton = (BootstrapButton) findViewById(R.id.action_decrypt);
- mDecryptButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- decryptClicked();
- }
- });
+ mTabsAdapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(mTabsAdapter);
}
@Override
@@ -206,68 +74,17 @@ public class DecryptActivity extends DrawerActivity {
setupDrawerNavigation(savedInstanceState);
- // Handle intent actions
+ // Handle intent actions, maybe changes the bundles
handleActions(getIntent());
- if (mSource.getCurrentView().getId() == R.id.sourceMessage
- && mMessage.getText().length() == 0) {
-
- CharSequence clipboardText = ClipboardReflection.getClipboardText(this);
-
- String data = "";
- if (clipboardText != null) {
- Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText);
- if (!matcher.matches()) {
- matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(clipboardText);
- }
- if (matcher.matches()) {
- data = matcher.group(1);
- mMessage.setText(data);
- AppMsg.makeText(this, R.string.using_clipboard_content, AppMsg.STYLE_INFO)
- .show();
- }
- }
- }
-
- mSignatureLayout.setVisibility(View.GONE);
- mSignatureLayout.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if (mSignatureKeyId == 0) {
- return;
- }
- PGPPublicKeyRing key = ProviderHelper.getPGPPublicKeyRingByKeyId(
- DecryptActivity.this, mSignatureKeyId);
- if (key != null) {
- Intent intent = new Intent(DecryptActivity.this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, mSignatureKeyId);
- startActivity(intent);
- }
- }
- });
-
- if (mReturnResult) {
- mSourcePrevious.setClickable(false);
- mSourcePrevious.setEnabled(false);
- mSourcePrevious.setVisibility(View.INVISIBLE);
-
- mSourceNext.setClickable(false);
- mSourceNext.setEnabled(false);
- mSourceNext.setVisibility(View.INVISIBLE);
-
- mSourceLabel.setClickable(false);
- mSourceLabel.setEnabled(false);
- }
-
- updateSource();
-
- if (mDecryptImmediately
- || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText()
- .length() > 0 || mContentUri != null))) {
- decryptClicked();
- }
+ mTabsAdapter.addTab(DecryptMessageFragment.class,
+ mMessageFragmentBundle, getString(R.string.label_message));
+ mTabsAdapter.addTab(DecryptFileFragment.class,
+ mFileFragmentBundle, getString(R.string.label_file));
+ mViewPager.setCurrentItem(mSwitchToTab);
}
+
/**
* Handles all actions with this intent
*
@@ -316,22 +133,26 @@ public class DecryptActivity extends DrawerActivity {
* Main Actions
*/
if (ACTION_DECRYPT.equals(action) && textData != null) {
- Log.d(Constants.TAG, "textData null, matching text ...");
+ Log.d(Constants.TAG, "textData not null, matching text ...");
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(textData);
if (matcher.matches()) {
Log.d(Constants.TAG, "PGP_MESSAGE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
+
+ mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData);
+ mSwitchToTab = PAGER_TAB_MESSAGE;
} else {
- matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(textData);
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(textData);
if (matcher.matches()) {
- Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
+ Log.d(Constants.TAG, "PGP_CLEARTEXT_SIGNATURE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
+
+ mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData);
+ mSwitchToTab = PAGER_TAB_MESSAGE;
} else {
Log.d(Constants.TAG, "Nothing matched!");
}
@@ -341,17 +162,12 @@ public class DecryptActivity extends DrawerActivity {
String path = FileHelper.getPath(this, uri);
if (path != null) {
- mInputFilename = path;
- mFilename.setText(mInputFilename);
- guessOutputFilename();
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
+ mFileFragmentBundle.putString(DecryptFileFragment.ARG_FILENAME, path);
+ mSwitchToTab = PAGER_TAB_FILE;
} else {
Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
+ "Direct binary data without actual file in filesystem is not supported. " +
+ "Please use the Remote Service API!");
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
.show();
// end activity
@@ -363,428 +179,4 @@ public class DecryptActivity extends DrawerActivity {
}
}
- private void guessOutputFilename() {
- mInputFilename = mFilename.getText().toString();
- File file = new File(mInputFilename);
- String filename = file.getName();
- if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
- filename = filename.substring(0, filename.length() - 4);
- }
- mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
- }
-
- private void updateSource() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mSourceLabel.setText(R.string.label_file);
- mDecryptButton.setText(getString(R.string.btn_decrypt));
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- mDecryptButton.setText(getString(R.string.btn_decrypt));
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- private void decryptClicked() {
- if (mSource.getCurrentView().getId() == R.id.sourceFile) {
- mDecryptTarget = Id.target.file;
- } else {
- mDecryptTarget = Id.target.message;
- }
- initiateDecryption();
- }
-
- private void initiateDecryption() {
- if (mDecryptTarget == Id.target.file) {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- guessOutputFilename();
- }
-
- if (mInputFilename.equals("")) {
- AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (mInputFilename.startsWith("file")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
- this,
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- }
- }
-
- if (mDecryptTarget == Id.target.message) {
- String messageData = mMessage.getText().toString();
- Matcher matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(messageData);
- if (matcher.matches()) {
- mSignedOnly = true;
- decryptStart();
- return;
- }
- }
-
- // else treat it as an decrypted message/file
- mSignedOnly = false;
-
- getDecryptionKeyFromInputStream();
-
- // if we need a symmetric passphrase or a passphrase to use a secret key ask for it
- if (mSecretKeyId == Id.key.symmetric
- || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
- showPassphraseDialog();
- } else {
- if (mDecryptTarget == Id.target.file) {
- askForOutputFilename();
- } else { // mDecryptTarget == Id.target.message
- decryptStart();
- }
- }
- }
-
- /**
- * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
- * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
- * for a symmetric passphrase
- */
- private void showPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- if (mDecryptTarget == Id.target.file) {
- askForOutputFilename();
- } else {
- decryptStart();
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
- messenger, mSecretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException 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);
- }
- }
-
- /**
- * TODO: Rework function, remove global variables
- */
- private void getDecryptionKeyFromInputStream() {
- InputStream inStream = null;
- if (mContentUri != null) {
- try {
- inStream = getContentResolver().openInputStream(mContentUri);
- } catch (FileNotFoundException e) {
- Log.e(Constants.TAG, "File not found!", e);
- AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- } else if (mDecryptTarget == Id.target.file) {
- // check if storage is ready
- if (!FileHelper.isStorageMounted(mInputFilename)) {
- AppMsg.makeText(this, getString(R.string.error_external_storage_not_ready),
- AppMsg.STYLE_ALERT).show();
- return;
- }
-
- try {
- inStream = new BufferedInputStream(new FileInputStream(mInputFilename));
- } catch (FileNotFoundException e) {
- Log.e(Constants.TAG, "File not found!", e);
- AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- } finally {
- try {
- if (inStream != null) {
- inStream.close();
- }
- } catch (Exception e) {
- }
- }
- } else {
- inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
- }
-
- // get decryption key for this inStream
- try {
- try {
- if (inStream.markSupported()) {
- inStream.mark(200); // should probably set this to the max size of two pgpF
- // objects, if it even needs to be anything other than 0.
- }
- mSecretKeyId = PgpHelper.getDecryptionKeyId(this, inStream);
- if (mSecretKeyId == Id.key.none) {
- throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
- }
- mAssumeSymmetricEncryption = false;
- } catch (NoAsymmetricEncryptionException e) {
- if (inStream.markSupported()) {
- inStream.reset();
- }
- mSecretKeyId = Id.key.symmetric;
- if (!PgpDecryptVerify.hasSymmetricEncryption(this, inStream)) {
- throw new PgpGeneralException(
- getString(R.string.error_no_known_encryption_found));
- }
- mAssumeSymmetricEncryption = true;
- }
- } catch (Exception e) {
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- }
-
- private void replyClicked() {
- Intent intent = new Intent(this, EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- String data = mMessage.getText().toString();
- data = data.replaceAll("(?m)^", "> ");
- data = "\n\n" + data;
- intent.putExtra(EncryptActivity.EXTRA_TEXT, data);
- intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[]{mSignatureKeyId});
- startActivity(intent);
- }
-
- private void askForOutputFilename() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- decryptStart();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
-
- private void lookupUnknownKey(long unknownKeyId) {
- Intent intent = new Intent(this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
- startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
- }
-
- private void decryptStart() {
- Log.d(Constants.TAG, "decryptStart");
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
-
- // choose action based on input: decrypt stream, file or bytes
- if (mContentUri != null) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_STREAM);
-
- data.putParcelable(KeychainIntentService.ENCRYPT_PROVIDER_URI, mContentUri);
- } else if (mDecryptTarget == Id.target.file) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
-
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
-
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
- } else {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
-
- String message = mMessage.getText().toString();
- data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, message.getBytes());
- }
-
- data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId);
-
- data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary);
- data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- mSignatureKeyId = 0;
- mSignatureLayout.setVisibility(View.GONE);
-
- AppMsg.makeText(DecryptActivity.this, R.string.decryption_successful,
- AppMsg.STYLE_INFO).show();
- if (mReturnResult) {
- Intent intent = new Intent();
- intent.putExtras(returnData);
- setResult(RESULT_OK, intent);
- finish();
- return;
- }
-
- switch (mDecryptTarget) {
- case Id.target.message:
- String decryptedMessage = returnData
- .getString(KeychainIntentService.RESULT_DECRYPTED_STRING);
- mMessage.setText(decryptedMessage);
- mMessage.setHorizontallyScrolling(false);
-
- break;
-
- case Id.target.file:
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
- }
- break;
-
- default:
- // shouldn't happen
- break;
-
- }
-
- PgpDecryptVerifyResult decryptVerifyResult =
- returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
-
- OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
-
- if (signatureResult != null) {
-
- String userId = signatureResult.getUserId();
- mSignatureKeyId = signatureResult.getKeyId();
- mUserIdRest.setText("id: "
- + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
- if (userId == null) {
- userId = getResources().getString(R.string.user_id_no_name);
- }
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mUserIdRest.setText("<" + chunks[1]);
- }
- mUserId.setText(userId);
-
- switch (signatureResult.getStatus()) {
- case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- mLookupKey.setVisibility(View.GONE);
- break;
- }
-
- // TODO!
-// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
-// break;
-// }
-
- case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mLookupKey.setVisibility(View.VISIBLE);
- AppMsg.makeText(DecryptActivity.this,
- R.string.unknown_signature,
- AppMsg.STYLE_ALERT).show();
- break;
- }
-
- default: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mLookupKey.setVisibility(View.GONE);
- break;
- }
- }
- mSignatureLayout.setVisibility(View.VISIBLE);
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case RESULT_CODE_FILE: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = FileHelper.getPath(this, data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
- }
- return;
- }
-
- // this request is returned after LookupUnknownKeyDialogFragment started
- // ImportKeysActivity and user looked uo key
- case RESULT_CODE_LOOKUP_KEY: {
- Log.d(Constants.TAG, "Returning from Lookup Key...");
- if (resultCode == RESULT_OK) {
- // decrypt again
- decryptStart();
- }
- return;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
- }
- }
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
new file mode 100644
index 000000000..492c0cf29
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.File;
+
+public class DecryptFileFragment extends DecryptFragment {
+ public static final String ARG_FILENAME = "filename";
+
+ private static final int RESULT_CODE_FILE = 0x00007003;
+
+ // view
+ private EditText mFilename;
+ private CheckBox mDeleteAfter;
+ private BootstrapButton mBrowse;
+ private BootstrapButton mDecryptButton;
+
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ private FileDialogFragment mFileDialog;
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false);
+
+ mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse);
+ mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
+ mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ RESULT_CODE_FILE);
+ }
+ });
+ mDecryptButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptAction();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String filename = getArguments().getString(ARG_FILENAME);
+ if (filename != null) {
+ mFilename.setText(filename);
+ }
+ }
+
+ private void guessOutputFilename() {
+ mInputFilename = mFilename.getText().toString();
+ File file = new File(mInputFilename);
+ String filename = file.getName();
+ if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
+ filename = filename.substring(0, filename.length() - 4);
+ }
+ mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
+ }
+
+ private void decryptAction() {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ guessOutputFilename();
+ }
+
+ if (mInputFilename.equals("")) {
+ AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (mInputFilename.startsWith("file")) {
+ File file = new File(mInputFilename);
+ if (!file.exists() || !file.isFile()) {
+ AppMsg.makeText(
+ getActivity(),
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
+ .show();
+ return;
+ }
+ }
+
+ askForOutputFilename();
+ }
+
+ private void askForOutputFilename() {
+ // Message is received after passphrase is cached
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ decryptStart(null);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ mFileDialog = FileDialogFragment.newInstance(messenger,
+ getString(R.string.title_decrypt_to_file),
+ getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null);
+
+ mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
+ }
+
+ @Override
+ protected void decryptStart(String passphrase) {
+ Log.d(Constants.TAG, "decryptStart");
+
+ // Send all information needed to service to decrypt in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
+
+ // data
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
+
+ Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
+ + mOutputFilename);
+
+ data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+
+ data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ PgpDecryptVerifyResult decryptVerifyResult =
+ returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
+
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(Id.key.symmetric);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.decryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ if (mDeleteAfter.isChecked()) {
+ // Create and show dialog to delete original file
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ }
+
+ OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+
+ // display signature result in activity
+ onSignatureResult(signatureResult);
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_FILE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = FileHelper.getPath(getActivity(), data.getData());
+ Log.d(Constants.TAG, "path=" + path);
+
+ mFilename.setText(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!");
+ }
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
new file mode 100644
index 000000000..1c465f55c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.app.Fragment;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+
+public class DecryptFragment extends Fragment {
+ private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
+
+ protected long mSignatureKeyId = 0;
+
+ protected RelativeLayout mSignatureLayout = null;
+ protected ImageView mSignatureStatusImage = null;
+ protected TextView mUserId = null;
+ protected TextView mUserIdRest = null;
+
+ protected BootstrapButton mLookupKey = null;
+
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mSignatureLayout = (RelativeLayout) getView().findViewById(R.id.signature);
+ mSignatureStatusImage = (ImageView) getView().findViewById(R.id.ic_signature_status);
+ mUserId = (TextView) getView().findViewById(R.id.mainUserId);
+ mUserIdRest = (TextView) getView().findViewById(R.id.mainUserIdRest);
+ mLookupKey = (BootstrapButton) getView().findViewById(R.id.lookup_key);
+ mLookupKey.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
+ }
+ });
+ mSignatureLayout.setVisibility(View.GONE);
+ mSignatureLayout.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
+ }
+ });
+ }
+
+ private void lookupUnknownKey(long unknownKeyId) {
+ Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
+ startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+
+ case RESULT_CODE_LOOKUP_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // TODO: generate new OpenPgpSignatureResult and display it
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+
+ protected void onSignatureResult(OpenPgpSignatureResult signatureResult) {
+ mSignatureKeyId = 0;
+ mSignatureLayout.setVisibility(View.GONE);
+ if (signatureResult != null) {
+
+ mSignatureKeyId = signatureResult.getKeyId();
+
+ String userId = signatureResult.getUserId();
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
+ if (userIdSplit[0] != null) {
+ mUserId.setText(userId);
+ } else {
+ mUserId.setText(R.string.user_id_no_name);
+ }
+ if (userIdSplit[1] != null) {
+ mUserIdRest.setText(userIdSplit[1]);
+ } else {
+ mUserIdRest.setText(getString(R.string.label_key_id) + ": "
+ + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
+ }
+
+ switch (signatureResult.getStatus()) {
+ case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
+ mLookupKey.setVisibility(View.GONE);
+ break;
+ }
+
+ // TODO!
+// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
+// break;
+// }
+
+ case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.VISIBLE);
+ AppMsg.makeText(getActivity(),
+ R.string.unknown_signature,
+ AppMsg.STYLE_ALERT).show();
+ break;
+ }
+
+ default: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.GONE);
+ break;
+ }
+ }
+ mSignatureLayout.setVisibility(View.VISIBLE);
+ }
+ }
+
+ protected void showPassphraseDialog(long keyId) {
+ PassphraseDialogFragment.show(getActivity(), keyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ String passphrase =
+ message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE);
+ decryptStart(passphrase);
+ }
+ }
+ });
+ }
+
+ /**
+ * Should be overridden by MessageFragment and FileFragment to start actual decryption
+ *
+ * @param passphrase
+ */
+ protected void decryptStart(String passphrase) {
+
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
new file mode 100644
index 000000000..2169bbd77
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.regex.Matcher;
+
+public class DecryptMessageFragment extends DecryptFragment {
+ public static final String ARG_CIPHERTEXT = "ciphertext";
+
+ // view
+ private EditText mMessage;
+ private BootstrapButton mDecryptButton;
+ private BootstrapButton mDecryptFromCLipboardButton;
+
+ // model
+ private String mCiphertext;
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_message_fragment, container, false);
+
+ mMessage = (EditText) view.findViewById(R.id.message);
+ mDecryptButton = (BootstrapButton) view.findViewById(R.id.action_decrypt);
+ mDecryptFromCLipboardButton = (BootstrapButton) view.findViewById(R.id.action_decrypt_from_clipboard);
+ mDecryptButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptClicked();
+ }
+ });
+ mDecryptFromCLipboardButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptFromClipboardClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String ciphertext = getArguments().getString(ARG_CIPHERTEXT);
+ if (ciphertext != null) {
+ mMessage.setText(ciphertext);
+ decryptStart(null);
+ }
+ }
+
+ private void decryptClicked() {
+ mCiphertext = mMessage.getText().toString();
+ decryptStart(null);
+ }
+
+ private void decryptFromClipboardClicked() {
+ CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
+
+ // only decrypt if clipboard content is available and a pgp message or cleartext signature
+ if (clipboardText != null) {
+ Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText);
+ if (!matcher.matches()) {
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText);
+ }
+ if (matcher.matches()) {
+ mCiphertext = matcher.group(1);
+ decryptStart(null);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ .show();
+ }
+ } else {
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ .show();
+ }
+ }
+
+ @Override
+ protected void decryptStart(String passphrase) {
+ Log.d(Constants.TAG, "decryptStart");
+
+ // Send all information needed to service to decrypt in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
+
+ // data
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+ data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
+ data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ PgpDecryptVerifyResult decryptVerifyResult =
+ returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
+
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(Id.key.symmetric);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.decryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ byte[] decryptedMessage = returnData
+ .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
+ mMessage.setText(new String(decryptedMessage));
+ mMessage.setHorizontallyScrolling(false);
+
+ OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+
+ // display signature result in activity
+ onSignatureResult(signatureResult);
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index c0fd53007..f81224380 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -21,19 +21,26 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.FontAwesomeText;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
public class DrawerActivity extends ActionBarActivity {
private DrawerLayout mDrawerLayout;
@@ -42,10 +49,8 @@ public class DrawerActivity extends ActionBarActivity {
private CharSequence mDrawerTitle;
private CharSequence mTitle;
+ private boolean mIsDrawerLocked = false;
- private static Class[] mItemsClass = new Class[]{KeyListActivity.class,
- EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
- RegisteredAppsListActivity.class};
private Class mSelectedItem;
private static final int MENU_ID_PREFERENCE = 222;
@@ -55,10 +60,22 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerTitle = getString(R.string.app_name);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
-
- // set a custom shadow that overlays the main content when the drawer
- // opens
- mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame);
+ int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin;
+ int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size);
+ int errorInMarginAllowed = 5;
+
+ // if the left margin of the loaded layout is close to the
+ // one used in tablets then set drawer as open and locked
+ if (Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList);
+ mDrawerLayout.setScrimColor(Color.TRANSPARENT);
+ mIsDrawerLocked = true;
+ } else {
+ // set a custom shadow that overlays the main content when the drawer opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ mIsDrawerLocked = false;
+ }
NavItem mItemIconTexts[] = new NavItem[]{
new NavItem("fa-user", getString(R.string.nav_contacts)),
@@ -73,8 +90,11 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // if the drawer is not locked
+ if (!mIsDrawerLocked) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
@@ -86,19 +106,8 @@ public class DrawerActivity extends ActionBarActivity {
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
- // creates call to onPrepareOptionsMenu()
- supportInvalidateOptionsMenu();
- // call intent activity if selected
- if (mSelectedItem != null) {
- finish();
- overridePendingTransition(0, 0);
-
- Intent intent = new Intent(DrawerActivity.this, mSelectedItem);
- startActivity(intent);
- // disable animation of activity start
- overridePendingTransition(0, 0);
- }
+ callIntentForDrawerItem(mSelectedItem);
}
public void onDrawerOpened(View drawerView) {
@@ -108,33 +117,56 @@ public class DrawerActivity extends ActionBarActivity {
supportInvalidateOptionsMenu();
}
};
- mDrawerLayout.setDrawerListener(mDrawerToggle);
- // if (savedInstanceState == null) {
- // selectItem(0);
- // }
+ if (!mIsDrawerLocked) {
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+ } else {
+ // If the drawer is locked open make it un-focusable
+ // so that it doesn't consume all the Back button presses
+ mDrawerLayout.setFocusableInTouchMode(false);
+ }
+ }
+
+ /**
+ * Uses startActivity to call the Intent of the given class
+ *
+ * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.*
+ */
+ public void callIntentForDrawerItem(Class drawerItem) {
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
+
+ // call intent activity if selected
+ if (drawerItem != null) {
+ finish();
+ overridePendingTransition(0, 0);
+
+ Intent intent = new Intent(this, drawerItem);
+ startActivity(intent);
+
+ // disable animation of activity start
+ overridePendingTransition(0, 0);
+ }
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
+ if (mDrawerToggle == null) {
+ return super.onCreateOptionsMenu(menu);
+ }
+
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
return super.onCreateOptionsMenu(menu);
}
- /* Called whenever we call invalidateOptionsMenu() */
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- // If the nav drawer is open, hide action items related to the content
- // view
- boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
- // menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
- return super.onPrepareOptionsMenu(menu);
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (mDrawerToggle == null) {
+ return super.onOptionsItemSelected(item);
+ }
+
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
@@ -155,26 +187,11 @@ public class DrawerActivity extends ActionBarActivity {
default:
return super.onOptionsItemSelected(item);
}
-
- // Handle action buttons
- // switch (item.getItemId()) {
- // case R.id.action_websearch:
- // // create intent to perform web search for this planet
- // Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
- // intent.putExtra(SearchManager.QUERY, getSupportActionBar().getTitle());
- // // catch event that there's no activity to handle intent
- // if (intent.resolveActivity(getPackageManager()) != null) {
- // startActivity(intent);
- // } else {
- // Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
- // }
- // return true;
- // default:
- // return super.onOptionsItemSelected(item);
- // }
}
- /* The click listener for ListView in the navigation drawer */
+ /**
+ * The click listener for ListView in the navigation drawer
+ */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -185,10 +202,18 @@ public class DrawerActivity extends ActionBarActivity {
private void selectItem(int position) {
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
- // setTitle(mDrawerTitles[position]);
- mDrawerLayout.closeDrawer(mDrawerList);
// set selected class
- mSelectedItem = mItemsClass[position];
+ mSelectedItem = Constants.DrawerItems.ARRAY[position];
+
+ // setTitle(mDrawerTitles[position]);
+ // If drawer isn't locked just close the drawer and
+ // it will move to the selected item by itself (via drawer toggle listener)
+ if (!mIsDrawerLocked) {
+ mDrawerLayout.closeDrawer(mDrawerList);
+ // else move to the selected item yourself
+ } else {
+ callIntentForDrawerItem(mSelectedItem);
+ }
}
/**
@@ -199,14 +224,18 @@ public class DrawerActivity extends ActionBarActivity {
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
- mDrawerToggle.syncState();
+ if (mDrawerToggle != null) {
+ mDrawerToggle.syncState();
+ }
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
- mDrawerToggle.onConfigurationChanged(newConfig);
+ if (mDrawerToggle != null) {
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
}
private class NavItem {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index edf980773..60bababd1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -27,32 +28,45 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.support.v4.app.ActivityCompat;
import android.support.v7.app.ActionBarActivity;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Toast;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
+import org.sufficientlysecure.keychain.ui.widget.Editor;
+import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
import org.sufficientlysecure.keychain.ui.widget.SectionView;
import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
@@ -61,9 +75,10 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
import java.util.GregorianCalendar;
+import java.util.List;
import java.util.Vector;
-public class EditKeyActivity extends ActionBarActivity {
+public class EditKeyActivity extends ActionBarActivity implements EditorListener {
// Actions for internal use only:
public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
@@ -74,10 +89,6 @@ public class EditKeyActivity extends ActionBarActivity {
public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
- // results when saving key
- public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String RESULT_EXTRA_USER_ID = "user_id";
-
// EDIT
private Uri mDataUri;
@@ -87,9 +98,11 @@ public class EditKeyActivity extends ActionBarActivity {
private SectionView mKeysView;
private String mCurrentPassphrase = null;
- private String mNewPassPhrase = null;
- private String mSavedNewPassPhrase = null;
- private boolean mIsPassPhraseSet;
+ private String mNewPassphrase = null;
+ private String mSavedNewPassphrase = null;
+ private boolean mIsPassphraseSet;
+ private boolean mNeedsSaving;
+ private boolean mIsBrandNewKeyring = false;
private BootstrapButton mChangePassphrase;
@@ -102,12 +115,37 @@ public class EditKeyActivity extends ActionBarActivity {
ExportHelper mExportHelper;
+ public boolean needsSaving() {
+ mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving();
+ mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving();
+ mNeedsSaving |= hasPassphraseChanged();
+ mNeedsSaving |= mIsBrandNewKeyring;
+ return mNeedsSaving;
+ }
+
+
+ public void somethingChanged() {
+ ActivityCompat.invalidateOptionsMenu(this);
+ }
+
+ public void onDeleted(Editor e, boolean wasNewItem) {
+ somethingChanged();
+ }
+
+ public void onEdited() {
+ somethingChanged();
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mExportHelper = new ExportHelper(this);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setIcon(android.R.color.transparent);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
mUserIds = new Vector<String>();
mKeys = new Vector<PGPSecretKey>();
mKeysUsages = new Vector<Integer>();
@@ -128,24 +166,10 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveClicked();
- }
- }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancelClicked();
- }
- }
- );
-
Bundle extras = intent.getExtras();
mCurrentPassphrase = "";
+ mIsBrandNewKeyring = true;
if (extras != null) {
// if userId is given, prefill the fields
@@ -180,7 +204,7 @@ public class EditKeyActivity extends ActionBarActivity {
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after generating is done in ApgService
+ // Message is received after generating is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this, getResources().getQuantityString(R.plurals.progress_generating, 1),
ProgressDialog.STYLE_HORIZONTAL, true,
@@ -197,28 +221,28 @@ public class EditKeyActivity extends ActionBarActivity {
@Override
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get new key from data bundle returned from service
Bundle data = message.getData();
- PGPSecretKey masterKey = (PGPSecretKey) PgpConversionHelper
- .BytesToPGPSecretKey(data
+
+ ArrayList<PGPSecretKey> newKeys =
+ PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
- PGPSecretKey subKey = (PGPSecretKey) PgpConversionHelper
- .BytesToPGPSecretKey(data
- .getByteArray(KeychainIntentService.RESULT_NEW_KEY2));
- // add master key
- mKeys.add(masterKey);
- mKeysUsages.add(Id.choice.usage.sign_only); //TODO: get from key flags
+ ArrayList<Integer> keyUsageFlags = data.getIntegerArrayList(
+ KeychainIntentService.RESULT_KEY_USAGES);
- // add sub key
- mKeys.add(subKey);
- mKeysUsages.add(Id.choice.usage.encrypt_only); //TODO: get from key flags
+ if (newKeys.size() == keyUsageFlags.size()) {
+ for (int i = 0; i < newKeys.size(); ++i) {
+ mKeys.add(newKeys.get(i));
+ mKeysUsages.add(keyUsageFlags.get(i));
+ }
+ }
- buildLayout();
+ buildLayout(true);
}
}
};
@@ -234,7 +258,7 @@ public class EditKeyActivity extends ActionBarActivity {
}
}
} else {
- buildLayout();
+ buildLayout(false);
}
}
@@ -244,67 +268,16 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionEditKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveClicked();
- }
- });
-
mDataUri = intent.getData();
if (mDataUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
finish();
- return;
} else {
Log.d(Constants.TAG, "uri: " + mDataUri);
// get master key id using row id
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
-
- mMasterCanSign = ProviderHelper.getMasterKeyCanSign(this, mDataUri);
- finallyEdit(masterKeyId, mMasterCanSign);
- }
- }
-
- private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- String passphrase = PassphraseCacheService.getCachedPassphrase(
- EditKeyActivity.this, masterKeyId);
- mCurrentPassphrase = passphrase;
- finallySaveClicked();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
- EditKeyActivity.this, messenger, masterKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key!");
- // send message to handler to start encryption directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- // show menu only on edit
- if (mDataUri != null) {
- return super.onPrepareOptionsMenu(menu);
- } else {
- return false;
+ finallyEdit(masterKeyId);
}
}
@@ -312,45 +285,66 @@ public class EditKeyActivity extends ActionBarActivity {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.key_edit, menu);
+ //totally get rid of some actions for new keys
+ if (mDataUri == null) {
+ MenuItem mButton = menu.findItem(R.id.menu_key_edit_export_file);
+ mButton.setVisible(false);
+ mButton = menu.findItem(R.id.menu_key_edit_delete);
+ mButton.setVisible(false);
+ }
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case android.R.id.home:
+ cancelClicked();
+ // TODO: why isn't this triggered on my tablet - one of many ui problems
+ // I've had with this device. A code compatibility issue or a Samsung fail?
+ return true;
case R.id.menu_key_edit_cancel:
cancelClicked();
return true;
case R.id.menu_key_edit_export_file:
- long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
- mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC);
+ if (needsSaving()) {
+ Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
+ } else {
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ mExportHelper.showExportKeysDialog(
+ new long[] { masterKeyId }, Constants.Path.APP_DIR_FILE_SEC, true);
+ return true;
+ }
+ return true;
+ case R.id.menu_key_edit_delete:
+ Uri convertUri = KeyRingData.buildSecretKeyRingUri(mDataUri);
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }};
+ mExportHelper.deleteKey(convertUri, returnHandler);
return true;
- case R.id.menu_key_edit_delete: {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
- mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
+ case R.id.menu_key_edit_save:
+ saveClicked();
return true;
- }
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("unchecked")
- private void finallyEdit(final long masterKeyId, final boolean masterCanSign) {
+ private void finallyEdit(final long masterKeyId) {
if (masterKeyId != 0) {
PGPSecretKey masterKey = null;
- mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
+ mKeyRing = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
if (mKeyRing != null) {
- masterKey = PgpKeyHelper.getMasterKey(mKeyRing);
+ masterKey = mKeyRing.getSecretKey();
+ mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey());
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) {
mKeys.add(key);
mKeysUsages.add(-1); // get usage when view is created
@@ -358,20 +352,29 @@ public class EditKeyActivity extends ActionBarActivity {
} else {
Log.e(Constants.TAG, "Keyring not found with masterKeyId: " + masterKeyId);
Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_LONG).show();
+ // TODO
}
if (masterKey != null) {
+ boolean isSet = false;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
Log.d(Constants.TAG, "Added userId " + userId);
+ if (!isSet) {
+ isSet = true;
+ String[] parts = PgpKeyHelper.splitUserId(userId);
+ if (parts[0] != null) {
+ setTitle(parts[0]);
+ }
+ }
mUserIds.add(userId);
}
}
}
mCurrentPassphrase = "";
+ buildLayout(false);
- buildLayout();
- mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
- if (!mIsPassPhraseSet) {
+ mIsPassphraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
+ if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
mChangePassphrase.setVisibility(View.GONE);
@@ -390,10 +393,11 @@ public class EditKeyActivity extends ActionBarActivity {
Bundle data = message.getData();
// set new returned passphrase!
- mNewPassPhrase = data
+ mNewPassphrase = data
.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
- updatePassPhraseButtonText();
+ updatePassphraseButtonText();
+ somethingChanged();
}
}
};
@@ -402,7 +406,7 @@ public class EditKeyActivity extends ActionBarActivity {
Messenger messenger = new Messenger(returnHandler);
// set title based on isPassphraseSet()
- int title = -1;
+ int title;
if (isPassphraseSet()) {
title = R.string.title_change_passphrase;
} else {
@@ -418,30 +422,37 @@ public class EditKeyActivity extends ActionBarActivity {
/**
* Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user
* id and key.
+ *
+ * @param newKeys
*/
- private void buildLayout() {
+ private void buildLayout(boolean newKeys) {
setContentView(R.layout.edit_key_activity);
// find views
mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase);
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
-
// Build layout based on given userIds and keys
+
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
+ if (mIsPassphraseSet) {
+ mChangePassphrase.setText(getString(R.string.btn_change_passphrase));
+ }
mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
mUserIdsView.setType(Id.type.user_id);
- mUserIdsView.setCanEdit(mMasterCanSign);
+ mUserIdsView.setCanBeEdited(mMasterCanSign);
mUserIdsView.setUserIds(mUserIds);
+ mUserIdsView.setEditorListener(this);
container.addView(mUserIdsView);
mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
mKeysView.setType(Id.type.key);
- mKeysView.setCanEdit(mMasterCanSign);
- mKeysView.setKeys(mKeys, mKeysUsages);
+ mKeysView.setCanBeEdited(mMasterCanSign);
+ mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
+ mKeysView.setEditorListener(this);
container.addView(mKeysView);
- updatePassPhraseButtonText();
+ updatePassphraseButtonText();
mChangePassphrase.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@@ -449,20 +460,21 @@ public class EditKeyActivity extends ActionBarActivity {
}
});
- // disable passphrase when no passphrase checkobox is checked!
+ // disable passphrase when no passphrase checkbox is checked!
mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// remove passphrase
- mSavedNewPassPhrase = mNewPassPhrase;
- mNewPassPhrase = "";
+ mSavedNewPassphrase = mNewPassphrase;
+ mNewPassphrase = "";
mChangePassphrase.setVisibility(View.GONE);
} else {
- mNewPassPhrase = mSavedNewPassPhrase;
+ mNewPassphrase = mSavedNewPassphrase;
mChangePassphrase.setVisibility(View.VISIBLE);
}
+ somethingChanged();
}
});
}
@@ -477,37 +489,116 @@ public class EditKeyActivity extends ActionBarActivity {
public boolean isPassphraseSet() {
if (mNoPassphrase.isChecked()) {
return true;
- } else if ((mIsPassPhraseSet)
- || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) {
+ } else if ((mIsPassphraseSet)
+ || (mNewPassphrase != null && !mNewPassphrase.equals(""))) {
return true;
} else {
return false;
}
}
- private void saveClicked() {
- long masterKeyId = getMasterKeyId();
- try {
- if (!isPassphraseSet()) {
- throw new PgpGeneralException(this.getString(R.string.set_a_passphrase));
+ public boolean hasPassphraseChanged() {
+ if (mNoPassphrase != null) {
+ if (mNoPassphrase.isChecked()) {
+ return mIsPassphraseSet;
+ } else {
+ return (mNewPassphrase != null && !mNewPassphrase.equals(""));
}
+ } else {
+ return false;
+ }
+ }
- String passphrase = null;
- if (mIsPassPhraseSet) {
- passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- } else {
- passphrase = "";
+ private void saveClicked() {
+ final long masterKeyId = getMasterKeyId();
+ if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu
+ try {
+ if (!isPassphraseSet()) {
+ throw new PgpGeneralException(this.getString(R.string.set_a_passphrase));
+ }
+
+ String passphrase;
+ if (mIsPassphraseSet) {
+ passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
+ } else {
+ passphrase = "";
+ }
+ if (passphrase == null) {
+ PassphraseDialogFragment.show(this, masterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(
+ EditKeyActivity.this, masterKeyId);
+ checkEmptyIDsWanted();
+ }
+ }
+ });
+ } else {
+ mCurrentPassphrase = passphrase;
+ checkEmptyIDsWanted();
+ }
+ } catch (PgpGeneralException e) {
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).show();
}
- if (passphrase == null) {
- showPassphraseDialog(masterKeyId, mMasterCanSign);
- } else {
- mCurrentPassphrase = passphrase;
- finallySaveClicked();
+ } else {
+ AppMsg.makeText(this, R.string.error_change_something_first, AppMsg.STYLE_ALERT).show();
+ }
+ }
+
+ private void checkEmptyIDsWanted() {
+ try {
+ ArrayList<String> userIDs = getUserIds(mUserIdsView);
+ List<Boolean> newIDs = mUserIdsView.getNewIDFlags();
+ ArrayList<String> originalIDs = mUserIdsView.getOriginalIDs();
+ int curID = 0;
+ for (String userID : userIDs) {
+ if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) {
+ AlertDialog.Builder alert = new AlertDialog.Builder(
+ EditKeyActivity.this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(EditKeyActivity.this.getString(R.string.ask_empty_id_ok));
+
+ alert.setPositiveButton(EditKeyActivity.this.getString(android.R.string.yes),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ finallySaveClicked();
+ }
+ }
+ );
+ alert.setNegativeButton(this.getString(android.R.string.no),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ }
+ );
+ alert.setCancelable(false);
+ alert.create().show();
+ return;
+ }
+ curID++;
}
} catch (PgpGeneralException e) {
- //Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
- // Toast.LENGTH_SHORT).show();
+ Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).show();
}
+ finallySaveClicked();
+ }
+
+ private boolean[] toPrimitiveArray(final List<Boolean> booleanList) {
+ final boolean[] primitives = new boolean[booleanList.size()];
+ int index = 0;
+ for (Boolean object : booleanList) {
+ primitives[index++] = object;
+ }
+ return primitives;
}
private void finallySaveClicked() {
@@ -517,42 +608,45 @@ public class EditKeyActivity extends ActionBarActivity {
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
+ SaveKeyringParcel saveParams = new SaveKeyringParcel();
+ saveParams.userIDs = getUserIds(mUserIdsView);
+ saveParams.originalIDs = mUserIdsView.getOriginalIDs();
+ saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
+ saveParams.newIDs = toPrimitiveArray(mUserIdsView.getNewIDFlags());
+ saveParams.primaryIDChanged = mUserIdsView.primaryChanged();
+ saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray());
+ saveParams.deletedKeys = mKeysView.getDeletedKeys();
+ saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView);
+ saveParams.keysUsages = getKeysUsages(mKeysView);
+ saveParams.newPassphrase = mNewPassphrase;
+ saveParams.oldPassphrase = mCurrentPassphrase;
+ saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray());
+ saveParams.keys = getKeys(mKeysView);
+ saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
+
+
// fill values for this action
Bundle data = new Bundle();
- data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE,
- mCurrentPassphrase);
- data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase);
- data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS,
- getUserIds(mUserIdsView));
- ArrayList<PGPSecretKey> keys = getKeys(mKeysView);
- data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS,
- PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
- data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES,
- getKeysUsages(mKeysView));
- data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES,
- getKeysExpiryDates(mKeysView));
- data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId());
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
+ data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after saving is done in ApgService
+ // Message is received after saving is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, getMasterKeyId());
- ArrayList<String> userIds = null;
- try {
- userIds = getUserIds(mUserIdsView);
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, "exception while getting user ids", e);
- }
- data.putExtra(RESULT_EXTRA_USER_ID, userIds.get(0));
+
+ // return uri pointing to new created key
+ Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri(
+ String.valueOf(getMasterKeyId()));
+ data.setData(uri);
+
setResult(RESULT_OK, data);
finish();
}
@@ -568,14 +662,42 @@ public class EditKeyActivity extends ActionBarActivity {
// start service with intent
startService(intent);
} catch (PgpGeneralException e) {
- //Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
- // Toast.LENGTH_SHORT).show();
+ Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).show();
}
}
private void cancelClicked() {
- setResult(RESULT_CANCELED);
- finish();
+ if (needsSaving()) { //ask if we want to save
+ AlertDialog.Builder alert = new AlertDialog.Builder(
+ EditKeyActivity.this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(EditKeyActivity.this.getString(R.string.ask_save_changed_key));
+
+ alert.setPositiveButton(EditKeyActivity.this.getString(android.R.string.yes),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ saveClicked();
+ }
+ });
+ alert.setNegativeButton(this.getString(android.R.string.no),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+ alert.setCancelable(false);
+ alert.create().show();
+ } else {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
/**
@@ -592,19 +714,8 @@ public class EditKeyActivity extends ActionBarActivity {
boolean gotMainUserId = false;
for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
- String userId = null;
- try {
- userId = editor.getValue();
- } catch (UserIdEditor.NoNameException e) {
- throw new PgpGeneralException(this.getString(R.string.error_user_id_needs_a_name));
- } catch (UserIdEditor.NoEmailException e) {
- throw new PgpGeneralException(
- this.getString(R.string.error_user_id_needs_an_email_address));
- }
-
- if (userId.equals("")) {
- continue;
- }
+ String userId;
+ userId = editor.getValue();
if (editor.isMainUserId()) {
userIds.add(0, userId);
@@ -688,7 +799,7 @@ public class EditKeyActivity extends ActionBarActivity {
return keysExpiryDates;
}
- private void updatePassPhraseButtonText() {
+ private void updatePassphraseButtonText() {
mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
: getString(R.string.btn_set_passphrase));
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 1231b6209..a03c7d797 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -17,49 +17,25 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.*;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.beardedhen.androidbootstrap.FontAwesomeText;
-import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.widget.Toast;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.File;
-import java.util.Vector;
-
-public class EncryptActivity extends DrawerActivity {
+public class EncryptActivity extends DrawerActivity implements
+ EncryptSymmetricFragment.OnSymmetricKeySelection,
+ EncryptAsymmetricFragment.OnAsymmetricKeySelection,
+ EncryptActivityInterface {
/* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT";
@@ -74,57 +50,94 @@ public class EncryptActivity extends DrawerActivity {
public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id";
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids";
+ // view
+ ViewPager mViewPagerMode;
+ PagerTabStrip mPagerTabStripMode;
+ PagerTabStripAdapter mTabsAdapterMode;
+ ViewPager mViewPagerContent;
+ PagerTabStrip mPagerTabStripContent;
+ PagerTabStripAdapter mTabsAdapterContent;
+
+ // tabs
+ Bundle mAsymmetricFragmentBundle = new Bundle();
+ Bundle mSymmetricFragmentBundle = new Bundle();
+ Bundle mMessageFragmentBundle = new Bundle();
+ Bundle mFileFragmentBundle = new Bundle();
+ int mSwitchToMode = PAGER_MODE_ASYMMETRIC;
+ int mSwitchToContent = PAGER_CONTENT_MESSAGE;
+
+ private static final int PAGER_MODE_ASYMMETRIC = 0;
+ private static final int PAGER_MODE_SYMMETRIC = 1;
+ private static final int PAGER_CONTENT_MESSAGE = 0;
+ private static final int PAGER_CONTENT_FILE = 1;
+
+ // model useb by message and file fragment
private long mEncryptionKeyIds[] = null;
+ private long mSigningKeyId = Id.key.none;
+ private String mPassphrase;
+ private String mPassphraseAgain;
- private EditText mMessage = null;
- private BootstrapButton mSelectKeysButton = null;
-
- private CheckBox mSign = null;
- private TextView mMainUserId = null;
- private TextView mMainUserIdRest = null;
-
- private ViewFlipper mSource = null;
- private TextView mSourceLabel = null;
- private ImageView mSourcePrevious = null;
- private ImageView mSourceNext = null;
+ @Override
+ public void onSigningKeySelected(long signingKeyId) {
+ mSigningKeyId = signingKeyId;
+ }
- private ViewFlipper mMode = null;
- private TextView mModeLabel = null;
- private ImageView mModePrevious = null;
- private ImageView mModeNext = null;
+ @Override
+ public void onEncryptionKeysSelected(long[] encryptionKeyIds) {
+ mEncryptionKeyIds = encryptionKeyIds;
+ }
- private int mEncryptTarget;
+ @Override
+ public void onPassphraseUpdate(String passphrase) {
+ mPassphrase = passphrase;
+ }
- private EditText mPassphrase = null;
- private EditText mPassphraseAgain = null;
- private CheckBox mAsciiArmor = null;
- private Spinner mFileCompression = null;
+ @Override
+ public void onPassphraseAgainUpdate(String passphrase) {
+ mPassphraseAgain = passphrase;
+ }
- private EditText mFilename = null;
- private CheckBox mDeleteAfter = null;
- private CheckBox mShareAfter = null;
- private BootstrapButton mBrowse = null;
+ @Override
+ public boolean isModeSymmetric() {
+ if (PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
- private String mInputFilename = null;
- private String mOutputFilename = null;
+ @Override
+ public long getSignatureKey() {
+ return mSigningKeyId;
+ }
- private Integer mShortAnimationDuration = null;
- private boolean mFileAdvancedSettingsVisible = false;
- private TextView mFileAdvancedSettings = null;
- private LinearLayout mFileAdvancedSettingsContainer = null;
- private FontAwesomeText mAdvancedSettingsIcon;
- private boolean mAsciiArmorDemand = false;
- private boolean mOverrideAsciiArmor = false;
+ @Override
+ public long[] getEncryptionKeys() {
+ return mEncryptionKeyIds;
+ }
- private boolean mGenerateSignature = false;
+ @Override
+ public String getPassphrase() {
+ return mPassphrase;
+ }
- private long mSecretKeyId = Id.key.none;
+ @Override
+ public String getPassphraseAgain() {
+ return mPassphraseAgain;
+ }
- private FileDialogFragment mFileDialog;
- private BootstrapButton mEncryptShare;
- private BootstrapButton mEncryptClipboard;
- private BootstrapButton mEncryptFile;
+ private void initView() {
+ mViewPagerMode = (ViewPager) findViewById(R.id.encrypt_pager_mode);
+ mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode);
+ mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content);
+ mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content);
+
+ mTabsAdapterMode = new PagerTabStripAdapter(this);
+ mViewPagerMode.setAdapter(mTabsAdapterMode);
+ mTabsAdapterContent = new PagerTabStripAdapter(this);
+ mViewPagerContent.setAdapter(mTabsAdapterContent);
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -142,14 +155,17 @@ public class EncryptActivity extends DrawerActivity {
// Handle intent actions
handleActions(getIntent());
- updateView();
- updateSource();
- updateMode();
-
- updateActionBarButtons();
-
- // retrieve and cache the system's short animation time
- mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+ mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class,
+ mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
+ mTabsAdapterMode.addTab(EncryptSymmetricFragment.class,
+ mSymmetricFragmentBundle, getString(R.string.label_symmetric));
+ mViewPagerMode.setCurrentItem(mSwitchToMode);
+
+ mTabsAdapterContent.addTab(EncryptMessageFragment.class,
+ mMessageFragmentBundle, getString(R.string.label_message));
+ mTabsAdapterContent.addTab(EncryptFileFragment.class,
+ mFileFragmentBundle, getString(R.string.label_file));
+ mViewPagerContent.setCurrentItem(mSwitchToContent);
}
/**
@@ -190,9 +206,8 @@ public class EncryptActivity extends DrawerActivity {
}
if (extras.containsKey(EXTRA_ASCII_ARMOR)) {
- mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
- mOverrideAsciiArmor = true;
- mAsciiArmor.setChecked(mAsciiArmorDemand);
+ boolean requestAsciiArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
+ mFileFragmentBundle.putBoolean(EncryptFileFragment.ARG_ASCII_ARMOR, requestAsciiArmor);
}
String textData = extras.getString(EXTRA_TEXT);
@@ -201,20 +216,19 @@ public class EncryptActivity extends DrawerActivity {
long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
// preselect keys given by intent
- preselectKeys(signatureKeyId, encryptionKeyIds);
+ mAsymmetricFragmentBundle.putLongArray(EncryptAsymmetricFragment.ARG_ENCRYPTION_KEY_IDS,
+ encryptionKeyIds);
+ mAsymmetricFragmentBundle.putLong(EncryptAsymmetricFragment.ARG_SIGNATURE_KEY_ID,
+ signatureKeyId);
+ mSwitchToMode = PAGER_MODE_ASYMMETRIC;
/**
* Main Actions
*/
if (ACTION_ENCRYPT.equals(action) && textData != null) {
// encrypt text based on given extra
-
- mMessage.setText(textData);
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
- mSource.showNext();
- }
+ mMessageFragmentBundle.putString(EncryptMessageFragment.ARG_TEXT, textData);
+ mSwitchToContent = PAGER_CONTENT_MESSAGE;
} else if (ACTION_ENCRYPT.equals(action) && uri != null) {
// encrypt file based on Uri
@@ -222,17 +236,12 @@ public class EncryptActivity extends DrawerActivity {
String path = FileHelper.getPath(this, uri);
if (path != null) {
- mInputFilename = path;
- mFilename.setText(mInputFilename);
-
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
+ mFileFragmentBundle.putString(EncryptFileFragment.ARG_FILENAME, path);
+ mSwitchToContent = PAGER_CONTENT_FILE;
} else {
Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported by Intents. Please use the Remote Service API!");
+ "Direct binary data without actual file in filesystem is not supported " +
+ "by Intents. Please use the Remote Service API!");
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
.show();
// end activity
@@ -244,779 +253,4 @@ public class EncryptActivity extends DrawerActivity {
}
}
- /**
- * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those!
- *
- * @param preselectedSignatureKeyId
- * @param preselectedEncryptionKeyIds
- */
- private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) {
- if (preselectedSignatureKeyId != 0) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this,
- preselectedSignatureKeyId);
- PGPSecretKey masterKey = null;
- if (keyRing != null) {
- masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey != null) {
- Vector<PGPSecretKey> signKeys = PgpKeyHelper.getUsableSigningKeys(keyRing);
- if (signKeys.size() > 0) {
- mSecretKeyId = masterKey.getKeyID();
- }
- }
- }
- }
-
- if (preselectedEncryptionKeyIds != null) {
- Vector<Long> goodIds = new Vector<Long>();
- for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this,
- preselectedEncryptionKeyIds[i]);
- PGPPublicKey masterKey = null;
- if (keyRing == null) {
- continue;
- }
- masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey == null) {
- continue;
- }
- Vector<PGPPublicKey> encryptKeys = PgpKeyHelper.getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- continue;
- }
- goodIds.add(masterKey.getKeyID());
- }
- if (goodIds.size() > 0) {
- mEncryptionKeyIds = new long[goodIds.size()];
- for (int i = 0; i < goodIds.size(); ++i) {
- mEncryptionKeyIds[i] = goodIds.get(i);
- }
- }
- }
- }
-
- /**
- * Guess output filename based on input path
- *
- * @param path
- * @return Suggestion for output filename
- */
- private String guessOutputFilename(String path) {
- // output in the same directory but with additional ending
- File file = new File(path);
- String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
- String outputFilename = file.getParent() + File.separator + file.getName() + ending;
-
- return outputFilename;
- }
-
- private void updateSource() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mSourceLabel.setText(R.string.label_file);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- break;
- }
-
- default: {
- break;
- }
- }
- updateActionBarButtons();
- }
-
- /**
- * Update ActionBar buttons based on current selection in view
- */
- private void updateActionBarButtons() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mEncryptShare.setVisibility(View.GONE);
- mEncryptClipboard.setVisibility(View.GONE);
- mEncryptFile.setVisibility(View.VISIBLE);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
-
- mEncryptShare.setVisibility(View.VISIBLE);
- mEncryptClipboard.setVisibility(View.VISIBLE);
- mEncryptFile.setVisibility(View.GONE);
-
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- } else {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- if (mSecretKeyId == 0) {
- mEncryptShare.setEnabled(false);
- mEncryptClipboard.setEnabled(false);
- } else {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- }
- } else {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- }
- }
- break;
- }
-
- default: {
- break;
- }
- }
-
- }
-
- private void updateMode() {
- switch (mMode.getCurrentView().getId()) {
- case R.id.modeAsymmetric: {
- mModeLabel.setText(R.string.label_asymmetric);
- break;
- }
-
- case R.id.modeSymmetric: {
- mModeLabel.setText(R.string.label_symmetric);
- break;
- }
-
- default: {
- break;
- }
- }
- updateActionBarButtons();
- }
-
- private void encryptToClipboardClicked() {
- mEncryptTarget = Id.target.clipboard;
- initiateEncryption();
- }
-
- private void encryptClicked() {
- Log.d(Constants.TAG, "encryptClicked invoked!");
-
- if (mSource.getCurrentView().getId() == R.id.sourceFile) {
- mEncryptTarget = Id.target.file;
- } else {
- mEncryptTarget = Id.target.email;
- }
- initiateEncryption();
- }
-
- private void initiateEncryption() {
- if (mEncryptTarget == Id.target.file) {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- mInputFilename = mFilename.getText().toString();
- }
-
- mOutputFilename = guessOutputFilename(mInputFilename);
-
- if (mInputFilename.equals("")) {
- AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (!mInputFilename.startsWith("content")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
- this,
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- }
- }
-
- // symmetric encryption
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- boolean gotPassPhrase = false;
- String passphrase = mPassphrase.getText().toString();
- String passphraseAgain = mPassphraseAgain.getText().toString();
- if (!passphrase.equals(passphraseAgain)) {
- AppMsg.makeText(this, R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- gotPassPhrase = (passphrase.length() != 0);
- if (!gotPassPhrase) {
- AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- } else {
- boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);
- // for now require at least one form of encryption for files
- if (!encryptIt && mEncryptTarget == Id.target.file) {
- AppMsg.makeText(this, R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (!encryptIt && mSecretKeyId == 0) {
- AppMsg.makeText(this, R.string.select_encryption_or_signature_key,
- AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (mSecretKeyId != 0
- && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
- showPassphraseDialog();
-
- return;
- }
- }
-
- if (mEncryptTarget == Id.target.file) {
- showOutputFileDialog();
- } else {
- encryptStart();
- }
- }
-
- /**
- * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
- * encryption
- */
- private void showPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- if (mEncryptTarget == Id.target.file) {
- showOutputFileDialog();
- } else {
- encryptStart();
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
- EncryptActivity.this, messenger, mSecretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException 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 showOutputFileDialog() {
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- encryptStart();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_encrypt_to_file),
- getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
-
- private void encryptStart() {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- boolean useAsciiArmor = true;
- long encryptionKeyIds[] = null;
- int compressionId = 0;
- boolean signOnly = false;
- long mSecretKeyIdToPass = 0;
-
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- String passphrase = mPassphrase.getText().toString();
- if (passphrase.length() == 0) {
- passphrase = null;
- }
- data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
- } else {
- mSecretKeyIdToPass = mSecretKeyId;
- encryptionKeyIds = mEncryptionKeyIds;
- signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
- }
-
- intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
-
- // choose default settings, target and data bundle by target
- if (mEncryptTarget == Id.target.file) {
- useAsciiArmor = mAsciiArmor.isChecked();
- compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
-
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
-
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
-
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
-
- } else {
- useAsciiArmor = true;
- compressionId = Preferences.getPreferences(this).getDefaultMessageCompression();
-
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
-
- String message = mMessage.getText().toString();
- if (signOnly) {
- fixBadCharactersForGmail(message);
- }
- data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
- }
-
- if (mOverrideAsciiArmor) {
- useAsciiArmor = mAsciiArmorDemand;
- }
-
- data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyIdToPass);
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds);
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
- data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
- data.putBoolean(KeychainIntentService.ENCRYPT_SIGN_ONLY, signOnly);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle data = message.getData();
-
- String output;
- switch (mEncryptTarget) {
- case Id.target.clipboard:
- output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
- Log.d(Constants.TAG, "output: " + output);
- ClipboardReflection.copyToClipboard(EncryptActivity.this, output);
- AppMsg.makeText(EncryptActivity.this,
- R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
- .show();
- break;
-
- case Id.target.email:
-
- output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
- Log.d(Constants.TAG, "output: " + output);
-
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
-
- // Type is set to text/plain so that encrypted messages can
- // be sent with Whatsapp, Hangouts, SMS etc...
- sendIntent.setType("text/plain");
-
- sendIntent.putExtra(Intent.EXTRA_TEXT, output);
- startActivity(Intent.createChooser(sendIntent,
- getString(R.string.title_send_email)));
- break;
-
- case Id.target.file:
- AppMsg.makeText(EncryptActivity.this, R.string.encryption_successful,
- AppMsg.STYLE_INFO).show();
-
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
- }
-
- if (mShareAfter.isChecked()) {
- // Share encrypted file
- Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
- sendFileIntent.setType("*/*");
- sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
- startActivity(Intent.createChooser(sendFileIntent,
- getString(R.string.title_send_file)));
- }
- break;
-
- default:
- // shouldn't happen
- break;
-
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- /**
- * Fixes bad message characters for gmail
- *
- * @param message
- * @return
- */
- private String fixBadCharactersForGmail(String message) {
- // fix the message a bit, trailing spaces and newlines break stuff,
- // because GMail sends as HTML and such things fuck up the
- // signature,
- // TODO: things like "<" and ">" also fuck up the signature
- message = message.replaceAll(" +\n", "\n");
- message = message.replaceAll("\n\n+", "\n\n");
- message = message.replaceFirst("^\n+", "");
- // make sure there'll be exactly one newline at the end
- message = message.replaceFirst("\n*$", "\n");
-
- return message;
- }
-
- private void initView() {
- mSource = (ViewFlipper) findViewById(R.id.source);
- mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
- mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
- mSourceNext = (ImageView) findViewById(R.id.sourceNext);
-
- mSourcePrevious.setClickable(true);
- mSourcePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_out));
- mSource.showPrevious();
- updateSource();
- }
- });
-
- mSourceNext.setClickable(true);
- OnClickListener nextSourceClickListener = new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_out));
- mSource.showNext();
- updateSource();
- }
- };
- mSourceNext.setOnClickListener(nextSourceClickListener);
-
- mSourceLabel.setClickable(true);
- mSourceLabel.setOnClickListener(nextSourceClickListener);
-
- mMode = (ViewFlipper) findViewById(R.id.mode);
- mModeLabel = (TextView) findViewById(R.id.modeLabel);
- mModePrevious = (ImageView) findViewById(R.id.modePrevious);
- mModeNext = (ImageView) findViewById(R.id.modeNext);
-
- mModePrevious.setClickable(true);
- mModePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_in));
- mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_out));
- mMode.showPrevious();
- updateMode();
- }
- });
-
- OnClickListener nextModeClickListener = new OnClickListener() {
- public void onClick(View v) {
- mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_in));
- mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_out));
- mMode.showNext();
- updateMode();
- }
- };
- mModeNext.setOnClickListener(nextModeClickListener);
-
- mModeLabel.setClickable(true);
- mModeLabel.setOnClickListener(nextModeClickListener);
-
- mMessage = (EditText) findViewById(R.id.message);
- mSelectKeysButton = (BootstrapButton) findViewById(R.id.btn_selectEncryptKeys);
- mSign = (CheckBox) findViewById(R.id.sign);
- mMainUserId = (TextView) findViewById(R.id.mainUserId);
- mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
-
- mPassphrase = (EditText) findViewById(R.id.passphrase);
- mPassphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
-
- // measure the height of the source_file view and set the message view's min height to that,
- // so it fills mSource fully... bit of a hack.
- View tmp = findViewById(R.id.sourceFile);
- tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- int height = tmp.getMeasuredHeight();
- mMessage.setMinimumHeight(height);
-
- mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*",
- Id.request.filename);
- }
- });
-
- mAdvancedSettingsIcon = (FontAwesomeText) findViewById(R.id.advancedSettingsIcon);
- mFileAdvancedSettingsContainer = (LinearLayout) findViewById(R.id.fileAdvancedSettingsContainer);
- mFileAdvancedSettings = (TextView) findViewById(R.id.advancedSettings);
-
- LinearLayout advancedSettingsControl = (LinearLayout) findViewById(R.id.advancedSettingsControl);
- advancedSettingsControl.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mFileAdvancedSettingsVisible = !mFileAdvancedSettingsVisible;
- if (mFileAdvancedSettingsVisible) {
- mAdvancedSettingsIcon.setIcon("fa-chevron-down");
- mFileAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- AlphaAnimation animation = new AlphaAnimation(0f, 1f);
- animation.setDuration(mShortAnimationDuration);
- mFileAdvancedSettingsContainer.startAnimation(animation);
- mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_hide);
-
- } else {
- mAdvancedSettingsIcon.setIcon("fa-chevron-right");
- AlphaAnimation animation = new AlphaAnimation(1f, 0f);
- animation.setDuration(mShortAnimationDuration);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- // do nothing
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- // making sure that at the end the container is completely removed from view
- mFileAdvancedSettingsContainer.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // do nothing
- }
- });
- mFileAdvancedSettingsContainer.startAnimation(animation);
- mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_show);
- }
- }
- });
-
- mFileCompression = (Spinner) findViewById(R.id.fileCompression);
- Choice[] choices = new Choice[]{
- new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zip, "ZIP ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zlib, "ZLIB ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.bzip2, "BZIP2 ("
- + getString(R.string.compression_very_slow) + ")"), };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mFileCompression.setAdapter(adapter);
-
- int defaultFileCompression = Preferences.getPreferences(this).getDefaultFileCompression();
- for (int i = 0; i < choices.length; ++i) {
- if (choices[i].getId() == defaultFileCompression) {
- mFileCompression.setSelection(i);
- break;
- }
- }
-
- mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
- mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
-
- mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
- mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
-
- mSelectKeysButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- selectPublicKeys();
- }
- });
-
- mSign.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- CheckBox checkBox = (CheckBox) v;
- if (checkBox.isChecked()) {
- selectSecretKey();
- } else {
- mSecretKeyId = Id.key.none;
- updateView();
- }
- }
- });
-
- mEncryptClipboard = (BootstrapButton) findViewById(R.id.action_encrypt_clipboard);
- mEncryptClipboard.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptToClipboardClicked();
- }
- });
- mEncryptShare = (BootstrapButton) findViewById(R.id.action_encrypt_share);
- mEncryptShare.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptClicked();
- }
- });
- mEncryptFile = (BootstrapButton) findViewById(R.id.action_encrypt_file);
- mEncryptFile.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptClicked();
- }
- });
- }
-
- private void updateView() {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
- } else {
- mSelectKeysButton.setText(getResources().getQuantityString(
- R.plurals.select_keys_button, mEncryptionKeyIds.length,
- mEncryptionKeyIds.length));
- }
-
- if (mSecretKeyId == Id.key.none) {
- mSign.setChecked(false);
- mMainUserId.setText("");
- mMainUserIdRest.setText("");
- } else {
- String uid = getResources().getString(R.string.user_id_no_name);
- String uidExtra = "";
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this,
- mSecretKeyId);
- if (keyRing != null) {
- PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
- if (key != null) {
- String userId = PgpKeyHelper.getMainUserIdSafe(this, key);
- String chunks[] = userId.split(" <", 2);
- uid = chunks[0];
- if (chunks.length > 1) {
- uidExtra = "<" + chunks[1];
- }
- }
- }
- mMainUserId.setText(uid);
- mMainUserIdRest.setText(uidExtra);
- mSign.setChecked(true);
- }
-
- updateActionBarButtons();
- }
-
- private void selectPublicKeys() {
- Intent intent = new Intent(this, SelectPublicKeyActivity.class);
- Vector<Long> keyIds = new Vector<Long>();
- if (mSecretKeyId != 0) {
- keyIds.add(mSecretKeyId);
- }
- if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
- for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
- keyIds.add(mEncryptionKeyIds[i]);
- }
- }
- long[] initialKeyIds = null;
- if (keyIds.size() > 0) {
- initialKeyIds = new long[keyIds.size()];
- for (int i = 0; i < keyIds.size(); ++i) {
- initialKeyIds[i] = keyIds.get(i);
- }
- }
- intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
- startActivityForResult(intent, Id.request.public_keys);
- }
-
- private void selectSecretKey() {
- Intent intent = new Intent(this, SelectSecretKeyActivity.class);
- startActivityForResult(intent, Id.request.secret_keys);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = FileHelper.getPath(this, data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
- }
- return;
- }
-
- case Id.request.public_keys: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mEncryptionKeyIds = bundle
- .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS);
- }
- updateView();
- break;
- }
-
- case Id.request.secret_keys: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
- } else {
- mSecretKeyId = Id.key.none;
- }
- updateView();
- break;
- }
-
- default: {
- break;
- }
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
new file mode 100644
index 000000000..0786b3a16
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+public interface EncryptActivityInterface {
+
+ public boolean isModeSymmetric();
+
+ public long getSignatureKey();
+ public long[] getEncryptionKeys();
+
+ public String getPassphrase();
+ public String getPassphraseAgain();
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
new file mode 100644
index 000000000..6e84211cc
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+
+import java.util.HashMap;
+import java.util.Vector;
+
+public class EncryptAsymmetricFragment extends Fragment {
+ public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
+ public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
+
+ public static final int RESULT_CODE_PUBLIC_KEYS = 0x00007001;
+ public static final int RESULT_CODE_SECRET_KEYS = 0x00007002;
+
+ OnAsymmetricKeySelection mKeySelectionListener;
+
+ // view
+ private BootstrapButton mSelectKeysButton;
+ private CheckBox mSign;
+ private TextView mMainUserId;
+ private TextView mMainUserIdRest;
+
+ // model
+ private long mSecretKeyId = Id.key.none;
+ private long mEncryptionKeyIds[] = null;
+
+ // Container Activity must implement this interface
+ public interface OnAsymmetricKeySelection {
+ public void onSigningKeySelected(long signingKeyId);
+
+ public void onEncryptionKeysSelected(long[] encryptionKeyIds);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mKeySelectionListener = (OnAsymmetricKeySelection) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement OnAsymmetricKeySelection");
+ }
+ }
+
+ private void setSignatureKeyId(long signatureKeyId) {
+ mSecretKeyId = signatureKeyId;
+ // update key selection in EncryptActivity
+ mKeySelectionListener.onSigningKeySelected(signatureKeyId);
+ updateView();
+ }
+
+ private void setEncryptionKeyIds(long[] encryptionKeyIds) {
+ mEncryptionKeyIds = encryptionKeyIds;
+ // update key selection in EncryptActivity
+ mKeySelectionListener.onEncryptionKeysSelected(encryptionKeyIds);
+ updateView();
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
+
+ mSelectKeysButton = (BootstrapButton) view.findViewById(R.id.btn_selectEncryptKeys);
+ mSign = (CheckBox) view.findViewById(R.id.sign);
+ mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mSelectKeysButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ selectPublicKeys();
+ }
+ });
+ mSign.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkBox = (CheckBox) v;
+ if (checkBox.isChecked()) {
+ selectSecretKey();
+ } else {
+ setSignatureKeyId(Id.key.none);
+ }
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ long signatureKeyId = getArguments().getLong(ARG_SIGNATURE_KEY_ID);
+ long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
+
+ // preselect keys given by arguments (given by Intent to EncryptActivity)
+ preselectKeys(signatureKeyId, encryptionKeyIds);
+ }
+
+ /**
+ * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those!
+ *
+ * @param preselectedSignatureKeyId
+ * @param preselectedEncryptionKeyIds
+ */
+ private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) {
+ if (preselectedSignatureKeyId != 0) {
+ // TODO: don't use bouncy castle objects!
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingWithKeyId(getActivity(),
+ preselectedSignatureKeyId);
+ PGPSecretKey masterKey;
+ if (keyRing != null) {
+ masterKey = keyRing.getSecretKey();
+ if (masterKey != null) {
+ Vector<PGPSecretKey> signKeys = PgpKeyHelper.getUsableSigningKeys(keyRing);
+ if (signKeys.size() > 0) {
+ setSignatureKeyId(masterKey.getKeyID());
+ }
+ }
+ }
+ }
+
+ if (preselectedEncryptionKeyIds != null) {
+ Vector<Long> goodIds = new Vector<Long>();
+ for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
+ long id = ProviderHelper.getMasterKeyId(getActivity(),
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(preselectedEncryptionKeyIds[i]))
+ );
+ // TODO check for available encrypt keys... is this even relevant?
+ goodIds.add(id);
+ }
+ if (goodIds.size() > 0) {
+ long[] keyIds = new long[goodIds.size()];
+ for (int i = 0; i < goodIds.size(); ++i) {
+ keyIds[i] = goodIds.get(i);
+ }
+ setEncryptionKeyIds(keyIds);
+ }
+ }
+ }
+
+ private void updateView() {
+ if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
+ mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
+ } else {
+ mSelectKeysButton.setText(getResources().getQuantityString(
+ R.plurals.select_keys_button, mEncryptionKeyIds.length,
+ mEncryptionKeyIds.length));
+ }
+
+ if (mSecretKeyId == Id.key.none) {
+ mSign.setChecked(false);
+ mMainUserId.setText("");
+ mMainUserIdRest.setText("");
+ } else {
+ String uid = getResources().getString(R.string.user_id_no_name);
+ String uidExtra = "";
+ // See if we can get a user_id from a unified query
+ String user_id = (String) ProviderHelper.getUnifiedData(
+ getActivity(), mSecretKeyId, KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ if(user_id != null) {
+ String chunks[] = user_id.split(" <", 2);
+ uid = chunks[0];
+ if (chunks.length > 1) {
+ uidExtra = "<" + chunks[1];
+ }
+ }
+
+ mMainUserId.setText(uid);
+ mMainUserIdRest.setText(uidExtra);
+ mSign.setChecked(true);
+ }
+ }
+
+ private void selectPublicKeys() {
+ Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
+ Vector<Long> keyIds = new Vector<Long>();
+ if (mSecretKeyId != 0) {
+ keyIds.add(mSecretKeyId);
+ }
+ if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
+ for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
+ keyIds.add(mEncryptionKeyIds[i]);
+ }
+ }
+ long[] initialKeyIds = null;
+ if (keyIds.size() > 0) {
+ initialKeyIds = new long[keyIds.size()];
+ for (int i = 0; i < keyIds.size(); ++i) {
+ initialKeyIds[i] = keyIds.get(i);
+ }
+ }
+ intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
+ startActivityForResult(intent, Id.request.public_keys);
+ }
+
+ private void selectSecretKey() {
+ Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
+ startActivityForResult(intent, Id.request.secret_keys);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_PUBLIC_KEYS: {
+ if (resultCode == Activity.RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ setEncryptionKeyIds(bundle
+ .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS));
+ }
+ break;
+ }
+
+ case RESULT_CODE_SECRET_KEYS: {
+ if (resultCode == Activity.RESULT_OK) {
+ Uri uriMasterKey = data.getData();
+ setSignatureKeyId(Long.valueOf(uriMasterKey.getLastPathSegment()));
+ } else {
+ setSignatureKeyId(Id.key.none);
+ }
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
new file mode 100644
index 000000000..470c85715
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.File;
+
+public class EncryptFileFragment extends Fragment {
+ public static final String ARG_FILENAME = "filename";
+ public static final String ARG_ASCII_ARMOR = "ascii_armor";
+
+ private static final int RESULT_CODE_FILE = 0x00007003;
+
+ private EncryptActivityInterface mEncryptInterface;
+
+ // view
+ private CheckBox mAsciiArmor = null;
+ private Spinner mFileCompression = null;
+ private EditText mFilename = null;
+ private CheckBox mDeleteAfter = null;
+ private CheckBox mShareAfter = null;
+ private BootstrapButton mBrowse = null;
+ private BootstrapButton mEncryptFile;
+
+ private FileDialogFragment mFileDialog;
+
+ // model
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mEncryptInterface = (EncryptActivityInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false);
+
+ mEncryptFile = (BootstrapButton) view.findViewById(R.id.action_encrypt_file);
+ mEncryptFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked();
+ }
+ });
+
+ mFilename = (EditText) view.findViewById(R.id.filename);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ Id.request.filename);
+ }
+ });
+
+ mFileCompression = (Spinner) view.findViewById(R.id.fileCompression);
+ Choice[] choices = new Choice[] {
+ new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zip, "ZIP ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zlib, "ZLIB ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2 ("
+ + getString(R.string.compression_very_slow) + ")"),
+ };
+ ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getActivity(),
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mFileCompression.setAdapter(adapter);
+
+ int defaultFileCompression = Preferences.getPreferences(getActivity()).getDefaultFileCompression();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultFileCompression) {
+ mFileCompression.setSelection(i);
+ break;
+ }
+ }
+
+ mDeleteAfter = (CheckBox) view.findViewById(R.id.deleteAfterEncryption);
+ mShareAfter = (CheckBox) view.findViewById(R.id.shareAfterEncryption);
+
+ mAsciiArmor = (CheckBox) view.findViewById(R.id.asciiArmor);
+ mAsciiArmor.setChecked(Preferences.getPreferences(getActivity()).getDefaultAsciiArmor());
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String filename = getArguments().getString(ARG_FILENAME);
+ if (filename != null) {
+ mFilename.setText(filename);
+ }
+ boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR);
+ if (asciiArmor) {
+ mAsciiArmor.setChecked(asciiArmor);
+ }
+ }
+
+ /**
+ * Guess output filename based on input path
+ *
+ * @param path
+ * @return Suggestion for output filename
+ */
+ private String guessOutputFilename(String path) {
+ // output in the same directory but with additional ending
+ File file = new File(path);
+ String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
+ String outputFilename = file.getParent() + File.separator + file.getName() + ending;
+
+ return outputFilename;
+ }
+
+ private void showOutputFileDialog() {
+ // Message is received after file is selected
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ encryptStart();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ mFileDialog = FileDialogFragment.newInstance(messenger,
+ getString(R.string.title_encrypt_to_file),
+ getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
+
+ mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
+ }
+
+ private void encryptClicked() {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ mInputFilename = mFilename.getText().toString();
+ }
+
+ mOutputFilename = guessOutputFilename(mInputFilename);
+
+ if (mInputFilename.equals("")) {
+ AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (!mInputFilename.startsWith("content")) {
+ File file = new File(mInputFilename);
+ if (!file.exists() || !file.isFile()) {
+ AppMsg.makeText(
+ getActivity(),
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
+ .show();
+ return;
+ }
+ }
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ // symmetric encryption
+
+ boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
+ && mEncryptInterface.getPassphrase().length() != 0);
+ if (!gotPassphrase) {
+ AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
+ .show();
+ return;
+ }
+
+ if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
+ AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+ } else {
+ // asymmetric encryption
+
+ boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
+ && mEncryptInterface.getEncryptionKeys().length > 0);
+
+ if (!gotEncryptionKeys) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
+ AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (mEncryptInterface.getSignatureKey() != 0 &&
+ PassphraseCacheService.getCachedPassphrase(getActivity(),
+ mEncryptInterface.getSignatureKey()) == null) {
+ PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ showOutputFileDialog();
+ }
+ }
+ });
+
+ return;
+ }
+ }
+
+ showOutputFileDialog();
+ }
+
+ private void encryptStart() {
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ Log.d(Constants.TAG, "Symmetric encryption enabled!");
+ String passphrase = mEncryptInterface.getPassphrase();
+ if (passphrase.length() == 0) {
+ passphrase = null;
+ }
+ data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ } else {
+ data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
+ mEncryptInterface.getSignatureKey());
+ data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
+ mEncryptInterface.getEncryptionKeys());
+ }
+
+ Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
+ + mOutputFilename);
+
+ data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+
+ boolean useAsciiArmor = mAsciiArmor.isChecked();
+ data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
+
+ int compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
+// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ AppMsg.makeText(getActivity(), R.string.encryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ if (mDeleteAfter.isChecked()) {
+ // Create and show dialog to delete original file
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ }
+
+ if (mShareAfter.isChecked()) {
+ // Share encrypted file
+ Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
+ sendFileIntent.setType("*/*");
+ sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
+ startActivity(Intent.createChooser(sendFileIntent,
+ getString(R.string.title_send_file)));
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_FILE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = FileHelper.getPath(getActivity(), data.getData());
+ Log.d(Constants.TAG, "path=" + path);
+
+ mFilename.setText(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!");
+ }
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
new file mode 100644
index 000000000..ba11074fc
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class EncryptMessageFragment extends Fragment {
+ public static final String ARG_TEXT = "text";
+
+ private EditText mMessage = null;
+ private BootstrapButton mEncryptShare;
+ private BootstrapButton mEncryptClipboard;
+
+ private EncryptActivityInterface mEncryptInterface;
+
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mEncryptInterface = (EncryptActivityInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
+
+ mMessage = (EditText) view.findViewById(R.id.message);
+ mEncryptClipboard = (BootstrapButton) view.findViewById(R.id.action_encrypt_clipboard);
+ mEncryptShare = (BootstrapButton) view.findViewById(R.id.action_encrypt_share);
+ mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked(true);
+ }
+ });
+ mEncryptShare.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked(false);
+ }
+ });
+
+ return view;
+ }
+
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String text = getArguments().getString(ARG_TEXT);
+ if (text != null) {
+ mMessage.setText(text);
+ }
+ }
+
+ /**
+ * Fixes bad message characters for gmail
+ *
+ * @param message
+ * @return
+ */
+ private String fixBadCharactersForGmail(String message) {
+ // fix the message a bit, trailing spaces and newlines break stuff,
+ // because GMail sends as HTML and such things fuck up the
+ // signature,
+ // TODO: things like "<" and ">" also fuck up the signature
+ message = message.replaceAll(" +\n", "\n");
+ message = message.replaceAll("\n\n+", "\n\n");
+ message = message.replaceFirst("^\n+", "");
+ // make sure there'll be exactly one newline at the end
+ message = message.replaceFirst("\n*$", "\n");
+
+ return message;
+ }
+
+ private void encryptClicked(final boolean toClipboard) {
+ if (mEncryptInterface.isModeSymmetric()) {
+ // symmetric encryption
+
+ boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
+ && mEncryptInterface.getPassphrase().length() != 0);
+ if (!gotPassphrase) {
+ AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
+ .show();
+ return;
+ }
+
+ if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
+ AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ } else {
+ // asymmetric encryption
+
+ boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
+ && mEncryptInterface.getEncryptionKeys().length > 0);
+
+ if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
+ AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (mEncryptInterface.getSignatureKey() != 0 &&
+ PassphraseCacheService.getCachedPassphrase(getActivity(),
+ mEncryptInterface.getSignatureKey()) == null) {
+ PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ encryptStart(toClipboard);
+ }
+ }
+ });
+
+ return;
+ }
+ }
+
+ encryptStart(toClipboard);
+ }
+
+ private void encryptStart(final boolean toClipboard) {
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+
+ String message = mMessage.getText().toString();
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ Log.d(Constants.TAG, "Symmetric encryption enabled!");
+ String passphrase = mEncryptInterface.getPassphrase();
+ if (passphrase.length() == 0) {
+ passphrase = null;
+ }
+ data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ } else {
+ data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
+ mEncryptInterface.getSignatureKey());
+ data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
+ mEncryptInterface.getEncryptionKeys());
+
+ boolean signOnly = (mEncryptInterface.getEncryptionKeys() == null
+ || mEncryptInterface.getEncryptionKeys().length == 0);
+ if (signOnly) {
+ message = fixBadCharactersForGmail(message);
+ }
+ }
+
+ data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
+
+ data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
+
+ int compressionId = Preferences.getPreferences(getActivity()).getDefaultMessageCompression();
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
+// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle data = message.getData();
+
+ String output = new String(data.getByteArray(KeychainIntentService.RESULT_BYTES));
+ Log.d(Constants.TAG, "output: " + output);
+
+ if (toClipboard) {
+ ClipboardReflection.copyToClipboard(getActivity(), output);
+ AppMsg.makeText(getActivity(),
+ R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
+ .show();
+ } else {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+
+ // Type is set to text/plain so that encrypted messages can
+ // be sent with Whatsapp, Hangouts, SMS etc...
+ sendIntent.setType("text/plain");
+
+ sendIntent.putExtra(Intent.EXTRA_TEXT, output);
+ startActivity(Intent.createChooser(sendIntent,
+ getString(R.string.title_send_email)));
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
new file mode 100644
index 000000000..8efa07953
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+
+public class EncryptSymmetricFragment extends Fragment {
+
+ OnSymmetricKeySelection mPassphraseUpdateListener;
+
+ private EditText mPassphrase;
+ private EditText mPassphraseAgain;
+
+ // Container Activity must implement this interface
+ public interface OnSymmetricKeySelection {
+ public void onPassphraseUpdate(String passphrase);
+
+ public void onPassphraseAgainUpdate(String passphrase);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mPassphraseUpdateListener = (OnSymmetricKeySelection) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement OnSymmetricKeySelection");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_symmetric_fragment, container, false);
+
+ mPassphrase = (EditText) view.findViewById(R.id.passphrase);
+ mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
+ mPassphrase.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // update passphrase in EncryptActivity
+ mPassphraseUpdateListener.onPassphraseUpdate(s.toString());
+ }
+ });
+ mPassphraseAgain.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // update passphrase in EncryptActivity
+ mPassphraseUpdateListener.onPassphraseAgainUpdate(s.toString());
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 05bfc613e..9b8b92136 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011 Senecaso
- *
+ *
* 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
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
@@ -34,8 +35,10 @@ import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -54,7 +57,6 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
+ "IMPORT_KEY_FROM_QR_CODE";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEYSERVER";
- // TODO: implement:
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
@@ -72,6 +74,10 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_FINGERPRINT = "fingerprint";
+ // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN when used from OpenPgpService
+ public static final String EXTRA_PENDING_INTENT_DATA = "data";
+ private Intent mPendingIntentData;
+
// view
private ImportKeysListFragment mListFragment;
private String[] mNavigationStrings;
@@ -86,7 +92,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
ImportKeysNFCFragment.class
};
- private int mCurrentNavPostition = -1;
+ private int mCurrentNavPosition = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -102,17 +108,22 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
}
});
- getSupportActionBar().setDisplayShowTitleEnabled(false);
+ mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
- setupDrawerNavigation(savedInstanceState);
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
+ setTitle(R.string.nav_import);
+ } else {
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
- // set drop down navigation
- mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
- Context context = getSupportActionBar().getThemedContext();
- ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
- R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
- getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
+ setupDrawerNavigation(savedInstanceState);
+
+ // set drop down navigation
+ Context context = getSupportActionBar().getThemedContext();
+ ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
+ R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
+ getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
+ }
handleActions(savedInstanceState, getIntent());
}
@@ -152,33 +163,52 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// action: directly load data
startListFragment(savedInstanceState, importData, null, null);
}
- } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
- String query = null;
- if (extras.containsKey(EXTRA_QUERY)) {
- query = extras.getString(EXTRA_QUERY);
- } else if (extras.containsKey(EXTRA_KEY_ID)) {
- long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
- if (keyId != 0) {
- query = PgpKeyHelper.convertKeyIdToHex(keyId);
+ } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)
+ || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(action)) {
+
+ // only used for OpenPgpService
+ if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) {
+ mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA);
+ }
+ if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) {
+ /* simple search based on query or key id */
+
+ String query = null;
+ if (extras.containsKey(EXTRA_QUERY)) {
+ query = extras.getString(EXTRA_QUERY);
+ } else if (extras.containsKey(EXTRA_KEY_ID)) {
+ long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
+ if (keyId != 0) {
+ query = PgpKeyHelper.convertKeyIdToHex(keyId);
+ }
+ }
+
+ if (query != null && query.length() > 0) {
+ // display keyserver fragment with query
+ Bundle args = new Bundle();
+ args.putString(ImportKeysServerFragment.ARG_QUERY, query);
+ loadNavFragment(0, args);
+
+ // action: search immediately
+ startListFragment(savedInstanceState, null, null, query);
+ } else {
+ Log.e(Constants.TAG, "Query is empty!");
+ return;
}
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
+ /*
+ * search based on fingerprint, here we can enforce a check in the end
+ * if the right key has been downloaded
+ */
+
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
- if (fingerprint != null) {
- query = "0x" + fingerprint;
- }
+ loadFromFingerprint(savedInstanceState, fingerprint);
} else {
Log.e(Constants.TAG,
- "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
+ "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
+ "'fingerprint' extra!");
return;
}
-
- // display keyserver fragment with query
- Bundle args = new Bundle();
- args.putString(ImportKeysServerFragment.ARG_QUERY, query);
- loadNavFragment(0, args);
-
- // action: search immediately
- startListFragment(savedInstanceState, null, null, query);
} else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
// NOTE: this only displays the appropriate fragment, no actions are taken
@@ -233,14 +263,14 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
* onNavigationItemSelected() should check whether the Fragment is already in existence
* inside your Activity."
* <p/>
- * from http://stackoverflow.com/questions/10983396/fragment-oncreateview-and-onactivitycreated-called-twice/14295474#14295474
+ * from http://stackoverflow.com/a/14295474
* <p/>
* In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint,
* the fragment would be loaded twice resulting in the query being empty after the second load.
* <p/>
* Our solution:
* To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment
- * checks against mCurrentNavPostition.
+ * checks against mCurrentNavPosition.
*
* @param itemPosition
* @param itemId
@@ -256,10 +286,12 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
}
private void loadNavFragment(int itemPosition, Bundle args) {
- if (mCurrentNavPostition != itemPosition) {
- getSupportActionBar().setSelectedNavigationItem(itemPosition);
+ if (mCurrentNavPosition != itemPosition) {
+ if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) {
+ getSupportActionBar().setSelectedNavigationItem(itemPosition);
+ }
loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]);
- mCurrentNavPostition = itemPosition;
+ mCurrentNavPosition = itemPosition;
}
}
@@ -279,7 +311,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
- if (fingerprint.length() < 16) {
+ loadFromFingerprint(savedInstanceState, fingerprint);
+ }
+
+ public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) {
+ if (fingerprint == null || fingerprint.length() < 40) {
AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint,
AppMsg.STYLE_ALERT).show();
return;
@@ -290,6 +326,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
+ args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true);
loadNavFragment(0, args);
// action: search directly
@@ -300,70 +337,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
mListFragment.loadNew(importData, dataUri, serverQuery, keyServer);
}
- // 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.convertFingerprintToHex(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();
- // }
- // }
-
-
/**
* Import keys with mImportData
*/
public void importKeys() {
- // Message is received after importing is done in ApgService
+ // Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
@@ -403,6 +381,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
BadImportKeyDialogFragment.newInstance(bad);
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
}
+
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
+ ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData);
+ finish();
+ }
}
}
};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index 3f0b4a46e..28e2091a9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -24,9 +25,13 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import java.util.Locale;
+
public class ImportKeysClipboardFragment extends Fragment {
private ImportKeysActivity mImportActivity;
@@ -60,6 +65,10 @@ public class ImportKeysClipboardFragment extends Fragment {
String sendText = "";
if (clipboardText != null) {
sendText = clipboardText.toString();
+ if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
+ mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText));
+ return;
+ }
}
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 9e8506193..077fa0cab 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -29,7 +29,11 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.ui.adapter.*;
+import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.KeyServer;
import org.sufficientlysecure.keychain.util.Log;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
index 44b5848d8..110647284 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
@@ -57,7 +57,7 @@ public class ImportKeysNFCFragment extends Fragment {
public void onClick(View v) {
// show nfc help
Intent intent = new Intent(getActivity(), HelpActivity.class);
- intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1);
+ intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 2);
startActivityForResult(intent, 0);
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 10c0752b1..8b553d273 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.ui;
+import com.google.zxing.integration.android.IntentResult;
+
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -28,8 +30,9 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.google.zxing.integration.android.IntentResult;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index 0b2fff64e..3eb463dac 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class ImportKeysServerFragment extends Fragment {
public static final String ARG_QUERY = "query";
public static final String ARG_KEY_SERVER = "key_server";
+ public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
private ImportKeysActivity mImportActivity;
@@ -140,6 +141,10 @@ public class ImportKeysServerFragment extends Fragment {
Log.d(Constants.TAG, "keyServer: " + keyServer);
}
+
+ if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) {
+ mQueryEditText.setEnabled(false);
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 078b998e1..1bc6d4ee1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -21,8 +21,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
@@ -53,27 +53,21 @@ public class KeyListActivity extends DrawerActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_key_list_import:
- Intent intentImport = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intentImport, 0);
-
+ callIntentForDrawerItem(Constants.DrawerItems.IMPORT_KEYS);
return true;
- 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);
- return true;
case R.id.menu_key_list_create:
createKey();
-
return true;
+
case R.id.menu_key_list_create_expert:
createKeyExpert();
-
return true;
- case R.id.menu_key_list_secret_export:
- mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC);
+ case R.id.menu_key_list_export:
+ mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE_PUB, true);
return true;
+
default:
return super.onOptionsItemSelected(item);
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index a08f4bc74..45db30fe5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -24,7 +24,11 @@ import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
-import android.os.*;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -33,22 +37,29 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
-import android.view.*;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
-import android.widget.*;
import android.widget.AbsListView.MultiChoiceModeListener;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -56,7 +67,6 @@ import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -142,9 +152,6 @@ public class KeyListFragment extends Fragment
} catch (ApiLevelTooLowException e) {
}
- // this view is made visible if no data is available
- mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
-
/*
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
* available for Android >= 3.0
@@ -178,18 +185,15 @@ public class KeyListFragment extends Fragment
break;
}
case R.id.menu_key_list_multi_delete: {
- ids = mStickyList.getWrappedList().getCheckedItemIds();
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
showDeleteKeyDialog(mode, ids);
break;
}
case R.id.menu_key_list_multi_export: {
- // todo: public/secret needs to be handled differently here
- ids = mStickyList.getWrappedList().getCheckedItemIds();
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
- mExportHelper
- .showExportKeysDialog(ids,
- Id.type.public_key,
- Constants.Path.APP_DIR_FILE_PUB);
+ mExportHelper.showExportKeysDialog(
+ ids, Constants.Path.APP_DIR_FILE_PUB, mAdapter.isAnySecretSelected());
break;
}
case R.id.menu_key_list_multi_select_all: {
@@ -243,23 +247,17 @@ public class KeyListFragment extends Fragment
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[]{
- KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.TYPE,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.Keys.IS_REVOKED
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.USER_ID,
+ KeyRings.IS_REVOKED,
+ KeyRings.HAS_SECRET
};
- static final int INDEX_TYPE = 1;
- static final int INDEX_MASTER_KEY_ID = 2;
- static final int INDEX_USER_ID = 3;
- static final int INDEX_IS_REVOKED = 4;
-
- static final String SORT_ORDER =
- // show secret before public key
- KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings.TYPE + " DESC, "
- // sort by user id otherwise
- + UserIds.USER_ID + " ASC";
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_USER_ID = 2;
+ static final int INDEX_IS_REVOKED = 3;
+ static final int INDEX_HAS_SECRET = 4;
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@@ -269,12 +267,12 @@ public class KeyListFragment extends Fragment
String where = null;
String whereArgs[] = null;
if (mCurQuery != null) {
- where = KeychainContract.UserIds.USER_ID + " LIKE ?";
+ where = KeyRings.USER_ID + " LIKE ?";
whereArgs = new String[]{"%" + mCurQuery + "%"};
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, SORT_ORDER);
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, null);
}
@Override
@@ -286,6 +284,9 @@ public class KeyListFragment extends Fragment
mStickyList.setAdapter(mAdapter);
+ // this view is made visible if no data is available
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
+
// NOTE: Not supported by StickyListHeader, but reimplemented here
// The list should now be shown.
if (isResumed()) {
@@ -315,17 +316,15 @@ public class KeyListFragment extends Fragment
viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
}
viewIntent.setData(
- KeychainContract
- .KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(mAdapter.getMasterKeyId(position))));
+ KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position))));
startActivity(viewIntent);
}
@TargetApi(11)
- protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) {
+ protected void encrypt(ActionMode mode, long[] masterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingMasterKeyIds);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
@@ -335,35 +334,17 @@ public class KeyListFragment extends Fragment
/**
* Show dialog to delete key
*
- * @param keyRingRowIds
+ * @param masterKeyIds
*/
@TargetApi(11)
// TODO: this method needs an overhaul to handle both public and secret keys gracefully!
- public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
+ public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- ArrayList<String> notDeleted =
- returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED);
- String notDeletedMsg = "";
- for (String userId : notDeleted) {
- notDeletedMsg += userId + "\n";
- }
- Toast.makeText(getActivity(),
- getString(R.string.error_can_not_delete_contacts, notDeletedMsg)
- + getResources()
- .getQuantityString(
- R.plurals.error_can_not_delete_info,
- notDeleted.size()),
- Toast.LENGTH_LONG).show();
-
- mode.finish();
- }
+ mode.finish();
}
}
};
@@ -372,7 +353,7 @@ public class KeyListFragment extends Fragment
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- keyRingRowIds, Id.type.public_key);
+ masterKeyIds);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
}
@@ -506,11 +487,15 @@ public class KeyListFragment extends Fragment
}
{ // set edit button and revoked info, specific by key type
+ View statusDivider = (View) view.findViewById(R.id.status_divider);
+ FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
Button button = (Button) view.findViewById(R.id.edit);
TextView revoked = (TextView) view.findViewById(R.id.revoked);
- if (cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (cursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
// this is a secret key - show the edit button
+ statusDivider.setVisibility(View.VISIBLE);
+ statusLayout.setVisibility(View.VISIBLE);
revoked.setVisibility(View.GONE);
button.setVisibility(View.VISIBLE);
@@ -518,26 +503,31 @@ public class KeyListFragment extends Fragment
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(
- KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(
- Long.toString(id)
- )
- );
+ editIntent.setData(KeyRingData.buildSecretKeyRingUri(Long.toString(id)));
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
startActivityForResult(editIntent, 0);
}
});
} else {
// this is a public key - hide the edit button, show if it's revoked
+ statusDivider.setVisibility(View.GONE);
button.setVisibility(View.GONE);
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
}
}
}
+ public boolean isSecretAvailable(int id) {
+ if (!mCursor.moveToPosition(id)) {
+ throw new IllegalStateException("couldn't move cursor to position " + id);
+ }
+
+ return mCursor.getInt(INDEX_HAS_SECRET) != 0;
+ }
public long getMasterKeyId(int id) {
if (!mCursor.moveToPosition(id)) {
throw new IllegalStateException("couldn't move cursor to position " + id);
@@ -581,7 +571,7 @@ public class KeyListFragment extends Fragment
throw new IllegalStateException("couldn't move cursor to position " + position);
}
- if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (mCursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
{ // set contact count
int num = mCursor.getCount();
String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num);
@@ -620,7 +610,7 @@ public class KeyListFragment extends Fragment
}
// early breakout: all secret keys are assigned id 0
- if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (mCursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
return 1L;
}
// otherwise, return the first character of the name as ID
@@ -645,6 +635,14 @@ public class KeyListFragment extends Fragment
notifyDataSetChanged();
}
+ public boolean isAnySecretSelected() {
+ for (int pos : mSelection.keySet()) {
+ if(mAdapter.isSecretAvailable(pos))
+ return true;
+ }
+ return false;
+ }
+
public long[] getCurrentSelectedMasterKeyIds() {
long[] ids = new long[mSelection.size()];
int i = 0;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 04179cb80..265bb2139 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -20,9 +20,15 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
-import android.preference.*;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -38,11 +44,11 @@ public class PreferencesActivity extends PreferenceActivity {
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
private PreferenceScreen mKeyServerPreference = null;
- private static Preferences mPreferences;
+ private static Preferences sPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
- mPreferences = Preferences.getPreferences(this);
+ sPreferences = Preferences.getPreferences(this);
super.onCreate(savedInstanceState);
// final ActionBar actionBar = getSupportActionBar();
@@ -55,11 +61,11 @@ public class PreferencesActivity extends PreferenceActivity {
if (action != null && action.equals(ACTION_PREFS_GEN)) {
addPreferencesFromResource(R.xml.gen_preferences);
- initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
+ initializePassPassphraceCacheTtl(
+ (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
- String servers[] = mPreferences.getKeyServers();
+ String servers[] = sPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
mKeyServerPreference
@@ -68,7 +74,7 @@ public class PreferencesActivity extends PreferenceActivity {
Intent intent = new Intent(PreferencesActivity.this,
PreferencesKeyServerActivity.class);
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
- mPreferences.getKeyServers());
+ sPreferences.getKeyServers());
startActivityForResult(intent, Id.request.key_server_preference);
return false;
}
@@ -104,8 +110,8 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
entries, values);
- initializeAsciiArmour(
- (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmor(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
@@ -125,7 +131,7 @@ public class PreferencesActivity extends PreferenceActivity {
}
String servers[] = data
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
- mPreferences.setKeyServers(servers);
+ sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(getResources().getQuantityString(
R.plurals.n_key_servers, servers.length, servers.length));
break;
@@ -159,11 +165,11 @@ public class PreferencesActivity extends PreferenceActivity {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.gen_preferences);
- initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
+ initializePassPassphraceCacheTtl(
+ (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
- String servers[] = mPreferences.getKeyServers();
+ String servers[] = sPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
mKeyServerPreference
@@ -172,7 +178,7 @@ public class PreferencesActivity extends PreferenceActivity {
Intent intent = new Intent(getActivity(),
PreferencesKeyServerActivity.class);
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
- mPreferences.getKeyServers());
+ sPreferences.getKeyServers());
startActivityForResult(intent, Id.request.key_server_preference);
return false;
}
@@ -188,7 +194,7 @@ public class PreferencesActivity extends PreferenceActivity {
}
String servers[] = data
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
- mPreferences.setKeyServers(servers);
+ sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(getResources().getQuantityString(
R.plurals.n_key_servers, servers.length, servers.length));
break;
@@ -241,8 +247,8 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
entries, values);
- initializeAsciiArmour(
- (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmor(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
@@ -255,15 +261,15 @@ public class PreferencesActivity extends PreferenceActivity {
|| super.isValidFragment(fragmentName);
}
- private static void initializePassPassPhraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
- mPassphraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
+ private static void initializePassPassphraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
+ mPassphraseCacheTtl.setValue("" + sPreferences.getPassphraseCacheTtl());
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
mPassphraseCacheTtl
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mPassphraseCacheTtl.setValue(newValue.toString());
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
- mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
+ sPreferences.setPassphraseCacheTtl(Integer.parseInt(newValue.toString()));
return false;
}
});
@@ -282,14 +288,14 @@ public class PreferencesActivity extends PreferenceActivity {
}
mEncryptionAlgorithm.setEntries(entries);
mEncryptionAlgorithm.setEntryValues(values);
- mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
+ mEncryptionAlgorithm.setValue("" + sPreferences.getDefaultEncryptionAlgorithm());
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
mEncryptionAlgorithm
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mEncryptionAlgorithm.setValue(newValue.toString());
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue
+ sPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue
.toString()));
return false;
}
@@ -309,13 +315,13 @@ public class PreferencesActivity extends PreferenceActivity {
}
mHashAlgorithm.setEntries(entries);
mHashAlgorithm.setEntryValues(values);
- mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
+ mHashAlgorithm.setValue("" + sPreferences.getDefaultHashAlgorithm());
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mHashAlgorithm.setValue(newValue.toString());
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
+ sPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
return false;
}
});
@@ -326,14 +332,14 @@ public class PreferencesActivity extends PreferenceActivity {
int[] valueIds, String[] entries, String[] values) {
mMessageCompression.setEntries(entries);
mMessageCompression.setEntryValues(values);
- mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
+ mMessageCompression.setValue("" + sPreferences.getDefaultMessageCompression());
mMessageCompression.setSummary(mMessageCompression.getEntry());
mMessageCompression
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mMessageCompression.setValue(newValue.toString());
mMessageCompression.setSummary(mMessageCompression.getEntry());
- mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue
+ sPreferences.setDefaultMessageCompression(Integer.parseInt(newValue
.toString()));
return false;
}
@@ -344,36 +350,36 @@ public class PreferencesActivity extends PreferenceActivity {
(final IntegerListPreference mFileCompression, String[] entries, String[] values) {
mFileCompression.setEntries(entries);
mFileCompression.setEntryValues(values);
- mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
+ mFileCompression.setValue("" + sPreferences.getDefaultFileCompression());
mFileCompression.setSummary(mFileCompression.getEntry());
mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mFileCompression.setValue(newValue.toString());
mFileCompression.setSummary(mFileCompression.getEntry());
- mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
+ sPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
return false;
}
});
}
- private static void initializeAsciiArmour(final CheckBoxPreference mAsciiArmour) {
- mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
- mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ private static void initializeAsciiArmor(final CheckBoxPreference mAsciiArmor) {
+ mAsciiArmor.setChecked(sPreferences.getDefaultAsciiArmor());
+ mAsciiArmor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
- mAsciiArmour.setChecked((Boolean) newValue);
- mPreferences.setDefaultAsciiArmour((Boolean) newValue);
+ mAsciiArmor.setChecked((Boolean) newValue);
+ sPreferences.setDefaultAsciiArmor((Boolean) newValue);
return false;
}
});
}
private static void initializeForceV3Signatures(final CheckBoxPreference mForceV3Signatures) {
- mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
+ mForceV3Signatures.setChecked(sPreferences.getForceV3Signatures());
mForceV3Signatures
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mForceV3Signatures.setChecked((Boolean) newValue);
- mPreferences.setForceV3Signatures((Boolean) newValue);
+ sPreferences.setForceV3Signatures((Boolean) newValue);
return false;
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
index d890f35cb..719378274 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
@@ -91,10 +91,15 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
}
}
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
// nothing to do
}
+ @Override
+ public void onEdited() {
+
+ }
+
public void onClick(View v) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
mEditors, false);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index 6ab9f1c6e..9bfe3eaa9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -32,7 +32,13 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
@@ -248,9 +254,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- // This is called when a new Loader needs to be created. This
- // sample only has one Loader, so we don't care about the ID.
- Uri baseUri = KeyRings.buildPublicKeyRingsUri();
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
@@ -258,24 +262,24 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
- "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys."
- + Keys.CAN_ENCRYPT + " = '1') AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
- "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND valid_keys." + Keys.IS_REVOKED + " = '0' AND valid_keys."
- + Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
- + now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
- + Keys.EXPIRY + " >= '" + now + "')) AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ +" WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_ENCRYPT + " = '1'"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_ENCRYPT + " = '1'"
+ + " AND k." + Keys.CREATION + " <= '" + now + "'"
+ + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY + " >= '" + now + "' )"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
String inMasterKeyList = null;
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
- inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN (";
+ inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN (";
for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
index 1509bc88c..0ff88d97c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
@@ -1,42 +1,37 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2012-2014 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
-import android.view.Menu;
-import org.sufficientlysecure.keychain.Constants;
+
import org.sufficientlysecure.keychain.R;
public class SelectSecretKeyActivity extends ActionBarActivity {
- // Actions for internal use only:
- public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX
- + "SELECT_SECRET_KEYRING";
-
public static final String EXTRA_FILTER_CERTIFY = "filter_certify";
public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String RESULT_EXTRA_USER_ID = "user_id";
- private boolean mFilterCertify = false;
+ private boolean mFilterCertify;
private SelectSecretKeyFragment mSelectFragment;
@Override
@@ -50,23 +45,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- // TODO: reimplement!
- // mFilterLayout = findViewById(R.id.layout_filter);
- // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
- //
- // mClearFilterButton.setOnClickListener(new OnClickListener() {
- // public void onClick(View v) {
- // handleIntent(new Intent());
- // }
- // });
-
mFilterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false);
- handleIntent(getIntent());
-
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.select_secret_key_fragment_container) != null) {
@@ -90,48 +70,14 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
/**
* This is executed by SelectSecretKeyFragment after clicking on an item
*
- * @param masterKeyId
- * @param userId
+ * @param selectedUri
*/
- public void afterListSelection(long masterKeyId, String userId) {
+ public void afterListSelection(Uri selectedUri) {
Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId);
- data.putExtra(RESULT_EXTRA_USER_ID, (String) userId);
+ data.setData(selectedUri);
+
setResult(RESULT_OK, data);
finish();
}
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- // TODO: reimplement!
-
- // String searchString = null;
- // if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- // searchString = intent.getStringExtra(SearchManager.QUERY);
- // if (searchString != null && searchString.trim().length() == 0) {
- // searchString = null;
- // }
- // }
-
- // if (searchString == null) {
- // mFilterLayout.setVisibility(View.GONE);
- // } else {
- // mFilterLayout.setVisibility(View.VISIBLE);
- // mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- // }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // TODO: reimplement!
- // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon(
- // android.R.drawable.ic_menu_search);
- return true;
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 47a3fbad3..9987facbc 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,6 +28,7 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -55,10 +56,9 @@ public class SelectSecretKeyFragment extends ListFragment implements
*/
public static SelectSecretKeyFragment newInstance(boolean filterCertify) {
SelectSecretKeyFragment frag = new SelectSecretKeyFragment();
- Bundle args = new Bundle();
+ Bundle args = new Bundle();
args.putBoolean(ARG_FILTER_CERTIFY, filterCertify);
-
frag.setArguments(args);
return frag;
@@ -85,10 +85,10 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
- String userId = mAdapter.getUserId(position);
+ Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
// return data to activity, which results in finishing it
- mActivity.afterListSelection(masterKeyId, userId);
+ mActivity.afterListSelection(result);
}
});
@@ -112,12 +112,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
- Uri baseUri = KeyRings.buildSecretKeyRingsUri();
-
- String capFilter = null;
- if (mFilterCertify) {
- capFilter = "(cert > 0)";
- }
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
@@ -125,29 +120,36 @@ public class SelectSecretKeyFragment extends ListFragment implements
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
- "(SELECT COUNT(cert_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS cert_keys WHERE cert_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND cert_keys."
- + Keys.CAN_CERTIFY + " = '1') AS cert",
- "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys."
- + Keys.CAN_SIGN + " = '1') AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
- "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys."
- + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN
- + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND "
- + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY
- + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
+ + " AND k." + Keys.CAN_CERTIFY + " = '1'"
+ + ") AS cert",
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ +" WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_SIGN + " = '1'"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_SIGN + " = '1'"
+ + " AND k." + Keys.CREATION + " <= '" + now + "'"
+ + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY + " >= '" + now + "' )"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
String orderBy = UserIds.USER_ID + " ASC";
+ String where = Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL";
+ if (mFilterCertify) {
+ where += " AND (cert > 0)";
+ }
+
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, projection, capFilter, null, orderBy);
+ return new CursorLoader(getActivity(), baseUri, projection, where, null, orderBy);
}
@Override
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
index 3d22ca9f6..514951385 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
@@ -19,22 +19,26 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.sufficientlysecure.keychain.Id;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
-public class SelectSecretKeyLayoutFragment extends Fragment {
+public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
@@ -43,10 +47,23 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
private BootstrapButton mSelectKeyButton;
private Boolean mFilterCertify;
+ private Uri mReceivedUri = null;
+
private SelectSecretKeyCallback mCallback;
private static final int REQUEST_CODE_SELECT_KEY = 8882;
+ private static final int LOADER_ID = 0;
+
+ //The Projection we will retrieve, Master Key ID is for convenience sake,
+ //to avoid having to pass the Key Around
+ final String[] PROJECTION = new String[] {
+ KeychainContract.Keys.MASTER_KEY_ID,
+ KeychainContract.UserIds.USER_ID
+ };
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_USER_ID = 1;
+
public interface SelectSecretKeyCallback {
void onKeySelected(long secretKeyId);
}
@@ -59,68 +76,30 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
mFilterCertify = filterCertify;
}
- public void selectKey(long secretKeyId) {
- if (secretKeyId == Id.key.none) {
- mNoKeySelected.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mKeyMasterKeyIdHex.setVisibility(View.GONE);
+ public void setNoKeySelected() {
+ mNoKeySelected.setVisibility(View.VISIBLE);
+ mKeyUserId.setVisibility(View.GONE);
+ mKeyUserIdRest.setVisibility(View.GONE);
+ mKeyMasterKeyIdHex.setVisibility(View.GONE);
+ }
- } else {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
- getActivity(), secretKeyId);
- if (keyRing != null) {
- PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
- String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
-
- if (key != null) {
- String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
-
- String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
- String userName, userEmail;
-
- if (userIdSplit[0] != null) {
- userName = userIdSplit[0];
- } else {
- userName = getActivity().getResources().getString(R.string.user_id_no_name);
- }
-
- if (userIdSplit[1] != null) {
- userEmail = userIdSplit[1];
- } else {
- userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email);
- }
-
- mKeyMasterKeyIdHex.setText(masterkeyIdHex);
- mKeyUserId.setText(userName);
- mKeyUserIdRest.setText(userEmail);
- mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.VISIBLE);
- mKeyUserIdRest.setVisibility(View.VISIBLE);
- mNoKeySelected.setVisibility(View.GONE);
- } else {
- mKeyMasterKeyIdHex.setVisibility(View.GONE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mNoKeySelected.setVisibility(View.VISIBLE);
- }
- } else {
- mKeyMasterKeyIdHex.setText(
- getActivity().getResources()
- .getString(R.string.no_keys_added_or_updated)
- + " for master id: " + secretKeyId);
- mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mNoKeySelected.setVisibility(View.GONE);
- }
+ public void setSelectedKeyData(String userName, String email, String masterKeyHex) {
+
+ mNoKeySelected.setVisibility(View.GONE);
+
+ mKeyUserId.setText(userName);
+ mKeyUserIdRest.setText(email);
+ mKeyMasterKeyIdHex.setText(masterKeyHex);
+
+ mKeyUserId.setVisibility(View.VISIBLE);
+ mKeyUserIdRest.setVisibility(View.VISIBLE);
+ mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- }
}
public void setError(String error) {
- mKeyUserId.requestFocus();
- mKeyUserId.setError(error);
+ mNoKeySelected.requestFocus();
+ mNoKeySelected.setError(error);
}
/**
@@ -147,29 +126,78 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
return view;
}
+ //For AppSettingsFragment
+ public void selectKey(long masterKeyId) {
+ Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ mReceivedUri = buildUri;
+ getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
+ }
+
private void startSelectKeyActivity() {
Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, mFilterCertify);
startActivityForResult(intent, REQUEST_CODE_SELECT_KEY);
}
- // Select Secret Key Activity delivers the intent which was sent by it using interface to Select
- // Secret Key Fragment.Intent contains Master Key Id, User Email, User Name, Master Key Id Hex.
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mReceivedUri);
+ //We don't care about the Loader id
+ return new CursorLoader(getActivity(), uri, PROJECTION, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (data.moveToFirst()) {
+ String userName, email, masterKeyHex;
+ String userID = data.getString(INDEX_USER_ID);
+ long masterKeyID = data.getLong(INDEX_MASTER_KEY_ID);
+
+ String splitUserID[] = PgpKeyHelper.splitUserId(userID);
+
+ if (splitUserID[0] != null) {
+ userName = splitUserID[0];
+ } else {
+ userName = getActivity().getResources().getString(R.string.user_id_no_name);
+ }
+
+ if (splitUserID[1] != null) {
+ email = splitUserID[1];
+ } else {
+ email = getActivity().getResources().getString(R.string.error_user_id_no_email);
+ }
+
+ //TODO Can the cursor return invalid values for the Master Key ?
+ masterKeyHex = PgpKeyHelper.convertKeyIdToHexShort(masterKeyID);
+
+ //Set the data
+ setSelectedKeyData(userName, email, masterKeyHex);
+
+ //Give value to the callback
+ mCallback.onKeySelected(masterKeyID);
+ } else {
+ //Set The empty View
+ setNoKeySelected();
+ }
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ return;
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode & 0xFFFF) {
+ switch (requestCode) {
case REQUEST_CODE_SELECT_KEY: {
- long secretKeyId;
if (resultCode == Activity.RESULT_OK) {
- Bundle bundle = data.getExtras();
- secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
- selectKey(secretKeyId);
+ mReceivedUri = data.getData();
- // remove displayed errors
- mKeyUserId.setError(null);
+ //Must be restartLoader() or the data will not be updated on selecting a new key
+ getActivity().getSupportLoaderManager().restartLoader(0, null, this);
- // give value back to callback
- mCallback.onKeySelected(secretKeyId);
+ mKeyUserId.setError(null);
}
break;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 2c8f66488..0e231e6a8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -98,11 +98,11 @@ public class UploadKeyActivity extends ActionBarActivity {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 611864d8e..b273955dd 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -37,7 +37,9 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
@@ -50,15 +52,14 @@ public class ViewCertActivity extends ActionBarActivity
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] {
- KeychainContract.Certs._ID,
- KeychainContract.Certs.KEY_ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.Certs.CREATION,
- KeychainContract.Certs.KEY_ID_CERTIFIER,
- "signer_uid",
- KeychainContract.Certs.KEY_DATA
+ Certs.MASTER_KEY_ID,
+ Certs.USER_ID,
+ Certs.CREATION,
+ Certs.KEY_ID_CERTIFIER,
+ Certs.SIGNER_UID,
+ Certs.KEY_DATA
};
- private static final int INDEX_KEY_ID = 1;
+ private static final int INDEX_MASTER_KEY_ID = 1;
private static final int INDEX_USER_ID = 2;
private static final int INDEX_CREATION = 3;
private static final int INDEX_KEY_ID_CERTIFIER = 4;
@@ -112,7 +113,7 @@ public class ViewCertActivity extends ActionBarActivity
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(data.moveToFirst()) {
- String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_KEY_ID));
+ String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_MASTER_KEY_ID));
mSigneeKey.setText(signeeKey);
String signeeUid = data.getString(INDEX_USER_ID);
@@ -179,17 +180,21 @@ public class ViewCertActivity extends ActionBarActivity
return true;
case R.id.menu_view_cert_view_signer:
// can't do this before the data is initialized
- // TODO notify user of this, maybe offer download?
- if(mSignerKeyId == 0)
- return true;
Intent viewIntent = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
viewIntent = new Intent(this, ViewKeyActivity.class);
} else {
viewIntent = new Intent(this, ViewKeyActivityJB.class);
}
- viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(mSignerKeyId))
+ //
+ long signerMasterKeyId = ProviderHelper.getMasterKeyId(this,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mSignerKeyId))
+ );
+ // TODO notify user of this, maybe offer download?
+ if(mSignerKeyId == 0L)
+ return true;
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(
+ Long.toString(signerMasterKeyId))
);
startActivity(viewIntent);
return true;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 592c4890b..7b9ba4b2d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
+import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -28,6 +29,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.Window;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -38,10 +40,8 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -60,6 +60,7 @@ public class ViewKeyActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
mExportHelper = new ExportHelper(this);
@@ -83,11 +84,7 @@ public class ViewKeyActivity extends ActionBarActivity {
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
}
- // normalize mDataUri to a "by row id" query, to ensure it works with any
- // given valid /public/ query
- long rowId = ProviderHelper.getRowId(this, getIntent().getData());
- // TODO: handle (rowId == 0) with something else than a crash
- mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
+ mDataUri = getIntent().getData();
Bundle mainBundle = new Bundle();
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
@@ -95,7 +92,7 @@ public class ViewKeyActivity extends ActionBarActivity {
ViewKeyMainFragment.class, mainBundle, (selectedTab == 0));
Bundle certBundle = new Bundle();
- certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId);
+ certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
ViewKeyCertsFragment.class, certBundle, (selectedTab == 1));
}
@@ -122,8 +119,12 @@ public class ViewKeyActivity extends ActionBarActivity {
uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
- long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
- mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB);
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ mExportHelper.showExportKeysDialog(
+ new long[] { masterKeyId } , Constants.Path.APP_DIR_FILE_PUB,
+ // TODO this doesn't work?
+ ((ViewKeyMainFragment) mTabsAdapter.getItem(0)).isSecretAvailable()
+ );
return true;
case R.id.menu_key_view_share_default_fingerprint:
shareKey(mDataUri, true);
@@ -158,33 +159,37 @@ public class ViewKeyActivity extends ActionBarActivity {
}
private void updateFromKeyserver(Uri dataUri) {
- long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
-
- if (updateKeyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- return;
- }
+ byte[] blob = (byte[]) ProviderHelper.getGenericData(
+ this, KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
Intent queryIntent = new Intent(this, ImportKeysActivity.class);
- queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
- queryIntent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, updateKeyId);
+ queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
- // TODO: lookup with onactivityresult!
startActivityForResult(queryIntent, RESULT_CODE_LOOKUP_KEY);
}
private void shareKey(Uri dataUri, boolean fingerprintOnly) {
String content;
if (fingerprintOnly) {
- byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
-
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ byte[] data = (byte[]) ProviderHelper.getGenericData(
+ this, KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if(data != null) {
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
+ content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ } else {
+ Toast.makeText(getApplicationContext(), "Bad key selected!",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
} else {
// get public keyring as ascii armored string
long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this,
- dataUri, new long[]{masterKeyId});
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
+ this, new long[]{ masterKeyId });
content = keyringArmored.get(0);
@@ -214,8 +219,8 @@ public class ViewKeyActivity extends ActionBarActivity {
private void copyToClipboard(Uri dataUri) {
// get public keyring as ascii armored string
long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
- new long[]{masterKeyId});
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
+ this, new long[]{ masterKeyId });
ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
Toast.makeText(getApplicationContext(), R.string.key_copied_to_clipboard, Toast.LENGTH_LONG)
@@ -232,25 +237,29 @@ public class ViewKeyActivity extends ActionBarActivity {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key
- Toast.makeText(ViewKeyActivity.this,
- getString(R.string.error_can_not_delete_contact)
- + getResources()
- .getQuantityString(R.plurals.error_can_not_delete_info, 1),
- Toast.LENGTH_LONG).show();
- } else {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
+ setResult(RESULT_CANCELED);
+ finish();
}
};
- mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler);
+ mExportHelper.deleteKey(dataUri, returnHandler);
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_LOOKUP_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // TODO: reload key??? move this into fragment?
+ }
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
index 997ff9c7a..6ce7d9aa8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
@@ -34,6 +34,9 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMessageCallback,
@@ -47,26 +50,18 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- initNfc(mDataUri);
}
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
- private void initNfc(Uri dataUri) {
+ private void initNfc() {
// check if NFC Beam is supported (>= Android 4.1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
// init nfc
-
- // get public keyring as byte array
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
- new long[]{masterKeyId});
-
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(this, this);
// Register callback to listen for message-sent success
@@ -86,9 +81,19 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
* guarantee that this activity starts when receiving a beamed message. For now, this code
* uses the tag dispatch system.
*/
- NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
- mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
- return msg;
+ // get public keyring as byte array
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ try {
+ mSharedKeyringBytes = ProviderHelper.getPGPPublicKeyRing(this, masterKeyId).getEncoded();
+
+ NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
+ mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
+ return msg;
+ } catch(IOException e) {
+ // not much trouble, but leave a note
+ Log.e(Constants.TAG, "Error parsing keyring: ", e);
+ return null;
+ }
}
/**
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index c65e9e691..26e72f10b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -53,20 +53,21 @@ public class ViewKeyCertsFragment extends Fragment
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] {
KeychainContract.Certs._ID,
+ KeychainContract.Certs.MASTER_KEY_ID,
KeychainContract.Certs.VERIFIED,
KeychainContract.Certs.RANK,
KeychainContract.Certs.KEY_ID_CERTIFIER,
- KeychainContract.UserIds.USER_ID,
- "signer_uid"
+ KeychainContract.Certs.USER_ID,
+ KeychainContract.Certs.SIGNER_UID
};
// sort by our user id,
static final String SORT_ORDER =
KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC, "
+ KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, "
- + "signer_uid ASC";
+ + KeychainContract.Certs.SIGNER_UID + " ASC";
- public static final String ARG_KEYRING_ROW_ID = "row_id";
+ public static final String ARG_DATA_URI = "data_uri";
private StickyListHeadersListView mStickyList;
private Spinner mSpinner;
@@ -125,14 +126,14 @@ public class ViewKeyCertsFragment extends Fragment
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
- if (!getArguments().containsKey(ARG_KEYRING_ROW_ID)) {
+ if (!getArguments().containsKey(ARG_DATA_URI)) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
getActivity().finish();
return;
}
- long rowId = getArguments().getLong(ARG_KEYRING_ROW_ID);
- mBaseUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId));
+ Uri uri = getArguments().getParcelable(ARG_DATA_URI);
+ mBaseUri = KeychainContract.Certs.buildCertsUri(uri);
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
@@ -229,12 +230,12 @@ public class ViewKeyCertsFragment extends Fragment
private void initIndex(Cursor cursor) {
if (cursor != null) {
- mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs._ID);
+ mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.MASTER_KEY_ID);
mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID);
mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK);
mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED);
mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER);
- mIndexSignerUserId = cursor.getColumnIndexOrThrow("signer_uid");
+ mIndexSignerUserId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.SIGNER_UID);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index 8f8c13c29..b5a800712 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -30,15 +30,19 @@ import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
@@ -52,6 +56,7 @@ public class ViewKeyMainFragment extends Fragment implements
public static final String ARG_DATA_URI = "uri";
+ private LinearLayout mContainer;
private TextView mName;
private TextView mEmail;
private TextView mComment;
@@ -68,7 +73,7 @@ public class ViewKeyMainFragment extends Fragment implements
private ListView mUserIds;
private ListView mKeys;
- private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_UNIFIED = 0;
private static final int LOADER_ID_USER_IDS = 1;
private static final int LOADER_ID_KEYS = 2;
@@ -77,10 +82,14 @@ public class ViewKeyMainFragment extends Fragment implements
private Uri mDataUri;
+ // for activity
+ private boolean mSecretAvailable = false;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view_key_main_fragment, container, false);
+ mContainer = (LinearLayout) view.findViewById(R.id.container);
mName = (TextView) view.findViewById(R.id.name);
mEmail = (TextView) view.findViewById(R.id.email);
mComment = (TextView) view.findViewById(R.id.comment);
@@ -119,64 +128,24 @@ public class ViewKeyMainFragment extends Fragment implements
return;
}
+ getActivity().setProgressBarIndeterminateVisibility(Boolean.TRUE);
+ mContainer.setVisibility(View.GONE);
+
mDataUri = dataUri;
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
- { // label whether secret key is available, and edit button if it is
- final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri);
- if (ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) {
- // set this attribute. this is a LITTLE unclean, but we have the info available
- // right here, so why not.
- mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
- mSecretKey.setText(R.string.secret_key_yes);
-
- // certify button
- // TODO this button MIGHT be useful if the user wants to
- // certify a private key with another...
- // mActionCertify.setVisibility(View.GONE);
-
- // edit button
- mActionEdit.setVisibility(View.VISIBLE);
- mActionEdit.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(
- KeychainContract
- .KeyRings.buildSecretKeyRingsByMasterKeyIdUri(
- Long.toString(masterKeyId)));
- editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
- startActivityForResult(editIntent, 0);
- }
- });
- } else {
- mSecretKey.setTextColor(Color.BLACK);
- mSecretKey.setText(getResources().getString(R.string.secret_key_no));
-
- // certify button
- mActionCertify.setVisibility(View.VISIBLE);
- // edit button
- mActionEdit.setVisibility(View.GONE);
- }
-
- // TODO see todo note above, doing this here for now
- mActionCertify.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- certifyKey(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(masterKeyId)
- ));
- }
- });
-
- }
-
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
-
@Override
public void onClick(View v) {
encryptToContact(mDataUri);
}
});
+ mActionCertify.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ certifyKey(mDataUri);
+ }
+ });
mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -186,74 +155,51 @@ public class ViewKeyMainFragment extends Fragment implements
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
- getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
+ getActivity().getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
getActivity().getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
}
- static final String[] KEYRING_PROJECTION =
- new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID};
- static final int KEYRING_INDEX_ID = 0;
- static final int KEYRING_INDEX_MASTER_KEY_ID = 1;
- static final int KEYRING_INDEX_USER_ID = 2;
-
- static final String[] USER_IDS_PROJECTION = new String[]{
- KeychainContract.UserIds._ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.UserIds.RANK,
- "verified",
+ static final String[] UNIFIED_PROJECTION = new String[] {
+ KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_SECRET,
+ KeyRings.USER_ID, KeyRings.FINGERPRINT,
+ KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.EXPIRY,
+
+ };
+ static final int INDEX_UNIFIED_MKI = 1;
+ static final int INDEX_UNIFIED_HAS_SECRET = 2;
+ static final int INDEX_UNIFIED_UID = 3;
+ static final int INDEX_UNIFIED_FINGERPRINT = 4;
+ static final int INDEX_UNIFIED_ALGORITHM = 5;
+ static final int INDEX_UNIFIED_KEY_SIZE = 6;
+ static final int INDEX_UNIFIED_CREATION = 7;
+ static final int INDEX_UNIFIED_EXPIRY = 8;
+
+ static final String[] USER_IDS_PROJECTION = new String[] {
+ UserIds._ID, UserIds.USER_ID, UserIds.RANK,
};
- // not the main user id
- static final String USER_IDS_SELECTION =
- KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " > 0 ";
- static final String USER_IDS_SORT_ORDER =
- KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC";
-
- static final String[] KEYS_PROJECTION = new String[]{
- KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID,
- KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM,
- KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY,
- KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT,
- KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY,
- KeychainContract.Keys.FINGERPRINT
- };
- static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC";
- static final int KEYS_INDEX_ID = 0;
- static final int KEYS_INDEX_KEY_ID = 1;
- static final int KEYS_INDEX_IS_MASTER_KEY = 2;
- static final int KEYS_INDEX_ALGORITHM = 3;
- static final int KEYS_INDEX_KEY_SIZE = 4;
- static final int KEYS_INDEX_CAN_CERTIFY = 5;
- static final int KEYS_INDEX_CAN_SIGN = 6;
- static final int KEYS_INDEX_CAN_ENCRYPT = 7;
- static final int KEYS_INDEX_CREATION = 8;
- static final int KEYS_INDEX_EXPIRY = 9;
- static final int KEYS_INDEX_FINGERPRINT = 10;
+
+ static final String[] KEYS_PROJECTION = new String[] {
+ Keys._ID,
+ Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE,
+ Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED,
+ Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT
+ };
+ static final int KEYS_INDEX_CAN_ENCRYPT = 6;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
- case LOADER_ID_KEYRING: {
- Uri baseUri = mDataUri;
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, KEYRING_PROJECTION, null, null, null);
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
}
case LOADER_ID_USER_IDS: {
- Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null,
- USER_IDS_SORT_ORDER);
+ Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null, null);
}
case LOADER_ID_KEYS: {
- Uri baseUri = KeychainContract.Keys.buildKeysUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, KEYS_SORT_ORDER);
+ Uri baseUri = Keys.buildKeysUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null);
}
default:
@@ -262,14 +208,19 @@ public class ViewKeyMainFragment extends Fragment implements
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if(data.getCount() == 0)
+ return;
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
switch (loader.getId()) {
- case LOADER_ID_KEYRING:
+ case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
// get name, email, and comment from USER_ID
- String[] mainUserId = PgpKeyHelper.splitUserId(data
- .getString(KEYRING_INDEX_USER_ID));
+ String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(INDEX_UNIFIED_UID));
if (mainUserId[0] != null) {
getActivity().setTitle(mainUserId[0]);
mName.setText(mainUserId[0]);
@@ -279,63 +230,97 @@ public class ViewKeyMainFragment extends Fragment implements
}
mEmail.setText(mainUserId[1]);
mComment.setText(mainUserId[2]);
- }
- break;
- case LOADER_ID_USER_IDS:
- mUserIdsAdapter.swapCursor(data);
- break;
- case LOADER_ID_KEYS:
- // the first key here is our master key
- if (data.moveToFirst()) {
- // get key id from MASTER_KEY_ID
- long keyId = data.getLong(KEYS_INDEX_KEY_ID);
+ if (data.getInt(INDEX_UNIFIED_HAS_SECRET) != 0) {
+ mSecretAvailable = true;
+
+ mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
+ mSecretKey.setText(R.string.secret_key_yes);
+
+ // edit button
+ mActionEdit.setVisibility(View.VISIBLE);
+ mActionEdit.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
+ editIntent.setData(mDataUri);
+ editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
+ startActivityForResult(editIntent, 0);
+ }
+ });
+ } else {
+ mSecretAvailable = false;
+
+ mSecretKey.setTextColor(Color.BLACK);
+ mSecretKey.setText(getResources().getString(R.string.secret_key_no));
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
+ // certify button
+ mActionCertify.setVisibility(View.VISIBLE);
+ // edit button
+ mActionEdit.setVisibility(View.GONE);
+ }
+
+ // get key id from MASTER_KEY_ID
+ long masterKeyId = data.getLong(INDEX_UNIFIED_MKI);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
mKeyId.setText(keyIdStr);
// get creation date from CREATION
- if (data.isNull(KEYS_INDEX_CREATION)) {
+ if (data.isNull(INDEX_UNIFIED_CREATION)) {
mCreation.setText(R.string.none);
} else {
- Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);
+ Date creationDate = new Date(data.getLong(INDEX_UNIFIED_CREATION) * 1000);
mCreation.setText(
DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- creationDate));
+ creationDate));
}
// get expiry date from EXPIRY
- if (data.isNull(KEYS_INDEX_EXPIRY)) {
+ if (data.isNull(INDEX_UNIFIED_EXPIRY)) {
mExpiry.setText(R.string.none);
} else {
- Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);
+ Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
mExpiry.setText(
DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- expiryDate));
+ expiryDate));
}
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- data.getInt(KEYS_INDEX_ALGORITHM), data.getInt(KEYS_INDEX_KEY_SIZE));
+ data.getInt(INDEX_UNIFIED_ALGORITHM), data.getInt(INDEX_UNIFIED_KEY_SIZE));
mAlgorithm.setText(algorithmStr);
- byte[] fingerprintBlob = data.getBlob(KEYS_INDEX_FINGERPRINT);
- if (fingerprintBlob == null) {
- // FALLBACK for old database entries
- fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
- }
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
+ byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
- mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint));
+ break;
}
+ }
- mKeysAdapter.swapCursor(data);
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(data);
break;
- default:
+ case LOADER_ID_KEYS:
+ // hide encrypt button if no encryption key is available
+ boolean canEncrypt = false;
+ data.moveToFirst();
+ do {
+ if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) {
+ canEncrypt = true;
+ break;
+ }
+ } while (data.moveToNext());
+ if (!canEncrypt) {
+ mActionEncrypt.setVisibility(View.GONE);
+ }
+
+ mKeysAdapter.swapCursor(data);
break;
}
+ getActivity().setProgressBarIndeterminateVisibility(Boolean.FALSE);
+ mContainer.setVisibility(View.VISIBLE);
}
/**
@@ -344,24 +329,25 @@ public class ViewKeyMainFragment extends Fragment implements
*/
public void onLoaderReset(Loader<Cursor> loader) {
switch (loader.getId()) {
- case LOADER_ID_KEYRING:
- // No resources need to be freed for this ID
- break;
case LOADER_ID_USER_IDS:
mUserIdsAdapter.swapCursor(null);
break;
case LOADER_ID_KEYS:
mKeysAdapter.swapCursor(null);
break;
- default:
- break;
}
}
+ /** Returns true if the key current displayed is known to have a secret key. */
+ public boolean isSecretAvailable() {
+ return mSecretAvailable;
+ }
+
private void encryptToContact(Uri dataUri) {
+ // TODO preselect from uri? should be feasible without trivial query
long keyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
- long[] encryptionKeyIds = new long[]{keyId};
+ long[] encryptionKeyIds = new long[]{ keyId };
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 0f05af447..f322ea980 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -30,6 +30,7 @@ import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -43,13 +44,12 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
protected List<ImportKeysListEntry> mData;
static class ViewHolder {
- private TextView mMainUserId;
- private TextView mMainUserIdRest;
- private TextView mKeyId;
- private TextView mFingerprint;
- private TextView mAlgorithm;
- private TextView mStatus;
-
+ public TextView mainUserId;
+ public TextView mainUserIdRest;
+ public TextView keyId;
+ public TextView fingerprint;
+ public TextView algorithm;
+ public TextView status;
}
public ImportKeysAdapter(Activity activity) {
@@ -100,12 +100,12 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
- holder.mMainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
- holder.mMainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
- holder.mKeyId = (TextView) convertView.findViewById(R.id.keyId);
- holder.mFingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
- holder.mAlgorithm = (TextView) convertView.findViewById(R.id.algorithm);
- holder.mStatus = (TextView) convertView.findViewById(R.id.status);
+ holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
+ holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
+ holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
+ holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
+ holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
+ holder.status = (TextView) convertView.findViewById(R.id.status);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
@@ -119,36 +119,36 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// show red user id if it is a secret key
if (entry.secretKey) {
userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0];
- holder.mMainUserId.setTextColor(Color.RED);
+ holder.mainUserId.setTextColor(Color.RED);
}
- holder.mMainUserId.setText(userIdSplit[0]);
+ holder.mainUserId.setText(userIdSplit[0]);
} else {
- holder.mMainUserId.setText(R.string.user_id_no_name);
+ holder.mainUserId.setText(R.string.user_id_no_name);
}
// email
if (userIdSplit[1] != null) {
- holder.mMainUserIdRest.setText(userIdSplit[1]);
- holder.mMainUserIdRest.setVisibility(View.VISIBLE);
+ holder.mainUserIdRest.setText(userIdSplit[1]);
+ holder.mainUserIdRest.setVisibility(View.VISIBLE);
} else {
- holder.mMainUserIdRest.setVisibility(View.GONE);
+ holder.mainUserIdRest.setVisibility(View.GONE);
}
- holder.mKeyId.setText(entry.hexKeyId);
+ holder.keyId.setText(entry.keyIdHex);
- if (entry.fingerPrint != null) {
- holder.mFingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
- holder.mFingerprint.setVisibility(View.VISIBLE);
+ if (entry.fingerPrintHex != null) {
+ holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex));
+ holder.fingerprint.setVisibility(View.VISIBLE);
} else {
- holder.mFingerprint.setVisibility(View.GONE);
+ holder.fingerprint.setVisibility(View.GONE);
}
- holder.mAlgorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
+ holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
if (entry.revoked) {
- holder.mStatus.setText(R.string.revoked);
+ holder.status.setText(R.string.revoked);
} else {
- holder.mStatus.setVisibility(View.GONE);
+ holder.status.setVisibility(View.GONE);
}
LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
index 19f0d1eaf..5631d40ea 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
@@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.SparseArray;
+
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
@@ -34,13 +36,13 @@ import java.util.Date;
public class ImportKeysListEntry implements Serializable, Parcelable {
private static final long serialVersionUID = -7797972103284992662L;
- public ArrayList<String> userIds;
+ public ArrayList<String> userIds;
public long keyId;
+ public String keyIdHex;
public boolean revoked;
public Date date; // TODO: not displayed
- public String fingerPrint;
- public String hexKeyId;
+ public String fingerPrintHex;
public int bitStrength;
public String algorithm;
public boolean secretKey;
@@ -54,8 +56,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.keyId = b.keyId;
this.revoked = b.revoked;
this.date = b.date;
- this.fingerPrint = b.fingerPrint;
- this.hexKeyId = b.hexKeyId;
+ this.fingerPrintHex = b.fingerPrintHex;
+ this.keyIdHex = b.keyIdHex;
this.bitStrength = b.bitStrength;
this.algorithm = b.algorithm;
this.secretKey = b.secretKey;
@@ -73,8 +75,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeLong(keyId);
dest.writeByte((byte) (revoked ? 1 : 0));
dest.writeSerializable(date);
- dest.writeString(fingerPrint);
- dest.writeString(hexKeyId);
+ dest.writeString(fingerPrintHex);
+ dest.writeString(keyIdHex);
dest.writeInt(bitStrength);
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
@@ -91,8 +93,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.keyId = source.readLong();
vr.revoked = source.readByte() == 1;
vr.date = (Date) source.readSerializable();
- vr.fingerPrint = source.readString();
- vr.hexKeyId = source.readString();
+ vr.fingerPrintHex = source.readString();
+ vr.keyIdHex = source.readString();
vr.bitStrength = source.readInt();
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
@@ -108,8 +110,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
}
};
- public long getKeyId() {
- return keyId;
+ public String getKeyIdHex() {
+ return keyIdHex;
}
public byte[] getBytes() {
@@ -120,6 +122,82 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.mBytes = bytes;
}
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ public void setSelected(boolean selected) {
+ this.mSelected = selected;
+ }
+
+ public long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(long keyId) {
+ this.keyId = keyId;
+ }
+
+ public void setKeyIdHex(String keyIdHex) {
+ this.keyIdHex = keyIdHex;
+ }
+
+ public boolean isRevoked() {
+ return revoked;
+ }
+
+ public void setRevoked(boolean revoked) {
+ this.revoked = revoked;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getFingerPrintHex() {
+ return fingerPrintHex;
+ }
+
+ public void setFingerPrintHex(String fingerPrintHex) {
+ this.fingerPrintHex = fingerPrintHex;
+ }
+
+ public int getBitStrength() {
+ return bitStrength;
+ }
+
+ public void setBitStrength(int bitStrength) {
+ this.bitStrength = bitStrength;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public boolean isSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(boolean secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public ArrayList<String> getUserIds() {
+ return userIds;
+ }
+
+ public void setUserIds(ArrayList<String> userIds) {
+ this.userIds = userIds;
+ }
+
/**
* Constructor for later querying from keyserver
*/
@@ -131,14 +209,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
userIds = new ArrayList<String>();
}
- public boolean isSelected() {
- return mSelected;
- }
-
- public void setSelected(boolean selected) {
- this.mSelected = selected;
- }
-
/**
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@@ -164,27 +234,41 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
for (String userId : new IterableIterator<String>(pgpKeyRing.getPublicKey().getUserIDs())) {
userIds.add(userId);
}
+
this.keyId = pgpKeyRing.getPublicKey().getKeyID();
+ this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
- this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
- .getFingerprint(), true);
- this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
+ this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
+ .getFingerprint());
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
- int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
- if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
- || algorithm == PGPPublicKey.RSA_SIGN) {
- this.algorithm = "RSA";
- } else if (algorithm == PGPPublicKey.DSA) {
- this.algorithm = "DSA";
- } else if (algorithm == PGPPublicKey.ELGAMAL_ENCRYPT
- || algorithm == PGPPublicKey.ELGAMAL_GENERAL) {
- this.algorithm = "ElGamal";
- } else if (algorithm == PGPPublicKey.EC || algorithm == PGPPublicKey.ECDSA) {
- this.algorithm = "ECC";
- } else {
- // TODO: with resources
- this.algorithm = "unknown";
- }
+ final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
+ this.algorithm = getAlgorithmFromId(algorithm);
+ }
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ private static final SparseArray<String> ALGORITHM_IDS = new SparseArray<String>() {{
+ put(-1, "unknown"); // TODO: with resources
+ put(0, "unencrypted");
+ put(PGPPublicKey.RSA_GENERAL, "RSA");
+ put(PGPPublicKey.RSA_ENCRYPT, "RSA");
+ put(PGPPublicKey.RSA_SIGN, "RSA");
+ put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal");
+ put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal");
+ put(PGPPublicKey.DSA, "DSA");
+ put(PGPPublicKey.EC, "ECC");
+ put(PGPPublicKey.ECDSA, "ECC");
+ put(PGPPublicKey.ECDH, "ECC");
+ }};
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ public static String getAlgorithmFromId(int algorithmId) {
+ return (ALGORITHM_IDS.get(algorithmId) != null ?
+ ALGORITHM_IDS.get(algorithmId) :
+ ALGORITHM_IDS.get(-1));
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
index a4dd06e40..259e14319 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.HkpKeyServer;
import org.sufficientlysecure.keychain.util.KeyServer;
@@ -53,7 +54,12 @@ public class ImportKeysListServerLoader
return mEntryListWrapper;
}
- queryServer(mServerQuery, mKeyServer);
+ if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) {
+ Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!");
+ queryServer(mServerQuery, mKeyServer, true);
+ } else {
+ queryServer(mServerQuery, mKeyServer, false);
+ }
return mEntryListWrapper;
}
@@ -84,14 +90,30 @@ public class ImportKeysListServerLoader
/**
* Query keyserver
*/
- private void queryServer(String query, String keyServer) {
+ private void queryServer(String query, String keyServer, boolean enforceFingerprint) {
HkpKeyServer server = new HkpKeyServer(keyServer);
try {
ArrayList<ImportKeysListEntry> searchResult = server.search(query);
mEntryList.clear();
// add result to data
- mEntryList.addAll(searchResult);
+ if (enforceFingerprint) {
+ String fingerprint = query.substring(2);
+ Log.d(Constants.TAG, "fingerprint: " + fingerprint);
+ // query must return only one result!
+ if (searchResult.size() > 0) {
+ ImportKeysListEntry uniqueEntry = searchResult.get(0);
+ /*
+ * set fingerprint explicitly after query
+ * to enforce a check when the key is imported by KeychainIntentService
+ */
+ uniqueEntry.setFingerPrintHex(fingerprint);
+ uniqueEntry.setSelected(true);
+ mEntryList.add(uniqueEntry);
+ }
+ } else {
+ mEntryList.addAll(searchResult);
+ }
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
} catch (KeyServer.InsufficientQuery e) {
Log.e(Constants.TAG, "InsufficientQuery", e);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
index c997599bd..5b5d316b6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
@@ -20,7 +20,11 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.widget.ArrayAdapter;
-import java.util.*;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
public class KeyValueSpinnerAdapter extends ArrayAdapter<String> {
private final HashMap<Integer, String> mData;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
new file mode 100644
index 000000000..fd864eb09
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v7.app.ActionBarActivity;
+
+import java.util.ArrayList;
+
+public class PagerTabStripAdapter extends FragmentPagerAdapter {
+ private final Context mContext;
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+
+ static final class TabInfo {
+ public final Class<?> clss;
+ public final Bundle args;
+ public final String title;
+
+ TabInfo(Class<?> clss, Bundle args, String title) {
+ this.clss = clss;
+ this.args = args;
+ this.title = title;
+ }
+ }
+
+ public PagerTabStripAdapter(ActionBarActivity activity) {
+ super(activity.getSupportFragmentManager());
+ mContext = activity;
+ }
+
+ public void addTab(Class<?> clss, Bundle args, String title) {
+ TabInfo info = new TabInfo(clss, args, title);
+ mTabs.add(info);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ TabInfo info = mTabs.get(position);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabs.get(position).title;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index beb76fc10..fbbb9caa4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -115,7 +115,7 @@ public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter {
// TODO: needed to key id to no?
keyId.setText(R.string.no_key);
long masterKeyId = cursor.getLong(mIndexMasterKeyId);
- keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId));
+ keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId));
// TODO: needed to set unknown_status?
status.setText(R.string.unknown_status);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
index f435d46ef..9ddfa90be 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
@@ -36,12 +36,12 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
- private final Class<?> mClss;
- private final Bundle mArgs;
+ public final Class<?> clss;
+ public final Bundle args;
- TabInfo(Class<?> mClss, Bundle mArgs) {
- this.mClss = mClss;
- this.mArgs = mArgs;
+ TabInfo(Class<?> clss, Bundle args) {
+ this.clss = clss;
+ this.args = args;
}
}
@@ -71,7 +71,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.mClss.getName(), info.mArgs);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
index 153a3f266..64b735bfa 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -18,27 +18,36 @@
package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
+import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import java.util.Date;
+
public class ViewKeyKeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private int mIndexKeyId;
private int mIndexAlgorithm;
private int mIndexKeySize;
- private int mIndexIsMasterKey;
+ private int mIndexRank;
private int mIndexCanCertify;
private int mIndexCanEncrypt;
private int mIndexCanSign;
+ private int mIndexRevokedKey;
+ private int mIndexExpiry;
+
+ private ColorStateList mDefaultTextColor;
public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -66,10 +75,12 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
mIndexKeyId = cursor.getColumnIndexOrThrow(Keys.KEY_ID);
mIndexAlgorithm = cursor.getColumnIndexOrThrow(Keys.ALGORITHM);
mIndexKeySize = cursor.getColumnIndexOrThrow(Keys.KEY_SIZE);
- mIndexIsMasterKey = cursor.getColumnIndexOrThrow(Keys.IS_MASTER_KEY);
+ mIndexRank = cursor.getColumnIndexOrThrow(Keys.RANK);
mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY);
mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT);
mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN);
+ mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED);
+ mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY);
}
}
@@ -77,20 +88,21 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId));
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
cursor.getInt(mIndexKeySize));
keyId.setText(keyIdStr);
-
keyDetails.setText("(" + algorithmStr + ")");
- if (cursor.getInt(mIndexIsMasterKey) != 1) {
+ if (cursor.getInt(mIndexRank) == 0) {
masterKeyIcon.setVisibility(View.INVISIBLE);
} else {
masterKeyIcon.setVisibility(View.VISIBLE);
@@ -113,11 +125,51 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
} else {
signIcon.setVisibility(View.VISIBLE);
}
+
+ boolean valid = true;
+ if (cursor.getInt(mIndexRevokedKey) > 0) {
+ revokedKeyIcon.setVisibility(View.VISIBLE);
+
+ valid = false;
+ } else {
+ keyId.setTextColor(mDefaultTextColor);
+ keyDetails.setTextColor(mDefaultTextColor);
+ keyExpiry.setTextColor(mDefaultTextColor);
+
+ revokedKeyIcon.setVisibility(View.GONE);
+ }
+
+ if (!cursor.isNull(mIndexExpiry)) {
+ Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000);
+
+ valid = valid && expiryDate.after(new Date());
+ keyExpiry.setText("(" +
+ context.getString(R.string.label_expiry) + ": " +
+ DateFormat.getDateFormat(context).format(expiryDate) + ")");
+
+ keyExpiry.setVisibility(View.VISIBLE);
+ } else {
+ keyExpiry.setVisibility(View.GONE);
+ }
+ // if key is expired or revoked, strike through text
+ if (!valid) {
+ keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
+ keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
+ keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
+ }
+ keyId.setEnabled(valid);
+ keyDetails.setEnabled(valid);
+ keyExpiry.setEnabled(valid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.view_key_keys_item, null);
+ View view = mInflater.inflate(R.layout.view_key_keys_item, null);
+ if (mDefaultTextColor == null) {
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ mDefaultTextColor = keyId.getTextColors();
+ }
+ return view;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
index 2778ed08c..2677a1a1a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -39,7 +39,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
private int mIndexUserId, mIndexRank;
private int mVerifiedId;
- final private ArrayList<Boolean> mCheckStates;
+ private final ArrayList<Boolean> mCheckStates;
public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) {
super(context, c, flags);
@@ -57,9 +57,9 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
@Override
public Cursor swapCursor(Cursor newCursor) {
initIndex(newCursor);
- if(mCheckStates != null) {
+ if (mCheckStates != null) {
mCheckStates.clear();
- if(newCursor != null) {
+ if (newCursor != null) {
int count = newCursor.getCount();
mCheckStates.ensureCapacity(count);
// initialize to true (use case knowledge: we usually want to sign all uids)
@@ -84,7 +84,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
if (cursor != null) {
mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK);
- mVerifiedId = cursor.getColumnIndexOrThrow("verified");
+ // mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED);
}
}
@@ -106,7 +106,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
}
vAddress.setText(userId[1]);
- int verified = cursor.getInt(mVerifiedId);
+ int verified = 1; // cursor.getInt(mVerifiedId);
// TODO introduce own resource for this :)
if(verified > 0)
vVerified.setImageResource(android.R.drawable.presence_online);
@@ -114,8 +114,9 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
vVerified.setImageResource(android.R.drawable.presence_invisible);
// don't care further if checkboxes aren't shown
- if(mCheckStates == null)
+ if (mCheckStates == null) {
return;
+ }
final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox);
final int position = cursor.getPosition();
@@ -138,8 +139,8 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
public ArrayList<String> getSelectedUserIds() {
ArrayList<String> result = new ArrayList<String>();
- for(int i = 0; i < mCheckStates.size(); i++) {
- if(mCheckStates.get(i)) {
+ for (int i = 0; i < mCheckStates.size(); i++) {
+ if (mCheckStates.get(i)) {
mCursor.moveToPosition(i);
result.add(mCursor.getString(mIndexUserId));
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
index a41bc2bee..ad558a81e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
@@ -25,6 +25,7 @@ import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import org.sufficientlysecure.keychain.Id;
@@ -113,21 +114,8 @@ public class CreateKeyDialogFragment extends DialogFragment {
public void onClick(DialogInterface di, int id) {
di.dismiss();
try {
- int nKeyIndex = keySize.getSelectedItemPosition();
- switch (nKeyIndex) {
- case 0:
- mNewKeySize = 512;
- break;
- case 1:
- mNewKeySize = 1024;
- break;
- case 2:
- mNewKeySize = 2048;
- break;
- case 3:
- mNewKeySize = 4096;
- break;
- }
+ final String selectedItem = (String) keySize.getSelectedItem();
+ mNewKeySize = Integer.parseInt(selectedItem);
} catch (NumberFormatException e) {
mNewKeySize = 0;
}
@@ -145,7 +133,27 @@ public class CreateKeyDialogFragment extends DialogFragment {
}
});
- return dialog.create();
+ final AlertDialog alertDialog = dialog.create();
+
+ final AdapterView.OnItemSelectedListener weakRsaListener = new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ final Choice selectedAlgorithm = (Choice) algorithm.getSelectedItem();
+ final int selectedKeySize = Integer.parseInt((String) keySize.getSelectedItem());
+ final boolean isWeakRsa = (selectedAlgorithm.getId() == Id.choice.algorithm.rsa &&
+ selectedKeySize <= 1024);
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!isWeakRsa);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ };
+
+ keySize.setOnItemSelectedListener(weakRsaListener);
+ algorithm.setOnItemSelectedListener(weakRsaListener);
+
+ return alertDialog;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index b067010df..b4c38184c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -87,11 +87,11 @@ public class DeleteFileDialogFragment extends DialogFragment {
false,
null);
- // Message is received after deleting is done in ApgService
+ // Message is received after deleting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler =
new KeychainIntentServiceHandler(activity, deletingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 1bcf5b33c..72ea4c013 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,161 +20,125 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
-import android.database.Cursor;
-import android.net.Uri;
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;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.ArrayList;
+import java.util.HashMap;
public class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
- private static final String ARG_KEY_TYPE = "key_type";
+ private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
public static final int MESSAGE_OKAY = 1;
+ public static final int MESSAGE_ERROR = 0;
+
+ private boolean mIsSingleSelection = false;
- public static final String MESSAGE_NOT_DELETED = "not_deleted";
+ private TextView mMainMessage;
+ private CheckBox mCheckDeleteSecret;
+ private LinearLayout mDeleteSecretKeyView;
+ private View mInflateView;
private Messenger mMessenger;
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
- int keyType) {
+ public static DeleteKeyDialogFragment newInstance(Messenger messenger,
+ long[] masterKeyIds) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
- args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
- args.putInt(ARG_KEY_TYPE, keyType);
+ args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
+ //We don't need the key type
frag.setArguments(args);
return frag;
}
- /**
- * Creates dialog
- */
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
+
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
- final int keyType = getArguments().getInt(ARG_KEY_TYPE);
+ final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(R.string.warning);
- if (keyRingRowIds.length == 1) {
- Uri dataUri;
- if (keyType == Id.type.public_key) {
- dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0]));
- } else {
- dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0]));
- }
- String userId = ProviderHelper.getUserId(activity, dataUri);
+ //Setup custom View to display in AlertDialog
+ LayoutInflater inflater = activity.getLayoutInflater();
+ mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
+ builder.setView(mInflateView);
+
+ mDeleteSecretKeyView = (LinearLayout) mInflateView.findViewById(R.id.deleteSecretKeyView);
+ mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
+ mCheckDeleteSecret = (CheckBox) mInflateView.findViewById(R.id.checkDeleteSecret);
- builder.setMessage(getString(
- keyType == Id.type.public_key ? R.string.key_deletion_confirmation
- : R.string.secret_key_deletion_confirmation, userId));
+ builder.setTitle(R.string.warning);
+
+ // If only a single key has been selected
+ if (masterKeyIds.length == 1) {
+ mIsSingleSelection = true;
+
+ long masterKeyId = masterKeyIds[0];
+
+ HashMap<String, Object> data = ProviderHelper.getUnifiedData(activity, masterKeyId, new String[]{
+ KeyRings.USER_ID,
+ KeyRings.HAS_SECRET
+ }, new int[] { ProviderHelper.FIELD_TYPE_STRING, ProviderHelper.FIELD_TYPE_INTEGER });
+ String userId = (String) data.get(KeyRings.USER_ID);
+ boolean hasSecret = ((Long) data.get(KeyRings.HAS_SECRET)) == 1;
+
+ // Hide the Checkbox and TextView since this is a single selection,user will be notified through message
+ mDeleteSecretKeyView.setVisibility(View.GONE);
+ // Set message depending on which key it is.
+ mMainMessage.setText(getString(
+ hasSecret ? R.string.secret_key_deletion_confirmation
+ : R.string.public_key_deletetion_confirmation,
+ userId));
} else {
- builder.setMessage(R.string.key_deletion_confirmation_multi);
+ mDeleteSecretKeyView.setVisibility(View.VISIBLE);
+ mMainMessage.setText(R.string.key_deletion_confirmation_multi);
}
builder.setIcon(R.drawable.ic_dialog_alert_holo_light);
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
-
@Override
- public void onClick(DialogInterface dialog, int id) {
- ArrayList<String> notDeleted = new ArrayList<String>();
-
- if (keyType == Id.type.public_key) {
- Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri();
- String[] projection = new String[]{
- KeychainContract.KeyRings._ID, // 0
- KeychainContract.KeyRings.MASTER_KEY_ID, // 1
- KeychainContract.UserIds.USER_ID // 2
- };
-
- // make selection with all entries where _ID is one of the given row ids
- String selection = KeychainDatabase.Tables.KEY_RINGS + "." +
- KeychainContract.KeyRings._ID + " IN(";
- String selectionIDs = "";
- for (int i = 0; i < keyRingRowIds.length; i++) {
- selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'";
- if (i + 1 < keyRingRowIds.length) {
- selectionIDs += ",";
- }
- }
- selection += selectionIDs + ")";
-
- Cursor cursor = activity.getContentResolver().query(queryUri, projection,
- selection, null, null);
-
- long rowId;
- long masterKeyId;
- String userId;
- try {
- while (cursor != null && cursor.moveToNext()) {
- rowId = cursor.getLong(0);
- masterKeyId = cursor.getLong(1);
- userId = cursor.getString(2);
-
- Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId
- + ", userId: " + userId);
-
- // check if a corresponding secret key exists...
- Cursor secretCursor = activity.getContentResolver().query(
- KeychainContract.KeyRings
- .buildSecretKeyRingsByMasterKeyIdUri(
- String.valueOf(masterKeyId)),
- null, null, null, null
- );
- if (secretCursor != null && secretCursor.getCount() > 0) {
- notDeleted.add(userId);
- } else {
- // it is okay to delete this key, no secret key found!
- ProviderHelper.deletePublicKeyRing(activity, rowId);
- }
- if (secretCursor != null) {
- secretCursor.close();
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- } else {
- for (long keyRowId : keyRingRowIds) {
- ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
- }
+ public void onClick(DialogInterface dialog, int which) {
+
+ boolean success = false;
+ for(long masterKeyId : masterKeyIds) {
+ int count = activity.getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null
+ );
+ success = count > 0;
}
-
- dismiss();
-
- if (notDeleted.size() > 0) {
- Bundle data = new Bundle();
- data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted);
- sendMessageToHandler(MESSAGE_OKAY, data);
- } else {
+ if (success) {
sendMessageToHandler(MESSAGE_OKAY, null);
+ } else {
+ sendMessageToHandler(MESSAGE_ERROR, null);
}
+ dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -184,6 +148,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
dismiss();
}
});
+
return builder.create();
}
@@ -198,7 +163,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {
if (data != null) {
msg.setData(data);
}
-
try {
mMessenger.send(msg);
} catch (RemoteException e) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 271219fa3..a3feab959 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -24,10 +24,12 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -59,11 +61,34 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_CANCEL = 2;
+ public static final String MESSAGE_DATA_PASSPHRASE = "passphrase";
+
private Messenger mMessenger;
private EditText mPassphraseEditText;
private boolean mCanKB;
/**
+ * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
+ * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
+ * for a symmetric passphrase
+ */
+ public static void show(FragmentActivity context, long keyId, Handler returnHandler) {
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ try {
+ PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(context,
+ messenger, keyId);
+
+ passphraseDialog.show(context.getSupportFragmentManager(), "passphraseDialog");
+ } catch (PgpGeneralException 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);
+ }
+ }
+
+ /**
* Creates new instance of this dialog fragment
*
* @param secretKeyId secret key id you want to use
@@ -114,10 +139,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
} else {
- // TODO: by master key id???
- secretKey = PgpKeyHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByKeyId(activity,
- secretKeyId));
- // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId));
+ secretKey = ProviderHelper.getPGPSecretKeyRing(activity, secretKeyId).getSecretKey();
if (secretKey == null) {
alert.setTitle(R.string.title_key_not_found);
@@ -173,7 +195,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return;
} else {
clickSecretKey = PgpKeyHelper.getKeyNum(ProviderHelper
- .getPGPSecretKeyRingByKeyId(activity, secretKeyId),
+ .getPGPSecretKeyRingWithKeyId(activity, secretKeyId),
curKeyIndex);
curKeyIndex++; // does post-increment work like C?
continue;
@@ -209,7 +231,11 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
passphrase);
}
- sendMessageToHandler(MESSAGE_OKAY);
+ // also return passphrase back to activity
+ Bundle data = new Bundle();
+ data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
+
+ sendMessageToHandler(MESSAGE_OKAY, data);
}
});
@@ -278,4 +304,25 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
}
+ /**
+ * Send message back to handler which is initialized in a activity
+ *
+ * @param what Message integer you want to send
+ */
+ private void sendMessageToHandler(Integer what, Bundle data) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ 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);
+ }
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
index b501ba230..b6ff139df 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
@@ -31,6 +31,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.QrCodeUtils;
@@ -89,29 +90,33 @@ public class ShareQrCodeDialogFragment extends DialogFragment {
if (mFingerprintOnly) {
alert.setPositiveButton(R.string.btn_okay, null);
- byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
+ byte[] blob = (byte[]) ProviderHelper.getGenericData(
+ getActivity(), KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if(blob == null) {
+ // TODO error handling?!
+ return null;
+ }
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
-
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
setQrCode(content);
} else {
mText.setText(R.string.share_qr_code_dialog_start);
- // TODO
+ // TODO works, but
long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
-
// get public keyring as ascii armored string
ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
- getActivity(), dataUri, new long[]{masterKeyId});
+ getActivity(), new long[] { masterKeyId });
// TODO: binary?
content = keyringArmored.get(0);
// OnClickListener are set in onResume to prevent automatic dismissing of Dialogs
- // http://stackoverflow.com/questions/2620444/how-to-prevent-a-dialog-from-closing-when-a-button-is-clicked
+ // http://bit.ly/O5vfaR
alert.setPositiveButton(R.string.btn_next, null);
alert.setNegativeButton(android.R.string.cancel, null);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
index 1cf510d3a..7b21c189d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
@@ -18,8 +18,10 @@ package org.sufficientlysecure.keychain.ui.widget;
public interface Editor {
public interface EditorListener {
- public void onDeleted(Editor editor);
+ public void onDeleted(Editor editor, boolean wasNewItem);
+ public void onEdited();
}
public void setEditorListener(EditorListener listener);
+ public boolean needsSaving();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
new file mode 100644
index 000000000..6b2f3bf06
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Class representing a LinearLayout that can fold and hide it's content when pressed
+ * To use just add the following to your xml layout
+
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED"
+ custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"
+ custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED"
+ custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED">
+
+ <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
+ */
+public class FoldableLinearLayout extends LinearLayout {
+
+ private FontAwesomeText mFoldableIcon;
+ private boolean mFolded;
+ private boolean mHasMigrated = false;
+ private Integer mShortAnimationDuration = null;
+ private TextView mFoldableTextView = null;
+ private LinearLayout mFoldableContainer = null;
+ private View mFoldableLayout = null;
+
+ private String mFoldedIconName;
+ private String mUnFoldedIconName;
+ private String mFoldedLabel;
+ private String mUnFoldedLabel;
+
+ public FoldableLinearLayout(Context context) {
+ super(context);
+ processAttributes(context, null);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ /**
+ * Load given attributes to inner variables,
+ * @param context
+ * @param attrs
+ */
+ private void processAttributes(Context context, AttributeSet attrs) {
+ if (attrs != null) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.FoldableLinearLayout, 0, 0);
+ mFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_foldedIcon);
+ mUnFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_unFoldedIcon);
+ mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel);
+ mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel);
+ a.recycle();
+ }
+ // If any attribute isn't found then set a default one
+ mFoldedIconName = (mFoldedIconName == null) ? "fa-chevron-right" : mFoldedIconName;
+ mUnFoldedIconName = (mUnFoldedIconName == null) ? "fa-chevron-down" : mUnFoldedIconName;
+ mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel;
+ mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // if the migration has already happened
+ // there is no need to move any children
+ if (!mHasMigrated) {
+ migrateChildrenToContainer();
+ mHasMigrated = true;
+ }
+
+ initialiseInnerViews();
+
+ super.onFinishInflate();
+ }
+
+ /**
+ * Migrates Child views as declared in xml to the inner foldableContainer
+ */
+ private void migrateChildrenToContainer() {
+ // Collect children of FoldableLinearLayout as declared in XML
+ int childNum = getChildCount();
+ View[] children = new View[childNum];
+
+ for (int i = 0; i < childNum; i++) {
+ children[i] = getChildAt(i);
+ }
+ if (children[0].getId() == R.id.foldableControl) {
+
+ }
+
+ // remove all of them from FoldableLinearLayout
+ detachAllViewsFromParent();
+
+ // Inflate the inner foldable_linearlayout.xml
+ LayoutInflater inflator = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mFoldableLayout = inflator.inflate(R.layout.foldable_linearlayout, this, true);
+ mFoldableContainer = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableContainer);
+
+ // Push previously collected children into foldableContainer.
+ for (int i = 0; i < childNum; i++) {
+ addView(children[i]);
+ }
+ }
+
+ private void initialiseInnerViews() {
+ mFoldableIcon = (FontAwesomeText) mFoldableLayout.findViewById(R.id.foldableIcon);
+ mFoldableIcon.setIcon(mFoldedIconName);
+ mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText);
+ mFoldableTextView.setText(mFoldedLabel);
+
+ // retrieve and cache the system's short animation time
+ mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+
+ LinearLayout foldableControl = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableControl);
+ foldableControl.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mFolded = !mFolded;
+ if (mFolded) {
+ mFoldableIcon.setIcon(mUnFoldedIconName);
+ mFoldableContainer.setVisibility(View.VISIBLE);
+ AlphaAnimation animation = new AlphaAnimation(0f, 1f);
+ animation.setDuration(mShortAnimationDuration);
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mUnFoldedLabel);
+
+ } else {
+ mFoldableIcon.setIcon(mFoldedIconName);
+ AlphaAnimation animation = new AlphaAnimation(1f, 0f);
+ animation.setDuration(mShortAnimationDuration);
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) { }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // making sure that at the end the container is completely removed from view
+ mFoldableContainer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) { }
+ });
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mFoldedLabel);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Adds provided child view to foldableContainer View
+ * @param child
+ */
+ @Override
+ public void addView(View child) {
+ if (mFoldableContainer != null) {
+ mFoldableContainer.addView(child);
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 7e0acfa28..c7bd1c987 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -26,17 +26,30 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.LinearLayout;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
-import org.sufficientlysecure.keychain.Id;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
import java.text.DateFormat;
-import java.util.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
@@ -47,11 +60,30 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
BootstrapButton mDeleteButton;
TextView mAlgorithm;
TextView mKeyId;
- Spinner mUsage;
TextView mCreationDate;
BootstrapButton mExpiryDateButton;
GregorianCalendar mCreatedDate;
GregorianCalendar mExpiryDate;
+ GregorianCalendar mOriginalExpiryDate = null;
+ CheckBox mChkCertify;
+ CheckBox mChkSign;
+ CheckBox mChkEncrypt;
+ CheckBox mChkAuthenticate;
+ int mUsage;
+ int mOriginalUsage;
+ boolean mIsNewKey;
+
+ private CheckBox.OnCheckedChangeListener mCheckChanged = new CheckBox.OnCheckedChangeListener()
+ {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
private int mDatePickerResultCount = 0;
private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
@@ -61,7 +93,20 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (mDatePickerResultCount++ == 0) {
GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
date.set(year, monthOfYear, dayOfMonth);
- setExpiryDate(date);
+ if (mOriginalExpiryDate != null) {
+ long numDays = (date.getTimeInMillis() / 86400000) -
+ (mOriginalExpiryDate.getTimeInMillis() / 86400000);
+ if (numDays == 0) {
+ setExpiryDate(mOriginalExpiryDate);
+ } else {
+ setExpiryDate(date);
+ }
+ } else {
+ setExpiryDate(date);
+ }
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
};
@@ -83,21 +128,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry);
- mUsage = (Spinner) findViewById(R.id.usage);
- Choice choices[] = {
- new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)),
- new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)),
- new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)), };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
+ mChkCertify = (CheckBox) findViewById(R.id.chkCertify);
+ mChkCertify.setOnCheckedChangeListener(mCheckChanged);
+ mChkSign = (CheckBox) findViewById(R.id.chkSign);
+ mChkSign.setOnCheckedChangeListener(mCheckChanged);
+ mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt);
+ mChkEncrypt.setOnCheckedChangeListener(mCheckChanged);
+ mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate);
+ mChkAuthenticate.setOnCheckedChangeListener(mCheckChanged);
setExpiryDate(null);
@@ -109,7 +150,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
}
/*
- * Using custom DatePickerDialog which overrides the setTitle because
+ * Using custom DatePickerDialog which overrides the setTitle because
* the DatePickerDialog title is buggy (unix warparound bug).
* See: https://code.google.com/p/android/issues/detail?id=49066
*/
@@ -125,6 +166,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
setExpiryDate(null);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
});
@@ -152,15 +196,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
super.onFinishInflate();
}
- public void setCanEdit(boolean bCanEdit) {
- if (!bCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ if (!canBeEdited) {
mDeleteButton.setVisibility(View.INVISIBLE);
- mUsage.setEnabled(false);
mExpiryDateButton.setEnabled(false);
+ mChkSign.setEnabled(false); //certify is always disabled
+ mChkEncrypt.setEnabled(false);
+ mChkAuthenticate.setEnabled(false);
}
}
- public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) {
+ public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@@ -175,47 +221,46 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
Vector<Choice> choices = new Vector<Choice>();
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
- if (!isElGamalKey) {
- choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)));
+ if (isElGamalKey) {
+ mChkSign.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_sign);
+ table.removeView(row);
}
- if (!mIsMasterKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)));
+ if (isDSAKey) {
+ mChkEncrypt.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_encrypt);
+ table.removeView(row);
}
- if (!isElGamalKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)));
+ if (!mIsMasterKey) {
+ mChkCertify.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_certify);
+ table.removeView(row);
+ } else {
+ TextView mLabelUsage2 = (TextView) findViewById(R.id.label_usage2);
+ mLabelUsage2.setVisibility(View.INVISIBLE);
}
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
-
- // Set value in choice dropdown to key
int selectId = 0;
- if (PgpKeyHelper.isEncryptionKey(key)) {
- if (PgpKeyHelper.isSigningKey(key)) {
- selectId = Id.choice.usage.sign_and_encrypt;
- } else {
- selectId = Id.choice.usage.encrypt_only;
- }
+ mIsNewKey = isNewKey;
+ if (isNewKey) {
+ mUsage = usage;
+ mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
+ mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
+ mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
+ ((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
+ mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
} else {
- // set usage if it is predefined
- if (usage != -1) {
- selectId = usage;
- } else {
- selectId = Id.choice.usage.sign_only;
- }
-
- }
-
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == selectId) {
- mUsage.setSelection(i);
- break;
+ mUsage = PgpKeyHelper.getKeyUsage(key);
+ mOriginalUsage = mUsage;
+ if (key.isMasterKey()) {
+ mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
}
+ mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
+ mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
+ mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
}
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
@@ -228,6 +273,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} else {
cal.setTime(PgpKeyHelper.getExpiryDate(key));
setExpiryDate(cal);
+ mOriginalExpiryDate = cal;
}
}
@@ -241,7 +287,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewKey);
}
}
}
@@ -273,9 +319,48 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
- return ((Choice) mUsage.getSelectedItem()).getId();
+ mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) |
+ (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
+ mUsage = (mUsage & ~KeyFlags.SIGN_DATA) |
+ (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) |
+ (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) |
+ (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
+ mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) |
+ (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
+
+ return mUsage;
+ }
+
+ public boolean needsSaving() {
+ if (mIsNewKey) {
+ return true;
+ }
+
+ boolean retval = (getUsage() != mOriginalUsage);
+
+ boolean dateChanged;
+ boolean mOEDNull = (mOriginalExpiryDate == null);
+ boolean mEDNull = (mExpiryDate == null);
+ if (mOEDNull != mEDNull) {
+ dateChanged = true;
+ } else {
+ if (mOEDNull) {
+ //both null, no change
+ dateChanged = false;
+ } else {
+ dateChanged = ((mExpiryDate.compareTo(mOriginalExpiryDate)) != 0);
+ }
+ }
+ retval |= dateChanged;
+
+ return retval;
}
+ public boolean getIsNewKey() {
+ return mIsNewKey;
+ }
}
class ExpiryDatePickerDialog extends DatePickerDialog {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index f92c7532a..171763672 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -66,11 +66,16 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, false);
}
}
}
+ @Override
+ public boolean needsSaving() {
+ return false;
+ }
+
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index e5b6003b1..fb59cd3b7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -32,7 +32,10 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
@@ -44,23 +47,33 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.util.Choice;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Vector;
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener {
+public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor {
private LayoutInflater mInflater;
private BootstrapButton mPlusButton;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
+ private EditorListener mEditorListener = null;
private Choice mNewKeyAlgorithmChoice;
private int mNewKeySize;
- private boolean mCanEdit = true;
+ private boolean mOldItemDeleted = false;
+ private ArrayList<String> mDeletedIDs = new ArrayList<String>();
+ private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
+ private boolean mCanBeEdited = true;
private ActionBarActivity mActivity;
private ProgressDialogFragment mGeneratingDialog;
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+
public SectionView(Context context) {
super(context);
mActivity = (ActionBarActivity) context;
@@ -94,9 +107,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
}
- public void setCanEdit(boolean bCanEdit) {
- mCanEdit = bCanEdit;
- if (!mCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ mCanBeEdited = canBeEdited;
+ if (!mCanBeEdited) {
mPlusButton.setVisibility(View.INVISIBLE);
}
}
@@ -124,8 +137,26 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
/**
* {@inheritDoc}
*/
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
+ mOldItemDeleted |= !wasNewItem;
+ if (mOldItemDeleted) {
+ if (mType == Id.type.user_id) {
+ mDeletedIDs.add(((UserIdEditor) editor).getOriginalID());
+ } else if (mType == Id.type.key) {
+ mDeletedKeys.add(((KeyEditor) editor).getValue());
+ }
+ }
this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+
+ @Override
+ public void onEdited() {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
protected void updateEditorsVisible() {
@@ -133,20 +164,118 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
}
+ public boolean needsSaving() {
+ //check each view for needs saving, take account of deleted items
+ boolean ret = mOldItemDeleted;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ ret |= editor.needsSaving();
+ if (mType == Id.type.user_id) {
+ ret |= ((UserIdEditor) editor).primarySwapped();
+ }
+ }
+ return ret;
+ }
+
+ public boolean primaryChanged() {
+ boolean ret = false;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id) {
+ ret |= ((UserIdEditor) editor).primarySwapped();
+ }
+ }
+ return ret;
+ }
+
+ public String getOriginalPrimaryID() {
+ //NB: this will have to change when we change how Primary IDs are chosen, and so we need to be
+ // careful about where Master key capabilities are stored... multiple primaries and
+ // revoked ones make this harder than the simple case we are continuing to assume here
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id) {
+ if (((UserIdEditor) editor).getIsOriginallyMainUserID()) {
+ return ((UserIdEditor) editor).getOriginalID();
+ }
+ }
+ }
+ return null;
+ }
+
+ public ArrayList<String> getOriginalIDs() {
+ ArrayList<String> orig = new ArrayList<String>();
+ if (mType == Id.type.user_id) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
+ if (editor.isMainUserId()) {
+ orig.add(0, editor.getOriginalID());
+ } else {
+ orig.add(editor.getOriginalID());
+ }
+ }
+ return orig;
+ } else {
+ return null;
+ }
+ }
+
+ public ArrayList<String> getDeletedIDs() {
+ return mDeletedIDs;
+ }
+
+ public ArrayList<PGPSecretKey> getDeletedKeys() {
+ return mDeletedKeys;
+ }
+
+ public List<Boolean> getNeedsSavingArray() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ mList.add(editor.needsSaving());
+ }
+ return mList;
+ }
+
+ public List<Boolean> getNewIDFlags() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
+ if (editor.isMainUserId()) {
+ mList.add(0, editor.getIsNewID());
+ } else {
+ mList.add(editor.getIsNewID());
+ }
+ }
+ return mList;
+ }
+
+ public List<Boolean> getNewKeysArray() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ if (mType == Id.type.key) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ KeyEditor editor = (KeyEditor) mEditors.getChildAt(i);
+ mList.add(editor.getIsNewKey());
+ }
+ }
+ return mList;
+ }
+
/**
* {@inheritDoc}
*/
public void onClick(View v) {
- if (mCanEdit) {
+ if (mCanBeEdited) {
switch (mType) {
case Id.type.user_id: {
UserIdEditor view = (UserIdEditor) mInflater.inflate(
R.layout.edit_key_user_id_item, mEditors, false);
view.setEditorListener(this);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
+ view.setValue("", mEditors.getChildCount() == 0, true);
mEditors.addView(view);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
break;
}
@@ -185,18 +314,15 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
mEditors, false);
view.setEditorListener(this);
- view.setValue(userId);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- view.setCanEdit(mCanEdit);
+ view.setValue(userId, mEditors.getChildCount() == 0, false);
+ view.setCanBeEdited(mCanBeEdited);
mEditors.addView(view);
}
this.updateEditorsVisible();
}
- public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages) {
+ public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != Id.type.key) {
return;
}
@@ -209,8 +335,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
false);
view.setEditorListener(this);
boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(list.get(i), isMasterKey, usages.get(i));
- view.setCanEdit(mCanEdit);
+ view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys);
+ view.setCanBeEdited(mCanBeEdited);
mEditors.addView(view);
}
@@ -256,11 +382,11 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
});
- // Message is received after generating is done in ApgService
+ // Message is received after generating is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
mGeneratingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -289,8 +415,15 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
- view.setValue(newKey, newKey.isMasterKey(), -1);
+ int usage = 0;
+ if (mEditors.getChildCount() == 0) {
+ usage = PGPKeyFlags.CAN_CERTIFY;
+ }
+ view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);
SectionView.this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index ed81b162e..2253872d5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -24,33 +24,36 @@ import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
+ private String mOriginalID;
private EditText mName;
+ private String mOriginalName;
private AutoCompleteTextView mEmail;
+ private String mOriginalEmail;
private EditText mComment;
+ private String mOriginalComment;
+ private boolean mOriginallyMainUserID;
+ private boolean mIsNewId;
- public static class NoNameException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoNameException(String message) {
- super(message);
- }
- }
-
- public void setCanEdit(boolean bCanEdit) {
- if (!bCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ if (!canBeEdited) {
mDeleteButton.setVisibility(View.INVISIBLE);
mName.setEnabled(false);
mIsMainUserId.setEnabled(false);
@@ -59,14 +62,6 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
}
}
- public static class NoEmailException extends Exception {
- static final long serialVersionUID = 0xf812773344L;
-
- public NoEmailException(String message) {
- super(message);
- }
- }
-
public static class InvalidEmailException extends Exception {
static final long serialVersionUID = 0xf812773345L;
@@ -83,6 +78,23 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
super(context, attrs);
}
+ TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
@Override
protected void onFinishInflate() {
setDrawingCacheEnabled(true);
@@ -94,8 +106,10 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
mIsMainUserId.setOnClickListener(this);
mName = (EditText) findViewById(R.id.name);
+ mName.addTextChangedListener(mTextWatcher);
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
mComment = (EditText) findViewById(R.id.comment);
+ mComment.addTextChangedListener(mTextWatcher);
mEmail.setThreshold(1); // Start working from first character
@@ -127,36 +141,45 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// remove drawable if email is empty
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
});
super.onFinishInflate();
}
- public void setValue(String userId) {
+ public void setValue(String userId, boolean isMainID, boolean isNewId) {
+
mName.setText("");
+ mOriginalName = "";
mComment.setText("");
+ mOriginalComment = "";
mEmail.setText("");
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mComment.setText(matcher.group(2));
- mEmail.setText(matcher.group(3));
- return;
+ mOriginalEmail = "";
+ mIsNewId = isNewId;
+ mOriginalID = userId;
+
+ String[] result = PgpKeyHelper.splitUserId(userId);
+ if (result[0] != null) {
+ mName.setText(result[0]);
+ mOriginalName = result[0];
}
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mEmail.setText(matcher.group(2));
- return;
+ if (result[1] != null) {
+ mEmail.setText(result[1]);
+ mOriginalEmail = result[1];
+ }
+ if (result[2] != null) {
+ mComment.setText(result[2]);
+ mOriginalComment = result[2];
}
+
+ mOriginallyMainUserID = isMainID;
+ setIsMainUserId(isMainID);
}
- public String getValue() throws NoNameException, NoEmailException {
+ public String getValue() {
String name = ("" + mName.getText()).trim();
String email = ("" + mEmail.getText()).trim();
String comment = ("" + mComment.getText()).trim();
@@ -173,16 +196,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// ok, empty one...
return userId;
}
-
- // otherwise make sure that name and email exist
- if (name.equals("")) {
- throw new NoNameException("need a name");
- }
-
- if (email.equals("")) {
- throw new NoEmailException("need an email");
- }
-
+ //TODO: check gpg accepts an entirely empty ID packet. specs say this is allowed
return userId;
}
@@ -192,7 +206,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
boolean wasMainUserId = mIsMainUserId.isChecked();
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewId);
}
if (wasMainUserId && parent.getChildCount() > 0) {
UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
@@ -207,6 +221,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
editor.setIsMainUserId(false);
}
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
@@ -221,4 +238,28 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
+
+ @Override
+ public boolean needsSaving() {
+ boolean retval = false; //(mOriginallyMainUserID != isMainUserId());
+ retval |= !(mOriginalName.equals(("" + mName.getText()).trim()));
+ retval |= !(mOriginalEmail.equals(("" + mEmail.getText()).trim()));
+ retval |= !(mOriginalComment.equals(("" + mComment.getText()).trim()));
+ retval |= mIsNewId;
+ return retval;
+ }
+
+ public boolean getIsOriginallyMainUserID() {
+ return mOriginallyMainUserID;
+ }
+
+ public boolean primarySwapped() {
+ return (mOriginallyMainUserID != isMainUserId());
+ }
+
+ public String getOriginalID() {
+ return mOriginalID;
+ }
+
+ public boolean getIsNewID() { return mIsNewId; }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
index 42fb03a3e..5efc732e4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.util;
-import android.text.Html;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -30,26 +29,30 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.net.*;
-import java.util.*;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/**
- * TODO:
- * rewrite to use machine readable output.
- * <p/>
- * see http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5
- * https://github.com/openpgp-keychain/openpgp-keychain/issues/259
- */
public class HkpKeyServer extends KeyServer {
private static class HttpError extends Exception {
private static final long serialVersionUID = 1718783705229428893L;
@@ -74,16 +77,69 @@ public class HkpKeyServer extends KeyServer {
private String mHost;
private short mPort;
- // example:
- // pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
- // href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge
- // &lt;joerg@joergrunge.de&gt;</a>
+ /**
+ * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>keyid</b>% = this is either the fingerprint or the key ID of the key.
+ * Either the 16-digit or 8-digit key IDs are acceptable, but obviously the fingerprint is best.
+ * </li>
+ * <li>%<b>algo</b>% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc).
+ * See <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a></li>
+ * <li>%<b>keylen</b>% = the key length (i.e. 1024, 2048, 4096, etc.)</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any
+ * order. The meaning of "disabled" is implementation-specific. Note that individual flags may
+ * be unimplemented, so the absence of a given flag does not necessarily mean the absence of the
+ * detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @see <a href="http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5.2">
+ * 5.2. Machine Readable Indexes</a>
+ * in Internet-Draft OpenPGP HTTP Keyserver Protocol Document
+ */
public static final Pattern PUB_KEY_LINE = Pattern
- .compile(
- "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
+ .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line
+ + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines
+ Pattern.CASE_INSENSITIVE);
+
+ /**
+ * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>escaped uid string</b>% = the user ID string, with HTTP %-escaping for anything that
+ * isn't 7-bit safe as well as for the ":" character. Any other characters may be escaped, as
+ * desired.</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any
+ * order. The meaning of "disabled" is implementation-specific. Note that individual flags may
+ * be unimplemented, so the absence of a given flag does not necessarily mean the absence of
+ * the detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ */
+ public static final Pattern UID_LINE = Pattern
+ .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)",
Pattern.CASE_INSENSITIVE);
- public static final Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
- | Pattern.CASE_INSENSITIVE);
private static final short PORT_DEFAULT = 11371;
@@ -136,6 +192,7 @@ public class HkpKeyServer extends KeyServer {
for (int i = 0; i < ips.length; ++i) {
try {
String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request;
+ Log.d(Constants.TAG, "hkp keyserver query: " + url);
URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
conn.setConnectTimeout(5000);
@@ -173,9 +230,9 @@ public class HkpKeyServer extends KeyServer {
} catch (UnsupportedEncodingException e) {
return null;
}
- String request = "/pks/lookup?op=index&search=" + encodedQuery;
+ String request = "/pks/lookup?op=index&options=mr&search=" + encodedQuery;
- String data = null;
+ String data;
try {
data = query(request);
} catch (HttpError e) {
@@ -193,48 +250,64 @@ public class HkpKeyServer extends KeyServer {
throw new QueryException("querying server(s) for '" + mHost + "' failed");
}
- Matcher matcher = PUB_KEY_LINE.matcher(data);
+ final Matcher matcher = PUB_KEY_LINE.matcher(data);
while (matcher.find()) {
- ImportKeysListEntry info = new ImportKeysListEntry();
- info.bitStrength = Integer.parseInt(matcher.group(1));
- info.algorithm = matcher.group(2);
- info.hexKeyId = "0x" + matcher.group(3);
- info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(3));
- String chunks[] = matcher.group(4).split("-");
-
- GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- tmpGreg.set(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]),
- Integer.parseInt(chunks[2]));
- info.date = tmpGreg.getTime();
- info.userIds = new ArrayList<String>();
- if (matcher.group(5).startsWith("*** KEY")) {
- info.revoked = true;
+ final ImportKeysListEntry entry = new ImportKeysListEntry();
+
+ entry.setBitStrength(Integer.parseInt(matcher.group(3)));
+
+ final int algorithmId = Integer.decode(matcher.group(2));
+ entry.setAlgorithm(ImportKeysListEntry.getAlgorithmFromId(algorithmId));
+
+ // group 1 contains the full fingerprint (v4) or the long key id if available
+ // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
+ String fingerprintOrKeyId = matcher.group(1);
+ if (fingerprintOrKeyId.length() > 16) {
+ entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
+ - 16, fingerprintOrKeyId.length()));
} else {
- String tmp = matcher.group(5).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+ // set key id only
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId);
}
- if (matcher.group(6).length() > 0) {
- Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
- while (matcher2.find()) {
- String tmp = matcher2.group(1).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+
+ final long creationDate = Long.parseLong(matcher.group(4));
+ final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ tmpGreg.setTimeInMillis(creationDate * 1000);
+ entry.setDate(tmpGreg.getTime());
+
+ entry.setRevoked(matcher.group(6).contains("r"));
+
+ ArrayList<String> userIds = new ArrayList<String>();
+ final String uidLines = matcher.group(7);
+ final Matcher uidMatcher = UID_LINE.matcher(uidLines);
+ while (uidMatcher.find()) {
+ String tmp = uidMatcher.group(1).trim();
+ if (tmp.contains("%")) {
+ try {
+ // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität".
+ tmp = (URLDecoder.decode(tmp, "UTF8"));
+ } catch (UnsupportedEncodingException ignored) {
+ // will never happen, because "UTF8" is supported
+ }
}
+ userIds.add(tmp);
}
- results.add(info);
- }
+ entry.setUserIds(userIds);
+ results.add(entry);
+ }
return results;
}
@Override
- public String get(long keyId) throws QueryException {
+ public String get(String keyIdHex) throws QueryException {
HttpClient client = new DefaultHttpClient();
try {
- HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
- + "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId));
-
+ String query = "http://" + mHost + ":" + mPort +
+ "/pks/lookup?op=get&options=mr&search=" + keyIdHex;
+ Log.d(Constants.TAG, "hkp keyserver get: " + query);
+ HttpGet get = new HttpGet(query);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new QueryException("not found");
@@ -257,13 +330,14 @@ public class HkpKeyServer extends KeyServer {
}
@Override
- public void add(String armoredText) throws AddKeyException {
+ public void add(String armoredKey) throws AddKeyException {
HttpClient client = new DefaultHttpClient();
try {
- HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
-
+ String query = "http://" + mHost + ":" + mPort + "/pks/add";
+ HttpPost post = new HttpPost(query);
+ Log.d(Constants.TAG, "hkp keyserver add: " + query);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
- nameValuePairs.add(new BasicNameValuePair("keytext", armoredText));
+ nameValuePairs.add(new BasicNameValuePair("keytext", armoredKey));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
index 092c14e00..28cfa11f2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2010-2014 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
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
index b95b3ee6a..ae87deb31 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
@@ -16,9 +16,10 @@
package org.sufficientlysecure.keychain.util;
+import com.google.zxing.integration.android.IntentIntegrator;
+
import android.content.Intent;
import android.support.v4.app.Fragment;
-import com.google.zxing.integration.android.IntentIntegrator;
/**
* IntentIntegrator for the V4 Android compatibility package.
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
index 40105df4f..3af674526 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
@@ -24,7 +24,7 @@ public class IterableIterator<T> implements Iterable<T> {
public IterableIterator(Iterator<T> iter, boolean failsafe) {
mIter = iter;
- if(failsafe && mIter == null) {
+ if (failsafe && mIter == null) {
// is there a better way?
mIter = new ArrayList<T>().iterator();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
index a31fdc5ae..7f70867a5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
@@ -46,7 +46,7 @@ public abstract class KeyServer {
abstract List<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery;
- abstract String get(long keyId) throws QueryException;
+ abstract String get(String keyIdHex) throws QueryException;
- abstract void add(String armoredText) throws AddKeyException;
+ abstract void add(String armoredKey) throws AddKeyException;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
index 14b2a2211..b205bd556 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
@@ -1,16 +1,20 @@
/*
- * 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
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
package org.sufficientlysecure.keychain.util;
public interface KeychainServiceListener {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
index 377a8d5d6..5ec915810 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
@@ -17,7 +17,11 @@
package org.sufficientlysecure.keychain.util;
-import java.util.concurrent.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
index a783d7820..4fcd3047f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2010-2014 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
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
new file mode 100644
index 000000000..23961c05f
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+/** This is a simple class that wraps a ProgressDialogUpdater, scaling the progress
+ * values into a specified range.
+ */
+public class ProgressScaler implements ProgressDialogUpdater {
+
+ final ProgressDialogUpdater mWrapped;
+ final int mFrom, mTo, mMax;
+
+ public ProgressScaler(ProgressDialogUpdater wrapped, int from, int to, int max) {
+ this.mWrapped = wrapped;
+ this.mFrom = from;
+ this.mTo = to;
+ this.mMax = max;
+ }
+
+ /**
+ * Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
+ */
+ public void setProgress(String message, int progress, int max) {
+ mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ mWrapped.setProgress(resourceId, progress, mMax);
+ }
+
+ public void setProgress(int progress, int max) {
+ mWrapped.setProgress(progress, max);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
index 8c3367bea..af9034aa7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
@@ -18,14 +18,16 @@
package org.sufficientlysecure.keychain.util;
-import android.graphics.Bitmap;
-import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
import org.sufficientlysecure.keychain.Constants;
import java.util.Hashtable;