diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main/java')
45 files changed, 1771 insertions, 893 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 ff4abe56a..f9a7962ec 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain;  import android.os.Environment;  import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; +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; @@ -73,7 +73,7 @@ public final class Constants {          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 = RegisteredAppsListActivity.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/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index 03cf936ee..cc240a67b 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 @@ -53,14 +53,14 @@ public class ExportHelper {          this.mActivity = activity;      } -    public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) { +    public void deleteKey(Uri dataUri, Handler deleteHandler) {          long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());          // Create a new Messenger for the communication back          Messenger messenger = new Messenger(deleteHandler);          DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, -                new long[]{keyRingRowId}, keyType); +                new long[]{keyRingRowId});          deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");      } @@ -139,7 +139,7 @@ public class ExportHelper {          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, @@ -151,7 +151,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..d0ba20ea6 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 @@ -65,81 +65,4 @@ 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); -        } - -        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/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 5b00d163a..b7db92b9b 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,6 +18,11 @@  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.util.encoders.Hex; @@ -27,6 +32,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException;  import java.util.*;  import java.util.regex.Matcher;  import java.util.regex.Pattern; @@ -444,7 +452,7 @@ public class PgpKeyHelper {              key = secretKey.getPublicKey();          } -        return convertFingerprintToHex(key.getFingerprint(), true); +        return convertFingerprintToHex(key.getFingerprint());      }      /** @@ -457,11 +465,8 @@ public class PgpKeyHelper {       * @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;      } @@ -477,6 +482,11 @@ 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);      } @@ -492,17 +502,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 ((!s1.isEmpty() ? Long.parseLong(s1, 16) << 32 : 0) | 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/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 5f2354c24..6e4899fc2 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,6 +19,7 @@ package org.sufficientlysecure.keychain.provider;  import android.net.Uri;  import android.provider.BaseColumns; +  import org.sufficientlysecure.keychain.Constants;  public class KeychainContract { @@ -56,10 +57,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_FK = "package_name"; // foreign key to api_apps.package_name      }      public static final class KeyTypes { @@ -87,7 +93,7 @@ public class KeychainContract {      public static final String PATH_KEYS = "keys";      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 class KeyRings implements KeyRingsColumns, BaseColumns {          public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() @@ -154,6 +160,7 @@ public class KeychainContract {          }          public static Uri buildSecretKeyRingsByEmailsUri(String emails) { +            // TODO: encoded?              return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)                      .appendPath(emails).build();          } @@ -262,16 +269,36 @@ public class KeychainContract {          /**           * 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.thialfihar.apg.api_app"; -        public static Uri buildIdUri(String rowId) { -            return CONTENT_URI.buildUpon().appendPath(rowId).build(); +        public static Uri buildByPackageNameUri(String packageName) { +            return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();          } +    } -        public static Uri buildByPackageNameUri(String packageName) { -            return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) +    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_app.accounts"; + +        /** +         * Use if a single item is returned +         */ +        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app.account"; + +        public static Uri buildBaseUri(String packageName) { +            return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)                      .build();          } + +        public static Uri buildByPackageAndAccountUri(String packageName, String accountName) { +            return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) +                    .appendEncodedPath(accountName).build(); +        }      }      public static class DataStream { 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 031a7d5ae..4abcec435 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 @@ -21,8 +21,10 @@ import android.content.Context;  import android.database.sqlite.SQLiteDatabase;  import android.database.sqlite.SQLiteOpenHelper;  import android.provider.BaseColumns; +  import org.sufficientlysecure.keychain.Constants;  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; @@ -30,13 +32,14 @@ import org.sufficientlysecure.keychain.util.Log;  public class KeychainDatabase extends SQLiteOpenHelper {      private static final String DATABASE_NAME = "apg.db"; -    private static final int DATABASE_VERSION = 7; +    private static final int DATABASE_VERSION = 8;      public interface Tables {          String KEY_RINGS = "key_rings";          String KEYS = "keys";          String USER_IDS = "user_ids";          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 @@ -61,26 +64,35 @@ public class KeychainDatabase extends SQLiteOpenHelper {              + 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)"; +            + 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)"; +            + 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_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)"; +            + 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_FK + " TEXT NOT NULL, " +            + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", " +            + ApiAppsAccountsColumns.PACKAGE_NAME_FK + "), " +            + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " +            + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)";      KeychainDatabase(Context context) {          super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -94,6 +106,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {          db.execSQL(CREATE_KEYS);          db.execSQL(CREATE_USER_IDS);          db.execSQL(CREATE_API_APPS); +        db.execSQL(CREATE_API_APPS_ACCOUNTS);      }      @Override @@ -133,6 +146,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {                      db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT                              + " BLOB;");                      break; +                case 7: +                    // new db layout for api apps +                    db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS); +                    db.execSQL(CREATE_API_APPS); +                    db.execSQL(CREATE_API_APPS_ACCOUNTS); +                    break;                  default:                      break; 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 b963ceb39..6469da978 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"); @@ -28,8 +28,17 @@ 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.KeyRingsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;  import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;  import org.sufficientlysecure.keychain.util.Log; @@ -71,8 +80,9 @@ public class KeychainProvider extends ContentProvider {      private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;      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; @@ -229,11 +239,22 @@ public class KeychainProvider extends ContentProvider {          /**           * API apps +         * +         * <pre> +         * api_apps +         * api_apps/_ (package name) +         * +         * api_apps/_/accounts +         * api_apps/_/accounts/_ (account name) +         * </pre>           */          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); + +        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 @@ -247,7 +268,7 @@ public class KeychainProvider extends ContentProvider {          return matcher;      } -    private KeychainDatabase mApgDatabase; +    private KeychainDatabase mKeychainDatabase;      /**       * {@inheritDoc} @@ -255,7 +276,7 @@ public class KeychainProvider extends ContentProvider {      @Override      public boolean onCreate() {          mUriMatcher = buildUriMatcher(); -        mApgDatabase = new KeychainDatabase(getContext()); +        mKeychainDatabase = new KeychainDatabase(getContext());          return true;      } @@ -302,10 +323,15 @@ public class KeychainProvider extends ContentProvider {              case API_APPS:                  return ApiApps.CONTENT_TYPE; -            case API_APPS_BY_ROW_ID:              case API_APPS_BY_PACKAGE_NAME:                  return ApiApps.CONTENT_ITEM_TYPE; +            case API_ACCOUNTS: +                return ApiAccounts.CONTENT_TYPE; + +            case API_ACCOUNTS_BY_ACCOUNT_NAME: +                return ApiAccounts.CONTENT_ITEM_TYPE; +              default:                  throw new UnsupportedOperationException("Unknown uri: " + uri);          } @@ -486,7 +512,7 @@ public class KeychainProvider extends ContentProvider {          Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");          SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); -        SQLiteDatabase db = mApgDatabase.getReadableDatabase(); +        SQLiteDatabase db = mKeychainDatabase.getReadableDatabase();          int match = mUriMatcher.match(uri); @@ -506,7 +532,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case PUBLIC_KEY_RING:              case SECRET_KEY_RING:                  qb = buildKeyRingQuery(qb, match); @@ -516,7 +541,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case PUBLIC_KEY_RING_BY_ROW_ID:              case SECRET_KEY_RING_BY_ROW_ID:                  qb = buildKeyRingQuery(qb, match); @@ -529,7 +553,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:              case SECRET_KEY_RING_BY_MASTER_KEY_ID:                  qb = buildKeyRingQuery(qb, match); @@ -542,7 +565,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case SECRET_KEY_RING_BY_KEY_ID:              case PUBLIC_KEY_RING_BY_KEY_ID:                  qb = buildKeyRingQueryWithSpecificKey(qb, match); @@ -555,7 +577,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case SECRET_KEY_RING_BY_EMAILS:              case PUBLIC_KEY_RING_BY_EMAILS:                  qb = buildKeyRingQuery(qb, match); @@ -585,7 +606,6 @@ public class KeychainProvider extends ContentProvider {                  }                  break; -              case SECRET_KEY_RING_BY_LIKE_EMAIL:              case PUBLIC_KEY_RING_BY_LIKE_EMAIL:                  qb = buildKeyRingQuery(qb, match); @@ -601,7 +621,6 @@ public class KeychainProvider extends ContentProvider {                          + "))");                  break; -              case PUBLIC_KEY_RING_KEY:              case SECRET_KEY_RING_KEY:                  qb.setTables(Tables.KEYS); @@ -614,7 +633,6 @@ public class KeychainProvider extends ContentProvider {                  qb.setProjectionMap(getProjectionMapForKeys());                  break; -              case PUBLIC_KEY_RING_KEY_BY_ROW_ID:              case SECRET_KEY_RING_KEY_BY_ROW_ID:                  qb.setTables(Tables.KEYS); @@ -630,7 +648,6 @@ public class KeychainProvider extends ContentProvider {                  qb.setProjectionMap(getProjectionMapForKeys());                  break; -              case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:                  qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "("                          + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." @@ -641,7 +658,6 @@ public class KeychainProvider extends ContentProvider {                  qb.setProjectionMap(getProjectionMapForUserIds());                  break; -              case PUBLIC_KEY_RING_USER_ID:              case SECRET_KEY_RING_USER_ID:                  qb.setTables(Tables.USER_IDS); @@ -649,7 +665,6 @@ public class KeychainProvider extends ContentProvider {                  qb.appendWhereEscapeString(uri.getPathSegments().get(2));                  break; -              case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:              case SECRET_KEY_RING_USER_ID_BY_ROW_ID:                  qb.setTables(Tables.USER_IDS); @@ -660,25 +675,31 @@ public class KeychainProvider extends ContentProvider {                  qb.appendWhereEscapeString(uri.getLastPathSegment());                  break; -              case API_APPS:                  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);                  break; +            case API_ACCOUNTS_BY_ACCOUNT_NAME: +                qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" +                        + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." +                        + ApiAccounts.PACKAGE_NAME_FK + " )"); +                qb.appendWhere(Tables.API_APPS + "." + ApiApps.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); @@ -715,7 +736,7 @@ 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 = mKeychainDatabase.getWritableDatabase();          Uri rowUri = null;          long rowId = -1; @@ -735,12 +756,14 @@ public class KeychainProvider extends ContentProvider {                      values.put(Keys.TYPE, KeyTypes.PUBLIC);                      rowId = db.insertOrThrow(Tables.KEYS, null, values); +                    // TODO: this is wrong:                      rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));                      sendBroadcastDatabaseChange(getKeyType(match), getType(uri));                      break;                  case PUBLIC_KEY_RING_USER_ID:                      rowId = db.insertOrThrow(Tables.USER_IDS, null, values); +                    // TODO: this is wrong:                      rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));                      sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); @@ -757,18 +780,33 @@ public class KeychainProvider extends ContentProvider {                      values.put(Keys.TYPE, KeyTypes.SECRET);                      rowId = db.insertOrThrow(Tables.KEYS, null, values); +                    // TODO: this is wrong:                      rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));                      sendBroadcastDatabaseChange(getKeyType(match), getType(uri));                      break;                  case SECRET_KEY_RING_USER_ID:                      rowId = db.insertOrThrow(Tables.USER_IDS, null, values); +                    // TODO: this is wrong:                      rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));                      break;                  case API_APPS:                      rowId = db.insertOrThrow(Tables.API_APPS, null, values); -                    rowUri = ApiApps.buildIdUri(Long.toString(rowId)); +//                    rowUri = ApiApps.buildIdUri(Long.toString(rowId)); + +                    break; +                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_FK, packageName); + +                    Log.d(Constants.TAG, "provider packageName: " + packageName); + +                    rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values); +                    // TODO: this is wrong: +//                    rowUri = ApiAccounts.buildIdUri(Long.toString(rowId));                      break;                  default: @@ -792,7 +830,7 @@ public class KeychainProvider extends ContentProvider {      public int delete(Uri uri, String selection, String[] selectionArgs) {          Log.v(Constants.TAG, "delete(uri=" + uri + ")"); -        final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); +        final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();          int count;          final int match = mUriMatcher.match(uri); @@ -828,12 +866,12 @@ public class KeychainProvider extends ContentProvider {                  count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),                          selectionArgs);                  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, selection),                          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, selection),                          selectionArgs);                  break;              default: @@ -853,7 +891,7 @@ 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; @@ -898,13 +936,13 @@ public class KeychainProvider extends ContentProvider {                      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); @@ -1003,19 +1041,29 @@ public class KeychainProvider extends ContentProvider {       * @param selection       * @return       */ -    private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) { -        String lastPathSegment = uri.getLastPathSegment(); +    private String buildDefaultApiAppsSelection(Uri uri, String selection) { +        String packageName = 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 ApiApps.PACKAGE_NAME + "=" + packageName + andSelection; +    } + +    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 + ")";          } + +        return ApiAccounts.PACKAGE_NAME_FK + "=" + packageName + " AND " +                + ApiAccounts.ACCOUNT_NAME + "=" + accountName +                + andSelection;      }      // @Override 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 db2c57207..71f74b8d8 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 @@ -22,6 +22,7 @@ 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.sufficientlysecure.keychain.Constants; @@ -33,7 +34,8 @@ 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.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; @@ -230,7 +232,7 @@ public class ProviderHelper {          // get current _ID of key          long currentRowId = -1;          Cursor oldQuery = context.getContentResolver() -                    .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); +                .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);          if (oldQuery != null && oldQuery.moveToFirst()) {              currentRowId = oldQuery.getLong(0);          } else { @@ -288,7 +290,7 @@ public class ProviderHelper {       * 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 keyRingRowId, PGPPublicKey key, int rank) throws IOException {          ContentValues values = new ContentValues();          values.put(Keys.KEY_ID, key.getKeyID());          values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); @@ -316,7 +318,7 @@ 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) { +                                                                        long keyRingRowId, String userId, int rank) {          ContentValues values = new ContentValues();          values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);          values.put(UserIds.USER_ID, userId); @@ -331,7 +333,7 @@ public class ProviderHelper {       * 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 { +                                                                     long keyRingRowId, PGPSecretKey key, int rank) throws IOException {          ContentValues values = new ContentValues();          boolean hasPrivate = true; @@ -368,7 +370,7 @@ public class ProviderHelper {       * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing       */      private static ContentProviderOperation buildSecretUserIdOperations(Context context, -                                            long keyRingRowId, String userId, int rank) { +                                                                        long keyRingRowId, String userId, int rank) {          ContentValues values = new ContentValues();          values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);          values.put(UserIds.USER_ID, userId); @@ -469,6 +471,15 @@ public class ProviderHelper {          cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);      } +    public static void deleteUnifiedKeyRing(Context context, String masterKeyId, boolean isSecretKey) { +        ContentResolver cr = context.getContentResolver(); +        cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId), null, null); +        if (isSecretKey) { +            cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId), null, null); +        } + +    } +      /**       * Get master key id of keyring by its row id       */ @@ -495,7 +506,7 @@ public class ProviderHelper {                          + " 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", }; +                        + " = 1) AS sign",};          ContentResolver cr = context.getContentResolver();          Cursor cursor = cr.query(queryUri, projection, null, null, null); @@ -792,19 +803,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) { @@ -812,30 +832,59 @@ 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 uri) { +        AccountSettings settings = null; + +        Cursor cur = context.getContentResolver().query(uri, 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 byte[] getApiAppSignature(Context context, String packageName) { -        Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName); +        Uri queryUri = ApiApps.buildByPackageNameUri(packageName);          String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; @@ -855,4 +904,4 @@ public class ProviderHelper {          return signature;      } -} +}
\ No newline at end of file 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..6c7e51bf0 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java @@ -0,0 +1,54 @@ +/* + * 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; + +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 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..fa6ccf63b 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; @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;  import org.sufficientlysecure.keychain.service.PassphraseCacheService;  import org.sufficientlysecure.keychain.util.InputData;  import org.sufficientlysecure.keychain.util.Log; @@ -137,7 +138,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 +147,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 +165,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 +188,7 @@ 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); @@ -217,7 +218,7 @@ public class OpenPgpService extends RemoteService {              // 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 +230,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 +240,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 +277,7 @@ public class OpenPgpService extends RemoteService {      }      private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input, -                                        ParcelFileDescriptor output, AppSettings appSettings) { +                                        ParcelFileDescriptor output, AccountSettings accSettings) {          try {              // Get Input- and OutputStream from ParcelFileDescriptor              InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); @@ -292,7 +293,7 @@ public class OpenPgpService extends RemoteService {                  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()) +                        .enforcedKeyId(accSettings.getKeyId())                          .passphrase(passphrase);                  // TODO: currently does not support binary signed-only content @@ -300,7 +301,7 @@ public class OpenPgpService extends RemoteService {                  if (decryptVerifyResult.isKeyPassphraseNeeded()) {                      // 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;                  } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {                      throw new PgpGeneralException("Decryption of symmetric content not supported by API!"); @@ -432,17 +433,26 @@ 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); +                return decryptAndVerifyImpl(data, input, output, accSettings);              } 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..7e935d317 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; @@ -47,7 +49,6 @@ public abstract class RemoteService extends Service {      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 +56,9 @@ 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();                  byte[] packageSignature;                  try { @@ -84,7 +81,7 @@ public abstract class RemoteService extends Service {                  intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);                  PendingIntent pi = PendingIntent.getActivity(getBaseContext(), -                                                    PRIVATE_REQUEST_CODE_REGISTER, intent, 0); +                        PRIVATE_REQUEST_CODE_REGISTER, intent, 0);                  // return PendingIntent to be executed by client                  Intent result = new Intent(); @@ -99,11 +96,11 @@ 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); +                    PRIVATE_REQUEST_CODE_ERROR, intent, 0);              // return PendingIntent to be executed by client              Intent result = new Intent(); @@ -125,27 +122,56 @@ 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() { +    private 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; +    } -            Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(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); -            AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); +        Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); -            if (settings != null) { -                return settings; -            } -        } +        AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); + +        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(), +                PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + +        // 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;      }      /** @@ -216,7 +242,7 @@ 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)");              }          } 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/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 837295018..3d88d216e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.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.pm.ApplicationInfo;  import android.content.pm.PackageManager; @@ -25,16 +25,18 @@ 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;  import android.widget.AdapterView.OnItemSelectedListener; -import com.beardedhen.androidbootstrap.BootstrapButton; +import android.widget.ImageView; +import android.widget.Spinner; +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.AccountSettings; +import org.sufficientlysecure.keychain.remote.AppSettings;  import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;  import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;  import org.sufficientlysecure.keychain.util.AlgorithmNames; @@ -43,15 +45,13 @@ import org.sufficientlysecure.keychain.util.Log;  import java.security.MessageDigest;  import java.security.NoSuchAlgorithmException; -public class AppSettingsFragment extends Fragment implements +public class AccountSettingsFragment extends Fragment implements          SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {      // model -    private AppSettings mAppSettings; +    private AccountSettings mAccSettings;      // view -    private LinearLayout mAdvancedSettingsContainer; -    private BootstrapButton mAdvancedSettingsButton;      private TextView mAppNameView;      private ImageView mAppIconView;      private Spinner mEncryptionAlgorithm; @@ -66,25 +66,25 @@ public class AppSettingsFragment extends Fragment implements      KeyValueSpinnerAdapter mHashAdapter;      KeyValueSpinnerAdapter mCompressionAdapter; -    public AppSettings getAppSettings() { -        return mAppSettings; +    public AccountSettings getAccSettings() { +        return mAccSettings;      } -    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); -        } +    public void setAccSettings(AccountSettings appSettings) { +        this.mAccSettings = 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 @@ -98,7 +98,7 @@ public class AppSettingsFragment extends Fragment implements       */      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { -        View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false); +        View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false);          initView(view);          return view;      } @@ -114,22 +114,17 @@ public class AppSettingsFragment extends Fragment implements      private void initView(View view) {          mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById( -                R.id.api_app_settings_select_key_fragment); +                R.id.api_account_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); +        mAppNameView = (TextView) view.findViewById(R.id.api_account_settings_app_name); +        mAppIconView = (ImageView) view.findViewById(R.id.api_account_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); +                .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); +        mPackageName = (TextView) view.findViewById(R.id.api_account_settings_package_name); +        mPackageSignature = (TextView) view.findViewById(R.id.api_account_settings_package_signature);          AlgorithmNames algorithmNames = new AlgorithmNames(getActivity()); @@ -140,7 +135,7 @@ public class AppSettingsFragment extends Fragment implements              @Override              public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { -                mAppSettings.setEncryptionAlgorithm((int) id); +                mAccSettings.setEncryptionAlgorithm((int) id);              }              @Override @@ -154,7 +149,7 @@ public class AppSettingsFragment extends Fragment implements              @Override              public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { -                mAppSettings.setHashAlgorithm((int) id); +                mAccSettings.setHashAlgorithm((int) id);              }              @Override @@ -169,69 +164,40 @@ public class AppSettingsFragment extends Fragment implements              @Override              public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { -                mAppSettings.setCompression((int) id); +                mAccSettings.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);      } +// +//    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); +        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..853dc2d3c --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java @@ -0,0 +1,176 @@ +/* + * 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.annotation.TargetApi; +import android.content.ContentUris; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +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.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.SimpleCursorAdapter; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; + +// TODO: make compat with < 11 +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +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. +    SimpleCursorAdapter 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 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) { +//                // edit app settings +//                Intent intent = new Intent(getActivity(), AppSettingsActivity.class); +//                intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id)); +//                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 SimpleCursorAdapter(getActivity(), +                android.R.layout.simple_list_item_1, +                null, +                new String[]{KeychainContract.ApiAccounts.ACCOUNT_NAME}, +                new int[]{android.R.id.text1}, +                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, +            KeychainContract.ApiAccounts.ACCOUNT_NAME}; + +    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. +        // First, pick the base URI to use depending on whether we are +        // currently filtering. +//        Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName); + +        // 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 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 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/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 2ef170dec..33cde49ba 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/AppSettingsActivity.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,20 @@ 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.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) { @@ -63,7 +67,7 @@ public class AppSettingsActivity extends ActionBarActivity {              return;          } else {              Log.d(Constants.TAG, "uri: " + mAppUri); -            loadData(mAppUri); +            loadData(savedInstanceState, mAppUri);          }      } @@ -87,9 +91,34 @@ public class AppSettingsActivity extends ActionBarActivity {          return super.onOptionsItemSelected(item);      } -    private void loadData(Uri appUri) { +    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); + +        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() { 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..8bcd83fc7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java @@ -0,0 +1,119 @@ +/* + * 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.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ImageView; +import android.widget.Spinner; +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.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 { + +    // 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; +        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); +        } + +    } + +    /** +     * 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; +    } + + +    private void initView(View view) { +        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); +    } + +    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); +    } + + +} 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..f3fa6e7c6 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,22 @@ 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.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.util.Log; -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 +56,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 +81,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 +112,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..d3ac5cade 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; @@ -94,13 +104,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_app_register_activity); + +            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,24 +157,24 @@ 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_account_create_activity); -            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); 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 6ec6dcf8a..bd3a0421b 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 @@ -25,6 +25,7 @@ import android.os.Bundle;  import android.os.Message;  import android.os.Messenger;  import android.os.RemoteException; +  import org.spongycastle.openpgp.*;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.Id; @@ -542,7 +543,15 @@ public class KeychainIntentService extends IntentService                              ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),                              oldPassPhrase, newPassPhrase);                  } else { -                    PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); +                    //TODO: Workaround due to ProviderHelper.getPGPPublicKeyByKeyId can not resolve public key of master-key id with uri/cursor +                    PGPPublicKey pubkey = null; +                    for(PGPSecretKey key : keys) { +                        PGPPublicKey tempKey = key.getPublicKey(); +                        if (tempKey.getKeyID() == masterKeyId) { +                            pubkey = tempKey; +                        } +                    } +                    //PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId);                      keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates,                              pubkey, oldPassPhrase, newPassPhrase);                  } @@ -692,11 +701,11 @@ public class KeychainIntentService extends IntentService                      for (long masterKeyId : masterKeyIds) {                          if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key) -                                                                && allPublicMasterKeyIds.contains(masterKeyId)) { +                                && allPublicMasterKeyIds.contains(masterKeyId)) {                              publicMasterKeyIds.add(masterKeyId);                          }                          if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) -                                                                && allSecretMasterKeyIds.contains(masterKeyId)) { +                                && allSecretMasterKeyIds.contains(masterKeyId)) {                              secretMasterKeyIds.add(masterKeyId);                          }                      } @@ -745,49 +754,58 @@ public class KeychainIntentService extends IntentService                  ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);                  String keyServer = data.getString(DOWNLOAD_KEY_SERVER); +                // TODO: add extra which requires fingerprint suport and force verification! +                // only supported by newer sks keyserver versions +                  // this downloads the keys and places them into the ImportKeysListEntry entries                  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 -                    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; +                    // 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(); +                    } -                                    entry.setBytes(newKeyring.getEncoded()); -                                } else { -                                    Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); -                                } +                    // create PGPKeyRing object based on downloaded armored key +                    PGPKeyRing downloadedKey = null; +                    BufferedInputStream bufferedInput = +                            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(); @@ -837,7 +855,7 @@ public class KeychainIntentService extends IntentService          if (this.mIsCanceled) {              return;          } -        Log.e(Constants.TAG, "ApgService Exception: ", e); +        Log.e(Constants.TAG, "KeychainIntentService Exception: ", e);          e.printStackTrace();          Bundle data = new Bundle(); 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 bd14369b0..5dc06c16d 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 @@ -198,6 +198,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements              case LOADER_ID_KEYRING:                  // the first key here is our master key                  if (data.moveToFirst()) { +                    // TODO: put findViewById in onCreate! +                      long keyId = data.getLong(INDEX_MASTER_KEY_ID);                      String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId);                      ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); @@ -210,8 +212,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements                          // 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: @@ -325,11 +327,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) { @@ -378,11 +380,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..9b3b00c19 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 @@ -443,8 +443,7 @@ public class DecryptActivity extends DrawerActivity {          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) { +        if (mAssumeSymmetricEncryption || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {              showPassphraseDialog();          } else {              if (mDecryptTarget == Id.target.file) { @@ -494,6 +493,7 @@ public class DecryptActivity extends DrawerActivity {       * TODO: Rework function, remove global variables       */      private void getDecryptionKeyFromInputStream() { +        mAssumeSymmetricEncryption = false;          InputStream inStream = null;          if (mContentUri != null) {              try { @@ -517,13 +517,6 @@ public class DecryptActivity extends DrawerActivity {                  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()); @@ -540,7 +533,6 @@ public class DecryptActivity extends DrawerActivity {                  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(); @@ -553,6 +545,7 @@ public class DecryptActivity extends DrawerActivity {                  mAssumeSymmetricEncryption = true;              }          } catch (Exception e) { +            Log.e(Constants.TAG, "error while reading decryption key from input stream", e);              AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),                      AppMsg.STYLE_ALERT).show();          } @@ -638,11 +631,11 @@ public class DecryptActivity extends DrawerActivity {          intent.putExtra(KeychainIntentService.EXTRA_DATA, data); -        // Message is received after encrypting is done in ApgService +        // Message is received after encrypting is done in KeychainIntentService          KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,                  getString(R.string.progress_decrypting), 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/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 31804719f..6eb5b9d2d 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 @@ -46,6 +46,7 @@ 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.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -58,7 +59,6 @@ import org.sufficientlysecure.keychain.ui.widget.SectionView;  import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; -  import java.util.ArrayList;  import java.util.GregorianCalendar;  import java.util.Vector; @@ -180,7 +180,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,7 +197,7 @@ 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) { @@ -325,9 +325,13 @@ public class EditKeyActivity extends ActionBarActivity {                  long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);                  long[] ids = new long[]{masterKeyId};                  mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, -                                                            null); +                        null);                  return true;              case R.id.menu_key_edit_delete: { +                //Convert the uri to one based on rowId +                long rowId= ProviderHelper.getRowId(this,mDataUri); +                Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); +                  // Message is received after key is deleted                  Handler returnHandler = new Handler() {                      @Override @@ -339,7 +343,7 @@ public class EditKeyActivity extends ActionBarActivity {                      }                  }; -                mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler); +                mExportHelper.deleteKey(convertUri, returnHandler);                  return true;              }          } @@ -491,11 +495,10 @@ public class EditKeyActivity extends ActionBarActivity {      private void saveClicked() {          long masterKeyId = getMasterKeyId(); -        try { -            if (!isPassphraseSet()) { -                throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); -            } - +        if (!isPassphraseSet()) { +            Log.e(Constants.TAG, "No passphrase has been set"); +            Toast.makeText(this, R.string.set_a_passphrase, Toast.LENGTH_LONG).show(); +        } else {              String passphrase = null;              if (mIsPassPhraseSet) {                  passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); @@ -508,9 +511,6 @@ public class EditKeyActivity extends ActionBarActivity {                  mCurrentPassphrase = passphrase;                  finallySaveClicked();              } -        } catch (PgpGeneralException e) { -            //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), -            //        Toast.LENGTH_SHORT).show();          }      } @@ -540,11 +540,11 @@ public class EditKeyActivity extends ActionBarActivity {              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) { @@ -572,8 +572,9 @@ 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();          }      } @@ -697,4 +698,4 @@ public class EditKeyActivity extends ActionBarActivity {                  : getString(R.string.btn_set_passphrase));      } -} +}
\ No newline at end of file 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..4f18f69d7 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 @@ -108,11 +108,6 @@ public class EncryptActivity extends DrawerActivity {      private String mInputFilename = null;      private String mOutputFilename = null; -    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; @@ -147,9 +142,6 @@ public class EncryptActivity extends DrawerActivity {          updateMode();          updateActionBarButtons(); - -        // retrieve and cache the system's short animation time -        mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);      }      /** @@ -605,11 +597,11 @@ public class EncryptActivity extends DrawerActivity {          intent.putExtra(KeychainIntentService.EXTRA_DATA, data); -        // Message is received after encrypting is done in ApgService +        // Message is received after encrypting is done in KeychainIntentService          KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,                  getString(R.string.progress_encrypting), 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) { @@ -795,49 +787,10 @@ public class EncryptActivity extends DrawerActivity {              }          }); -        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[]{ @@ -1002,8 +955,8 @@ public class EncryptActivity extends DrawerActivity {              case Id.request.secret_keys: {                  if (resultCode == RESULT_OK) { -                    Bundle bundle = data.getExtras(); -                    mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); +                    Uri uri_master_key = data.getData(); +                    mSecretKeyId = Long.valueOf(uri_master_key.getLastPathSegment());                  } else {                      mSecretKeyId = Id.key.none;                  } 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..834509f53 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 @@ -363,7 +363,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa       * 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), 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/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index cac8b7046..daf455c03 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 @@ -355,25 +355,7 @@ public class KeyListFragment extends Fragment              @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();                  }              }          }; @@ -382,7 +364,7 @@ public class KeyListFragment extends Fragment          Messenger messenger = new Messenger(returnHandler);          DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, -                keyRingRowIds, Id.type.public_key); +                keyRingRowIds);          deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");      } 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 82a3c2e8e..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 @@ -76,10 +76,6 @@ public class SelectSecretKeyActivity extends ActionBarActivity {          Intent data = new Intent();          data.setData(selectedUri); -        // TODO: deprecate RESULT_EXTRA_MASTER_KEY_ID! -        long masterKeyId = Long.valueOf(selectedUri.getLastPathSegment()); -        data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); -          setResult(RESULT_OK, data);          finish();      } 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 960b4aafb..cbc0f4c5c 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,21 @@ 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.UserIds.USER_ID +            , KeychainContract.KeyRings.MASTER_KEY_ID}; +    final int INDEX_USER_ID = 0; +    final int INDEX_MASTER_KEY_ID = 1; +      public interface SelectSecretKeyCallback {          void onKeySelected(long secretKeyId);      } @@ -59,63 +74,25 @@ 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.convertKeyIdToHexShort(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) { @@ -147,29 +124,80 @@ public class SelectSecretKeyLayoutFragment extends Fragment {          return view;      } +    //For AppSettingsFragment +    public void selectKey(long masterKeyId) { +        Uri buildUri = KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(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);      } +    @Override +    public Loader<Cursor> onCreateLoader(int id, Bundle args) { +        //We don't care about the Loader id +        return new CursorLoader(getActivity(), mReceivedUri, 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; +    } +      // 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. +    // Secret Key Fragment.Intent contains the passed Uri      @Override      public void onActivityResult(int requestCode, int resultCode, Intent data) {          switch (requestCode & 0xFFFF) {              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(); + +                    //Must be restartLoader() or the data will not be updated on selecting a new key +                    getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); -                    // remove displayed errors                      mKeyUserId.setError(null); -                    // give value back to callback -                    mCallback.onKeySelected(secretKeyId);                  }                  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/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index c4097403c..3e7e6d7ce 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 @@ -182,7 +182,7 @@ public class ViewKeyActivity extends ActionBarActivity {          String content;          if (fingerprintOnly) {              byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri); -            String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); +            String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);              content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;          } else { @@ -237,25 +237,12 @@ 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);      }  } 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 e4f707f3c..8a4f2758a 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 @@ -320,9 +320,9 @@ public class ViewKeyMainFragment extends Fragment implements                          // FALLBACK for old database entries                          fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);                      } -                    String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); +                    String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); -                    mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint)); +                    mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));                  }                  int valid_keys = 0;                  data.moveToFirst(); 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..7d3166af9 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,7 +30,9 @@ 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.helper.OtherHelper;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import java.util.ArrayList; @@ -43,13 +45,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; - +        private TextView mainUserId; +        private TextView mainUserIdRest; +        private TextView keyId; +        private TextView fingerprint; +        private TextView algorithm; +        private TextView status;      }      public ImportKeysAdapter(Activity activity) { @@ -100,12 +101,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 +120,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 05521b0c9..9b20effc2 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 @@ -20,6 +20,7 @@ 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; @@ -35,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; @@ -55,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; @@ -74,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)); @@ -92,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; @@ -109,8 +110,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {          }      }; -    public long getKeyId() { -        return keyId; +    public String getKeyIdHex() { +        return keyIdHex;      }      public byte[] getBytes() { @@ -121,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       */ @@ -132,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       */ @@ -165,12 +234,13 @@ 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();          final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();          this.algorithm = getAlgorithmFromId(algorithm); 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 0064e9f13..d925480e9 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,9 +18,11 @@  package org.sufficientlysecure.keychain.ui.adapter;  import android.content.Context; +import android.content.res.ColorStateList;  import android.database.Cursor;  import android.graphics.Color;  import android.support.v4.widget.CursorAdapter; +import android.text.format.DateFormat;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; @@ -30,6 +32,8 @@ import org.sufficientlysecure.keychain.R;  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; @@ -41,6 +45,9 @@ public class ViewKeyKeysAdapter extends CursorAdapter {      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); @@ -73,6 +80,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter {              mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT);              mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN);              mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); +            mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY);          }      } @@ -80,6 +88,7 @@ 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); @@ -122,13 +131,36 @@ public class ViewKeyKeysAdapter extends CursorAdapter {              keyId.setTextColor(Color.RED);              keyDetails.setTextColor(Color.RED);          } else { +            keyId.setTextColor(mDefaultTextColor); +            keyDetails.setTextColor(mDefaultTextColor);              revokedKeyIcon.setVisibility(View.GONE);          } + +        boolean valid = true; +        if (!cursor.isNull(mIndexExpiry)) { +            Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000); +            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); +        } +        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/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..3ff88aa2b 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 @@ -28,6 +28,12 @@ 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; @@ -40,143 +46,163 @@ import java.util.ArrayList;  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_KEY_RING_ROW_IDS = "delete_key_ring_row_ids";      public static final int MESSAGE_OKAY = 1; +    public static final int MESSAGE_ERROR = 0; + +    private boolean isSingleSelection = false; -    public static final String MESSAGE_NOT_DELETED = "not_deleted"; +    private TextView mainMessage; +    private CheckBox checkDeleteSecret; +    private LinearLayout deleteSecretKeyView; +    private View inflateView;      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[] keyRingRowIds +    ) {          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); +        //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);          AlertDialog.Builder builder = new AlertDialog.Builder(activity); + +        //Setup custom View to display in AlertDialog +        LayoutInflater inflater = activity.getLayoutInflater(); +        inflateView = inflater.inflate(R.layout.view_key_delete_fragment, null); +        builder.setView(inflateView); + +        deleteSecretKeyView = (LinearLayout) inflateView.findViewById(R.id.deleteSecretKeyView); +        mainMessage = (TextView) inflateView.findViewById(R.id.mainMessage); +        checkDeleteSecret = (CheckBox) inflateView.findViewById(R.id.checkDeleteSecret); +          builder.setTitle(R.string.warning); +        //If only a single key has been selected          if (keyRingRowIds.length == 1) {              Uri dataUri; -            if (keyType == Id.type.public_key) { -                dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0])); +            ArrayList<Long> publicKeyRings; //Any one will do +            isSingleSelection = true; + +            long selectedRow = keyRingRowIds[0]; +            long keyType; +            publicKeyRings = ProviderHelper.getPublicKeyRingsRowIds(activity); + +            if (publicKeyRings.contains(selectedRow)) { +                //TODO Should be a better method to do this other than getting all the KeyRings +                dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(selectedRow)); +                keyType = Id.type.public_key;              } else { -                dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0])); +                dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(selectedRow)); +                keyType = Id.type.secret_key;              } +              String userId = ProviderHelper.getUserId(activity, dataUri); +            //Hide the Checkbox and TextView since this is a single selection,user will be notified thru message +            deleteSecretKeyView.setVisibility(View.GONE); +            //Set message depending on which key it is. +            mainMessage.setText(getString(keyType == Id.type.secret_key ? R.string.secret_key_deletion_confirmation +                    : R.string.public_key_deletetion_confirmation, userId)); + -            builder.setMessage(getString( -                    keyType == Id.type.public_key ? R.string.key_deletion_confirmation -                            : R.string.secret_key_deletion_confirmation, userId));          } else { -            builder.setMessage(R.string.key_deletion_confirmation_multi); +            deleteSecretKeyView.setVisibility(View.VISIBLE); +            mainMessage.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(); +            public void onClick(DialogInterface dialog, int which) { +                Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); +                String[] projection = new String[]{ +                        KeychainContract.KeyRings.MASTER_KEY_ID, // 0 +                        KeychainContract.KeyRings.TYPE// 1 +                }; + +                // 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 masterKeyId; +                long keyType; +                boolean isSuccessfullyDeleted; +                try { +                    isSuccessfullyDeleted = false; +                    while (cursor != null && cursor.moveToNext()) { +                        masterKeyId = cursor.getLong(0); +                        keyType = cursor.getLong(1); + +                        Log.d(Constants.TAG, "masterKeyId: " + masterKeyId +                                + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private")); + + +                        if (keyType == KeychainContract.KeyTypes.SECRET) { +                            if (checkDeleteSecret.isChecked() || isSingleSelection) { +                                ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true);                              } -                        } -                    } finally { -                        if (cursor != null) { -                            cursor.close(); +                        } else { +                            ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false);                          }                      } -                } else { -                    for (long keyRowId : keyRingRowIds) { -                        ProviderHelper.deleteSecretKeyRing(activity, keyRowId); + +                    //Check if the selected rows have actually been deleted +                    cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null); +                    if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) { +                        isSuccessfullyDeleted = true;                      } + +                } finally { +                    if (cursor != null) { +                        cursor.close(); +                    } +                  }                  dismiss(); -                if (notDeleted.size() > 0) { -                    Bundle data = new Bundle(); -                    data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted); -                    sendMessageToHandler(MESSAGE_OKAY, data); -                } else { +                if (isSuccessfullyDeleted) {                      sendMessageToHandler(MESSAGE_OKAY, null); +                } else { +                    sendMessageToHandler(MESSAGE_ERROR, null);                  }              } -        }); + +        } +        );          builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {              @Override @@ -198,7 +224,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {          if (data != null) {              msg.setData(data);          } -          try {              mMessenger.send(msg);          } catch (RemoteException e) { @@ -207,4 +232,5 @@ public class DeleteKeyDialogFragment extends DialogFragment {              Log.w(Constants.TAG, "Messenger is null!", e);          }      } -} + +}
\ No newline at end of file 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..94586810e 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 @@ -90,7 +90,7 @@ public class ShareQrCodeDialogFragment extends DialogFragment {              alert.setPositiveButton(R.string.btn_okay, null);              byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri); -            String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); +            String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);              mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint); 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..f9a5b92f3 --- /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/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index e5b6003b1..1ef178f15 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 @@ -256,11 +256,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) { 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 9d6850027..b987e1533 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,6 +29,7 @@ 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; @@ -72,26 +72,26 @@ public class HkpKeyServer extends KeyServer {      /**       * 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> +     * <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> @@ -105,23 +105,23 @@ public class HkpKeyServer extends KeyServer {      /**       * 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> +     * <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 @@ -179,6 +179,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); @@ -238,22 +239,34 @@ public class HkpKeyServer extends KeyServer {          final Matcher matcher = PUB_KEY_LINE.matcher(data);          while (matcher.find()) { -            final ImportKeysListEntry info = new ImportKeysListEntry(); -            info.bitStrength = Integer.parseInt(matcher.group(3)); -            final int algorithmId = Integer.decode(matcher.group(2)); -            info.algorithm = getAlgorithmFromId(algorithmId); +            final ImportKeysListEntry entry = new ImportKeysListEntry(); + +            entry.setBitStrength(Integer.parseInt(matcher.group(3))); -            info.hexKeyId = "0x" + matcher.group(1); -            info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(1)); +            final int algorithmId = Integer.decode(matcher.group(2)); +            entry.setAlgorithm(getAlgorithmFromId(algorithmId)); + +            // group 1 contains the full fingerprint (v4) or the long key id if available +            // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff +            // and https://github.com/openpgp-keychain/openpgp-keychain/issues/259#issuecomment-38168176 +            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 { +                // set key id only +                entry.setKeyIdHex("0x" + fingerprintOrKeyId); +            }              final long creationDate = Long.parseLong(matcher.group(4));              final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));              tmpGreg.setTimeInMillis(creationDate * 1000); -            info.date = tmpGreg.getTime(); +            entry.setDate(tmpGreg.getTime()); -            info.revoked = matcher.group(6).contains("r"); -            info.userIds = new ArrayList<String>(); +            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()) { @@ -266,20 +279,23 @@ public class HkpKeyServer extends KeyServer {                          // will never happen, because "UTF8" is supported                      }                  } -                info.userIds.add(tmp); +                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&options=mr&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"); @@ -302,13 +318,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/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;  }  | 
