From d1c64e31598574759ae472f9e89c6a82e808bddb Mon Sep 17 00:00:00 2001 From: Christian Hagau Date: Thu, 2 Aug 2018 00:00:00 +0000 Subject: [PATCH] Extract key sharing methods from ViewKeyAdvShareFragment --- .../keychain/ui/ViewKeyAdvShareFragment.java | 116 +------------- .../keychain/util/ShareKeyHelper.java | 150 ++++++++++++++++++ 2 files changed, 158 insertions(+), 108 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareKeyHelper.java 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 9c80c17d6..2a01d3ed3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -18,12 +18,6 @@ package org.sufficientlysecure.keychain.ui; -import java.io.BufferedWriter; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.security.NoSuchAlgorithmException; - import android.app.Activity; import android.app.ActivityOptions; import android.arch.lifecycle.LiveData; @@ -38,7 +32,6 @@ import android.graphics.PorterDuff; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.ParcelFileDescriptor; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; @@ -50,23 +43,17 @@ import android.view.animation.AlphaAnimation; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; -import org.sufficientlysecure.keychain.pgp.SshPublicKey; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.daos.KeyRepository; -import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; import org.sufficientlysecure.keychain.ui.ViewKeyAdvActivity.ViewKeyAdvViewModel; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; -import timber.log.Timber; +import org.sufficientlysecure.keychain.util.ShareKeyHelper; public class ViewKeyAdvShareFragment extends Fragment { private ImageView mQrCode; @@ -121,12 +108,15 @@ public class ViewKeyAdvShareFragment extends Fragment { vFingerprintShareButton.setOnClickListener(v -> shareFingerprint(false)); vFingerprintClipboardButton.setOnClickListener(v -> shareFingerprint(true)); - vKeyShareButton.setOnClickListener(v -> shareKey(false, false)); - vKeyClipboardButton.setOnClickListener(v -> shareKey(true, false)); + + vKeyShareButton.setOnClickListener(v -> ShareKeyHelper.shareKey(getActivity(), unifiedKeyInfo)); + + vKeyClipboardButton.setOnClickListener(v -> ShareKeyHelper.shareKeyToClipboard(getActivity(), unifiedKeyInfo)); vKeySafeSlingerButton.setOnClickListener(v -> startSafeSlinger()); - vKeySshShareButton.setOnClickListener(v -> shareKey(false, true)); - vKeySshClipboardButton.setOnClickListener(v -> shareKey(true, true)); + vKeySshShareButton.setOnClickListener(v -> ShareKeyHelper.shareSshKey(getActivity(), unifiedKeyInfo)); + + vKeySshClipboardButton.setOnClickListener(v -> ShareKeyHelper.shareSshKeyToClipboard(getActivity(), unifiedKeyInfo)); vKeyUploadButton.setOnClickListener(v -> uploadToKeyserver()); return view; @@ -138,96 +128,6 @@ public class ViewKeyAdvShareFragment extends Fragment { startActivityForResult(safeSlingerIntent, 0); } - private String getShareKeyContent(boolean asSshKey) - throws KeyRepository.NotFoundException, IOException, PgpGeneralException, NoSuchAlgorithmException { - - KeyRepository keyRepository = KeyRepository.create(requireContext()); - - String content; - if (asSshKey) { - long authSubKeyId = unifiedKeyInfo.has_auth_key_int(); - CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(unifiedKeyInfo.master_key_id()) - .getPublicKey(authSubKeyId); - SshPublicKey sshPublicKey = new SshPublicKey(publicKey); - content = sshPublicKey.getEncodedKey(); - } else { - content = keyRepository.getPublicKeyRingAsArmoredString(unifiedKeyInfo.master_key_id()); - } - - return content; - } - - private void shareKey(boolean toClipboard, boolean asSshKey) { - Activity activity = getActivity(); - if (activity == null || unifiedKeyInfo == null) { - return; - } - if (asSshKey && !unifiedKeyInfo.has_auth_key()) { - Notify.create(activity, R.string.authentication_subkey_not_found, Style.ERROR).show(); - return; - } - - try { - String content = getShareKeyContent(asSshKey); - - if (toClipboard) { - ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); - if (clipMan == null) { - Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show(); - return; - } - - ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content); - clipMan.setPrimaryClip(clip); - - Notify.create(activity, R.string.key_copied_to_clipboard, Notify.Style.OK).show(); - return; - } - - // let user choose application - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.setType(Constants.MIME_TYPE_KEYS); - - // NOTE: Don't use Intent.EXTRA_TEXT to send the key - // better send it via a Uri! - // example: Bluetooth Share will convert text/plain sent via Intent.EXTRA_TEXT to HTML - try { - TemporaryFileProvider shareFileProv = new TemporaryFileProvider(); - - String filename; - if (unifiedKeyInfo.name() != null) { - filename = unifiedKeyInfo.name(); - } else { - filename = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); - } - Uri contentUri = TemporaryFileProvider.createFile(activity, filename + Constants.FILE_EXTENSION_ASC); - - BufferedWriter contentWriter = new BufferedWriter(new OutputStreamWriter( - new ParcelFileDescriptor.AutoCloseOutputStream( - shareFileProv.openFile(contentUri, "w")))); - contentWriter.write(content); - contentWriter.close(); - - sendIntent.putExtra(Intent.EXTRA_STREAM, contentUri); - } catch (FileNotFoundException e) { - Timber.e(e, "Error creating temporary key share file!"); - // no need for a snackbar because one sharing option doesn't work - // Notify.create(getActivity(), R.string.error_temp_file, Notify.Style.ERROR).show(); - } - - String title = getString(R.string.title_share_key); - Intent shareChooser = Intent.createChooser(sendIntent, title); - - startActivity(shareChooser); - } catch (PgpGeneralException | IOException | NoSuchAlgorithmException e) { - Timber.e(e, "error processing key!"); - Notify.create(activity, R.string.error_key_processing, Notify.Style.ERROR).show(); - } catch (KeyRepository.NotFoundException e) { - Timber.e(e, "key not found!"); - Notify.create(activity, R.string.error_key_not_found, Notify.Style.ERROR).show(); - } - } - private void shareFingerprint(boolean toClipboard) { Activity activity = getActivity(); if (activity == null || unifiedKeyInfo == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareKeyHelper.java new file mode 100644 index 000000000..7c1c67d49 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareKeyHelper.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 Schürmann & Breitmoser GbR + * + * 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 . + */ + +package org.sufficientlysecure.keychain.util; + + +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.daos.KeyRepository; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; +import org.sufficientlysecure.keychain.pgp.SshPublicKey; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.Notify; +import timber.log.Timber; + +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.security.NoSuchAlgorithmException; + +public class ShareKeyHelper { + + private static String getShareKeyContent(Activity activity, UnifiedKeyInfo unifiedKeyInfo, boolean asSshKey) + throws KeyRepository.NotFoundException, IOException, PgpGeneralException, NoSuchAlgorithmException { + + KeyRepository keyRepository = KeyRepository.create(activity); + + String content; + if (asSshKey) { + long authSubKeyId = unifiedKeyInfo.has_auth_key_int(); + CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(unifiedKeyInfo.master_key_id()) + .getPublicKey(authSubKeyId); + SshPublicKey sshPublicKey = new SshPublicKey(publicKey); + content = sshPublicKey.getEncodedKey(); + } else { + content = keyRepository.getPublicKeyRingAsArmoredString(unifiedKeyInfo.master_key_id()); + } + + return content; + } + + private static void shareKey(Activity activity, UnifiedKeyInfo unifiedKeyInfo, boolean toClipboard, boolean asSshKey) { + if (activity == null || unifiedKeyInfo == null) { + return; + } + if (asSshKey && !unifiedKeyInfo.has_auth_key()) { + Notify.create(activity, R.string.authentication_subkey_not_found, Notify.Style.ERROR).show(); + return; + } + + try { + String content = getShareKeyContent(activity, unifiedKeyInfo, asSshKey); + + if (toClipboard) { + ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipMan == null) { + Notify.create(activity, R.string.error_clipboard_copy, Notify.Style.ERROR).show(); + return; + } + + ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content); + clipMan.setPrimaryClip(clip); + + Notify.create(activity, R.string.key_copied_to_clipboard, Notify.Style.OK).show(); + return; + } + + // let user choose application + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.setType(Constants.MIME_TYPE_KEYS); + + // NOTE: Don't use Intent.EXTRA_TEXT to send the key + // better send it via a Uri! + // example: Bluetooth Share will convert text/plain sent via Intent.EXTRA_TEXT to HTML + try { + TemporaryFileProvider shareFileProv = new TemporaryFileProvider(); + + String filename; + if (unifiedKeyInfo.name() != null) { + filename = unifiedKeyInfo.name(); + } else { + filename = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); + } + Uri contentUri = TemporaryFileProvider.createFile(activity, filename + Constants.FILE_EXTENSION_ASC); + + BufferedWriter contentWriter = new BufferedWriter(new OutputStreamWriter( + new ParcelFileDescriptor.AutoCloseOutputStream( + shareFileProv.openFile(contentUri, "w")))); + contentWriter.write(content); + contentWriter.close(); + + sendIntent.putExtra(Intent.EXTRA_STREAM, contentUri); + } catch (FileNotFoundException e) { + Timber.e(e, "Error creating temporary key share file!"); + // no need for a snackbar because one sharing option doesn't work + // Notify.create(getActivity(), R.string.error_temp_file, Notify.Style.ERROR).show(); + } + + String title = activity.getString(R.string.title_share_key); + Intent shareChooser = Intent.createChooser(sendIntent, title); + + activity.startActivity(shareChooser); + } catch (PgpGeneralException | IOException | NoSuchAlgorithmException e) { + Timber.e(e, "error processing key!"); + Notify.create(activity, R.string.error_key_processing, Notify.Style.ERROR).show(); + } catch (KeyRepository.NotFoundException e) { + Timber.e(e, "key not found!"); + Notify.create(activity, R.string.error_key_not_found, Notify.Style.ERROR).show(); + } + } + + public static void shareKeyToClipboard(Activity activity, UnifiedKeyInfo unifiedKeyInfo) { + shareKey(activity, unifiedKeyInfo, true, false); + } + public static void shareKey(Activity activity, UnifiedKeyInfo unifiedKeyInfo) { + shareKey(activity, unifiedKeyInfo, false, false); + } + public static void shareSshKey(Activity activity, UnifiedKeyInfo unifiedKeyInfo) { + shareKey(activity, unifiedKeyInfo, false, true); + } + public static void shareSshKeyToClipboard(Activity activity, UnifiedKeyInfo unifiedKeyInfo) { + shareKey(activity, unifiedKeyInfo, true, true); + } + +}