aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-03-15 20:02:57 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-03-15 20:02:57 +0100
commit4afd6b881edfd0bc404734a4e8959a7a0fa785a7 (patch)
tree63a051fb595d168f8ddbfd76a51bb2319889d40b /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util
parentba7d8a58673d55660bc7faf68970229f17458514 (diff)
parentdb39b779c984a758e13adc16837893509f3b364f (diff)
downloadopen-keychain-4afd6b881edfd0bc404734a4e8959a7a0fa785a7.tar.gz
open-keychain-4afd6b881edfd0bc404734a4e8959a7a0fa785a7.tar.bz2
open-keychain-4afd6b881edfd0bc404734a4e8959a7a0fa785a7.zip
Merge branch 'development' into linked-identities
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java296
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java2
2 files changed, 261 insertions, 37 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
index 1413273e8..6efc0a5ea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
@@ -190,7 +190,7 @@ public class ContactHelper {
* @param context
* @return
*/
- private static List<String> getMainProfileContactName(Context context) {
+ public static List<String> getMainProfileContactName(Context context) {
ContentResolver resolver = context.getContentResolver();
Cursor profileCursor = resolver.query(
ContactsContract.Profile.CONTENT_URI,
@@ -214,6 +214,55 @@ public class ContactHelper {
return new ArrayList<>(names);
}
+ /**
+ * returns the CONTACT_ID of the main ("me") contact
+ * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
+ *
+ * @param resolver
+ * @return
+ */
+ public static long getMainProfileContactId(ContentResolver resolver) {
+ Cursor profileCursor = resolver.query(ContactsContract.Profile.CONTENT_URI,
+ new String[]{ ContactsContract.Profile._ID}, null, null, null);
+
+ if(profileCursor != null && profileCursor.getCount() != 0 && profileCursor.moveToNext()) {
+ long contactId = profileCursor.getLong(0);
+ profileCursor.close();
+ return contactId;
+ }
+ else {
+ if(profileCursor != null) {
+ profileCursor.close();
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * loads the profile picture of the main ("me") contact
+ * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
+ *
+ * @param contentResolver
+ * @param highRes true for large image if present, false for thumbnail
+ * @return bitmap of loaded photo
+ */
+ public static Bitmap loadMainProfilePhoto(ContentResolver contentResolver, boolean highRes) {
+ try {
+ long mainProfileContactId = getMainProfileContactId(contentResolver);
+
+ Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
+ Long.toString(mainProfileContactId));
+ InputStream photoInputStream =
+ ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, contactUri, highRes);
+ if (photoInputStream == null) {
+ return null;
+ }
+ return BitmapFactory.decodeStream(photoInputStream);
+ } catch (Throwable ignored) {
+ return null;
+ }
+ }
+
public static List<String> getContactMails(Context context) {
ContentResolver resolver = context.getContentResolver();
Cursor mailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
@@ -269,7 +318,7 @@ public class ContactHelper {
/**
* returns the CONTACT_ID of the raw contact to which a masterKeyId is associated, if the
- * raw contact has not been marked for deletion
+ * raw contact has not been marked for deletion.
*
* @param resolver
* @param masterKeyId
@@ -296,6 +345,34 @@ public class ContactHelper {
return contactId;
}
+ /**
+ * Returns the display name of the system contact associated with contactId, null if the
+ * contact does not exist
+ *
+ * @param resolver
+ * @param contactId
+ * @return primary display name of system contact associated with contactId, null if it does
+ * not exist
+ */
+ public static String getContactName(ContentResolver resolver, long contactId) {
+ String contactName = null;
+ Cursor raw = resolver.query(ContactsContract.Contacts.CONTENT_URI,
+ new String[]{
+ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
+ },
+ ContactsContract.Contacts._ID + "=?",
+ new String[]{//"0" for "not deleted"
+ Long.toString(contactId)
+ }, null);
+ if (raw != null) {
+ if (raw.moveToNext()) {
+ contactName = raw.getString(0);
+ }
+ raw.close();
+ }
+ return contactName;
+ }
+
public static Bitmap getCachedPhotoByMasterKeyId(ContentResolver contentResolver, long masterKeyId) {
if (masterKeyId == -1) {
return null;
@@ -333,46 +410,47 @@ public class ContactHelper {
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_EXPIRED,
- KeychainContract.KeyRings.IS_REVOKED};
+ KeychainContract.KeyRings.IS_REVOKED,
+ KeychainContract.KeyRings.VERIFIED,
+ KeychainContract.KeyRings.HAS_SECRET,
+ KeychainContract.KeyRings.HAS_ANY_SECRET};
public static final int INDEX_MASTER_KEY_ID = 0;
public static final int INDEX_USER_ID = 1;
public static final int INDEX_IS_EXPIRED = 2;
public static final int INDEX_IS_REVOKED = 3;
+ public static final int INDEX_VERIFIED = 4;
+ public static final int INDEX_HAS_SECRET = 5;
+ public static final int INDEX_HAS_ANY_SECRET = 6;
/**
* Write/Update the current OpenKeychain keys to the contact db
*/
public static void writeKeysToContacts(Context context) {
ContentResolver resolver = context.getContentResolver();
- Set<Long> deletedKeys = getRawContactMasterKeyIds(resolver);
if (Constants.DEBUG_SYNC_REMOVE_CONTACTS) {
debugDeleteRawContacts(resolver);
}
-// ContentProviderClient client = resolver.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
-// ContentValues values = new ContentValues();
-// Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
-// values.put(ContactsContract.Settings.ACCOUNT_NAME, account.name);
-// values.put(ContactsContract.Settings.ACCOUNT_TYPE, account.type);
-// values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, true);
-// try {
-// client.insert(ContactsContract.Settings.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(), values);
-// } catch (RemoteException e) {
-// e.printStackTrace();
-// }
-
- // Load all Keys from OK
- Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), KEYS_TO_CONTACT_PROJECTION,
- null, null, null);
+ writeKeysToMainProfileContact(context, resolver);
+
+ Set<Long> deletedKeys = getRawContactMasterKeyIds(resolver);
+
+ // Load all public Keys from OK
+ // TODO: figure out why using selectionArgs does not work in this case
+ Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ KEYS_TO_CONTACT_PROJECTION,
+ KeychainContract.KeyRings.HAS_ANY_SECRET + "=0",
+ null, null);
+
if (cursor != null) {
while (cursor.moveToNext()) {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
- String keyIdShort = KeyFormattingUtils.convertKeyIdToHexShort(cursor.getLong(INDEX_MASTER_KEY_ID));
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
@@ -384,9 +462,11 @@ public class ContactHelper {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
- // Do not store expired or revoked keys in contact db - and remove them if they already exist
- if (isExpired || isRevoked) {
- Log.d(Constants.TAG, "Expired or revoked: Deleting rawContactId " + rawContactId);
+ // Do not store expired or revoked or unverified keys in contact db - and
+ // remove them if they already exist. Secret keys do not reach this point
+ if (isExpired || isRevoked || !isVerified) {
+ Log.d(Constants.TAG, "Expired or revoked or unverified: Deleting rawContactId "
+ + rawContactId);
if (rawContactId != -1) {
deleteRawContactById(resolver, rawContactId);
}
@@ -397,7 +477,7 @@ public class ContactHelper {
Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId);
insertContact(ops, context, masterKeyId);
- writeContactKey(ops, context, rawContactId, masterKeyId, keyIdShort);
+ writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
}
// We always update the display name (which is derived from primary user id)
@@ -422,43 +502,165 @@ public class ContactHelper {
}
/**
- * Delete all raw contacts associated to OpenKeychain.
+ * Links all keys with secrets to the main ("me") contact
+ * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
+ *
+ * @param context
+ */
+ public static void writeKeysToMainProfileContact(Context context, ContentResolver resolver) {
+ Set<Long> keysToDelete = getMainProfileMasterKeyIds(resolver);
+
+ // get all keys which have associated secret keys
+ // TODO: figure out why using selectionArgs does not work in this case
+ Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ KEYS_TO_CONTACT_PROJECTION,
+ KeychainContract.KeyRings.HAS_ANY_SECRET + "!=0",
+ null, null);
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
+ boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
+ boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
+
+ if (!isExpired && !isRevoked && userIdSplit[0] != null) {
+ // if expired or revoked will not be removed from keysToDelete or inserted
+ // into main profile ("me" contact)
+ boolean existsInMainProfile = keysToDelete.remove(masterKeyId);
+ if (!existsInMainProfile) {
+ long rawContactId = -1;//new raw contact
+
+ Log.d(Constants.TAG, "masterKeyId with secret " + masterKeyId);
+
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+ insertMainProfileRawContact(ops, masterKeyId);
+ writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
+
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, ops);
+ } catch (Exception e) {
+ Log.w(Constants.TAG, e);
+ }
+ }
+ }
+ }
+ }
+
+ for (long masterKeyId : keysToDelete) {
+ deleteMainProfileRawContactByMasterKeyId(resolver, masterKeyId);
+ Log.d(Constants.TAG, "Delete main profile raw contact with masterKeyId " + masterKeyId);
+ }
+ }
+
+ /**
+ * Inserts a raw contact into the table defined by ContactsContract.Profile
+ * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
+ *
+ * @param ops
+ * @param masterKeyId
+ */
+ private static void insertMainProfileRawContact(ArrayList<ContentProviderOperation> ops,
+ long masterKeyId) {
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE)
+ .withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId))
+ .build());
+ }
+
+ /**
+ * deletes a raw contact from the main profile table ("me" contact)
+ * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
+ *
+ * @param resolver
+ * @param masterKeyId
+ * @return
+ */
+ private static int deleteMainProfileRawContactByMasterKeyId(ContentResolver resolver,
+ long masterKeyId) {
+ // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
+ // would be just flagged for deletion
+ Uri deleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon().
+ appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
+
+ return resolver.delete(deleteUri,
+ ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
+ ContactsContract.RawContacts.SOURCE_ID + "=?",
+ new String[]{
+ Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
+ });
+ }
+
+ /**
+ * Delete all raw contacts associated to OpenKeychain, including those from "me" contact
+ * defined by ContactsContract.Profile
+ *
+ * @return number of rows deleted
*/
private static int debugDeleteRawContacts(ContentResolver resolver) {
- //allows us to actually wipe the RawContact from the device, otherwise would be just flagged
- //for deletion
+ // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
+ // would be just flagged for deletion
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
Log.d(Constants.TAG, "Deleting all raw contacts associated to OK...");
- return resolver.delete(deleteUri,
+ int delete = resolver.delete(deleteUri,
+ ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
+ new String[]{
+ Constants.ACCOUNT_TYPE
+ });
+
+ Uri mainProfileDeleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
+
+ delete += resolver.delete(mainProfileDeleteUri,
ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
new String[]{
Constants.ACCOUNT_TYPE
});
+
+ return delete;
}
+ /**
+ * Deletes raw contacts from ContactsContract.RawContacts based on rawContactId. Does not
+ * delete contacts from the "me" contact defined in ContactsContract.Profile
+ *
+ * @param resolver
+ * @param rawContactId
+ * @return number of rows deleted
+ */
private static int deleteRawContactById(ContentResolver resolver, long rawContactId) {
- //allows us to actually wipe the RawContact from the device, otherwise would be just flagged
- //for deletion
+ // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
+ // would be just flagged for deletion
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
return resolver.delete(deleteUri,
- ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts._ID + "=?",
+ ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
+ ContactsContract.RawContacts._ID + "=?",
new String[]{
Constants.ACCOUNT_TYPE, Long.toString(rawContactId)
});
}
+ /**
+ * Deletes raw contacts from ContactsContract.RawContacts based on masterKeyId. Does not
+ * delete contacts from the "me" contact defined in ContactsContract.Profile
+ *
+ * @param resolver
+ * @param masterKeyId
+ * @return number of rows deleted
+ */
private static int deleteRawContactByMasterKeyId(ContentResolver resolver, long masterKeyId) {
- //allows us to actually wipe the RawContact from the device, otherwise would be just flagged
- //for deletion
+ // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
+ // would be just flagged for deletion
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
return resolver.delete(deleteUri,
- ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?",
+ ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
+ ContactsContract.RawContacts.SOURCE_ID + "=?",
new String[]{
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
});
@@ -487,6 +689,28 @@ public class ContactHelper {
}
/**
+ * @return a set of all key master key ids currently present in the contact db
+ */
+ private static Set<Long> getMainProfileMasterKeyIds(ContentResolver resolver) {
+ HashSet<Long> result = new HashSet<>();
+ Cursor masterKeyIds = resolver.query(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI,
+ new String[]{
+ ContactsContract.RawContacts.SOURCE_ID
+ },
+ ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
+ new String[]{
+ Constants.ACCOUNT_TYPE
+ }, null);
+ if (masterKeyIds != null) {
+ while (masterKeyIds.moveToNext()) {
+ result.add(masterKeyIds.getLong(0));
+ }
+ masterKeyIds.close();
+ }
+ return result;
+ }
+
+ /**
* This will search the contact db for a raw contact with a given master key id
*
* @return raw contact id or -1 if not found
@@ -528,10 +752,10 @@ public class ContactHelper {
* This creates the link to OK in contact details
*/
private static void writeContactKey(ArrayList<ContentProviderOperation> ops, Context context, long rawContactId,
- long masterKeyId, String keyIdShort) {
+ long masterKeyId, String keyName) {
ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId)
.withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE)
- .withValue(ContactsContract.Data.DATA1, context.getString(R.string.contact_show_key, keyIdShort))
+ .withValue(ContactsContract.Data.DATA1, context.getString(R.string.contact_show_key, keyName))
.withValue(ContactsContract.Data.DATA2, masterKeyId)
.build());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
index 1d78ed80e..cda5892fe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
@@ -124,7 +124,7 @@ public class ExportHelper {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle data = message.getData();