aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Faulk <wfaulk@webassign.net>2015-04-29 19:59:32 -0400
committerWilliam Faulk <wfaulk@webassign.net>2015-05-04 16:46:15 -0400
commitb06e7cd737c9f85c37fb2d17533cc1a2b25715a9 (patch)
treeabf70471c2e4253033d9fb11adeb1196648f8c9d
parent291f95db5ad2028ef252448e47b4ad22a297bf33 (diff)
downloadopen-keychain-b06e7cd737c9f85c37fb2d17533cc1a2b25715a9.tar.gz
open-keychain-b06e7cd737c9f85c37fb2d17533cc1a2b25715a9.tar.bz2
open-keychain-b06e7cd737c9f85c37fb2d17533cc1a2b25715a9.zip
Fix Bluetooth share without breaking others
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java30
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml1
4 files changed, 100 insertions, 9 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 710dbf8aa..161979ce3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -40,6 +40,8 @@ import org.sufficientlysecure.keychain.util.PRNGFixes;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
+import java.io.File;
+import java.io.FilenameFilter;
import java.security.Security;
import java.util.HashMap;
@@ -88,6 +90,17 @@ public class KeychainApplication extends Application {
}
}
+ // Clean up leftover Bluetooth Share files
+ for (File toDelete : this.getExternalCacheDir().listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ if (filename.matches("^key-[0-9a-fA-F]{8}\\.pgp\\.asc$")) {
+ return true;
+ }
+ return false;
+ }
+ })) { toDelete.delete(); }
+
brandGlowEffect(getApplicationContext(),
getApplicationContext().getResources().getColor(R.color.primary));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
index 6bd3a9303..4141da202 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.FileObserver;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -54,6 +55,9 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
@@ -175,11 +179,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
boolean toClipboard) {
try {
String content;
+ byte[] fingerprintData = (byte[]) providerHelper.getGenericData(
+ KeyRings.buildUnifiedKeyRingUri(dataUri),
+ Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
if (fingerprintOnly) {
- byte[] data = (byte[]) providerHelper.getGenericData(
- KeyRings.buildUnifiedKeyRingUri(dataUri),
- Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(data);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintData);
if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
@@ -213,13 +217,62 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, content);
sendIntent.setType("text/plain");
+
String title;
if (fingerprintOnly) {
title = getResources().getString(R.string.title_share_fingerprint_with);
} else {
title = getResources().getString(R.string.title_share_key);
}
- startActivity(Intent.createChooser(sendIntent, title));
+ Intent shareChooser = Intent.createChooser(sendIntent, title);
+
+ // Bluetooth Share will convert text/plain sent via EXTRA_TEXT to HTML
+ // Add replacement extra to send a text/plain file instead.
+ try {
+ final File contentFile = new File(getActivity().getExternalCacheDir(),
+ "key-" + KeyFormattingUtils.getShortKeyIdAsHexFromFingerprint(fingerprintData, false) +
+ ".pgp.asc");
+ FileWriter contentFileWriter = new FileWriter(contentFile, false);
+ BufferedWriter contentWriter = new BufferedWriter(contentFileWriter);
+ contentWriter.write(content);
+ contentWriter.close();
+ Uri contentUri = Uri.fromFile(contentFile);
+
+ final Runnable deleteContentFile = new Runnable() {
+ public void run() {
+ contentFile.delete();
+ }
+ };
+
+ // delete the file after Bluetooth Share closes the file
+ FileObserver tempFileObserver = new FileObserver(contentFile.getAbsolutePath(),
+ FileObserver.CLOSE_NOWRITE) {
+ @Override
+ public void onEvent(int event, String path) {
+ // Hopefully it will only be opened and then closed by the share process once
+ getContainer().post(deleteContentFile);
+ }
+ };
+ tempFileObserver.startWatching();
+
+ // If it's not complete in 1m, the file was not used; delete it
+ getContainer().postDelayed(deleteContentFile, 1 * 60 * 1000);
+
+ // create replacement extras inside try{}:
+ // if file creation fails, just don't add the replacements
+ Bundle replacements = new Bundle();
+ shareChooser.putExtra(Intent.EXTRA_REPLACEMENT_EXTRAS, replacements);
+
+ Bundle bluetoothExtra = new Bundle(sendIntent.getExtras());
+ replacements.putBundle("com.android.bluetooth", bluetoothExtra);
+
+ bluetoothExtra.putParcelable(Intent.EXTRA_STREAM, contentUri);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error creating temporary Bluetooth key share file!", e);
+ Notify.create(getActivity(), R.string.error_bluetooth_file, Notify.Style.ERROR).show();
+ }
+
+ startActivity(shareChooser);
}
} catch (PgpGeneralException | IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
@@ -379,4 +432,4 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
-} \ No newline at end of file
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 91a7d361a..0b80b5fe9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -227,6 +227,14 @@ public class KeyFormattingUtils {
return buf.getLong();
}
+ public static int getShortKeyIdFromFingerprint(byte[] fingerprint) {
+ ByteBuffer buf = ByteBuffer.wrap(fingerprint);
+ // skip first 16 bytes of the fingerprint
+ buf.position(16);
+ // the last four bytes are the short key id (big endian, which is default order in ByteBuffer)
+ return buf.getInt();
+ }
+
/**
* Convert key id from long to 64 bit hex string
* <p/>
@@ -238,16 +246,24 @@ public class KeyFormattingUtils {
* @return
*/
public static String convertKeyIdToHex(long keyId) {
+ return convertKeyIdToHex(keyId, true);
+ }
+
+ public static String convertKeyIdToHex(long keyId, boolean header) {
long upper = keyId >> 32;
if (upper == 0) {
// this is a short key id
- return convertKeyIdToHexShort(keyId);
+ return convertKeyIdToHexShort(keyId, header);
}
- return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
+ return header?"0x":"" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
}
public static String convertKeyIdToHexShort(long keyId) {
- return "0x" + convertKeyIdToHex32bit(keyId);
+ return convertKeyIdToHexShort(keyId, true);
+ }
+
+ public static String convertKeyIdToHexShort(long keyId, boolean header) {
+ return header?"0x":"" + convertKeyIdToHex32bit(keyId);
}
private static String convertKeyIdToHex32bit(long keyId) {
@@ -258,6 +274,14 @@ public class KeyFormattingUtils {
return hexString;
}
+ public static String getKeyIdAsHexFromFingerprint(byte[] fingerprint, boolean header) {
+ return convertKeyIdToHex(getKeyIdFromFingerprint(fingerprint), header);
+ }
+
+ public static String getShortKeyIdAsHexFromFingerprint(byte[] fingerprint, boolean header) {
+ return convertKeyIdToHex(getShortKeyIdFromFingerprint(fingerprint), header);
+ }
+
/**
* Makes a human-readable version of a key ID, which is usually 64 bits: lower-case, no
* leading 0x, space-separated quartets (for keys whose length in hex is divisible by 4)
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 72ba46702..45b1dc26b 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -1289,5 +1289,6 @@
<string name="snack_yubi_other">Different key stored on YubiKey!</string>
<string name="error_nfc">"NFC Error: %s"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string>
+ <string name="error_bluetooth_file">Error creating temporary file. Bluetooth sharing will fail.</string>
</resources>