From ca8f8e3ff7dd4742f65b541e4a8304433c093f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 13 Sep 2013 10:05:43 +0200 Subject: unify naming of api package and process --- .../keychain/openpgp_api/AppSettings.java | 84 +++ .../keychain/openpgp_api/AppSettingsActivity.java | 109 +++ .../keychain/openpgp_api/AppSettingsFragment.java | 304 +++++++++ .../keychain/openpgp_api/OpenPgpService.java | 747 +++++++++++++++++++++ .../openpgp_api/OpenPgpServiceActivity.java | 325 +++++++++ .../openpgp_api/RegisteredAppsAdapter.java | 76 +++ .../openpgp_api/RegisteredAppsListActivity.java | 61 ++ .../openpgp_api/RegisteredAppsListFragment.java | 107 +++ .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/remote_api/AppSettings.java | 84 --- .../keychain/remote_api/AppSettingsActivity.java | 109 --- .../keychain/remote_api/AppSettingsFragment.java | 304 --------- .../keychain/remote_api/OpenPgpService.java | 747 --------------------- .../remote_api/OpenPgpServiceActivity.java | 325 --------- .../keychain/remote_api/RegisteredAppsAdapter.java | 76 --- .../remote_api/RegisteredAppsListActivity.java | 61 -- .../remote_api/RegisteredAppsListFragment.java | 107 --- .../keychain/ui/MainActivity.java | 2 +- 18 files changed, 1815 insertions(+), 1815 deletions(-) create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettings.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpService.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpServiceActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsAdapter.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListFragment.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpService.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpServiceActivity.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java (limited to 'OpenPGP-Keychain/src') diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettings.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettings.java new file mode 100644 index 000000000..982d5fe40 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettings.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.sufficientlysecure.keychain.Id; + +public class AppSettings { + private String packageName; + private long keyId = Id.key.none; + private int encryptionAlgorithm; + private int hashAlgorithm; + private int compression; + + public AppSettings() { + + } + + public AppSettings(String packageName) { + super(); + this.packageName = packageName; + // defaults: + this.encryptionAlgorithm = PGPEncryptedData.AES_128; + this.hashAlgorithm = HashAlgorithmTags.SHA256; + this.compression = Id.choice.compression.zlib; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public long getKeyId() { + return keyId; + } + + public void setKeyId(long scretKeyId) { + this.keyId = scretKeyId; + } + + public int getEncryptionAlgorithm() { + return encryptionAlgorithm; + } + + public void setEncryptionAlgorithm(int encryptionAlgorithm) { + this.encryptionAlgorithm = encryptionAlgorithm; + } + + public int getHashAlgorithm() { + return hashAlgorithm; + } + + public void setHashAlgorithm(int hashAlgorithm) { + this.hashAlgorithm = hashAlgorithm; + } + + public int getCompression() { + return compression; + } + + public void setCompression(int compression) { + this.compression = compression; + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsActivity.java new file mode 100644 index 000000000..030a2bbb8 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsActivity.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +public class AppSettingsActivity extends SherlockFragmentActivity { + private Uri mAppUri; + + private AppSettingsFragment settingsFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Inflate a "Done" custom action bar view to serve as the "Up" affordance. + ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + save(); + } + }); + + setContentView(R.layout.api_app_settings_activity); + + settingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( + R.id.api_app_settings_fragment); + + Intent intent = getIntent(); + mAppUri = intent.getData(); + if (mAppUri == null) { + Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); + finish(); + return; + } else { + Log.d(Constants.TAG, "uri: " + mAppUri); + loadData(mAppUri); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getSupportMenuInflater().inflate(R.menu.api_app_settings, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_api_settings_revoke: + revokeAccess(); + return true; + case R.id.menu_api_settings_cancel: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void loadData(Uri appUri) { + AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri); + settingsFragment.setAppSettings(settings); + } + + private void revokeAccess() { + if (getContentResolver().delete(mAppUri, null, null) <= 0) { + throw new RuntimeException(); + } + finish(); + } + + private void save() { + ProviderHelper.updateApiApp(this, settingsFragment.getAppSettings(), mAppUri); + + finish(); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsFragment.java new file mode 100644 index 000000000..ad463d316 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/AppSettingsFragment.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import java.util.HashMap; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.SelectSecretKeyActivity; +import org.sufficientlysecure.keychain.util.KeyValueSpinnerAdapter; +import org.sufficientlysecure.keychain.util.Log; + +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +public class AppSettingsFragment extends Fragment { + + // model + private AppSettings appSettings; + + // view + private LinearLayout mAdvancedSettingsContainer; + private Button mAdvancedSettingsButton; + private TextView mAppNameView; + private ImageView mAppIconView; + private TextView mKeyUserId; + private TextView mKeyUserIdRest; + private Button mSelectKeyButton; + private Spinner mEncryptionAlgorithm; + private Spinner mHashAlgorithm; + private Spinner mCompression; + + KeyValueSpinnerAdapter encryptionAdapter; + KeyValueSpinnerAdapter hashAdapter; + KeyValueSpinnerAdapter compressionAdapter; + + public AppSettings getAppSettings() { + return appSettings; + } + + public void setAppSettings(AppSettings appSettings) { + this.appSettings = appSettings; + setPackage(appSettings.getPackageName()); + updateSelectedKeyView(appSettings.getKeyId()); + mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings + .getEncryptionAlgorithm())); + mHashAlgorithm.setSelection(hashAdapter.getPosition(appSettings.getHashAlgorithm())); + mCompression.setSelection(compressionAdapter.getPosition(appSettings.getCompression())); + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false); + initView(view); + return view; + } + + private void initView(View view) { + mAdvancedSettingsButton = (Button) 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); + mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id); + mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest); + mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button); + 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); + + HashMap encryptionMap = new HashMap(); + encryptionMap.put(PGPEncryptedData.AES_128, "AES-128"); + encryptionMap.put(PGPEncryptedData.AES_192, "AES-192"); + encryptionMap.put(PGPEncryptedData.AES_256, "AES-256"); + encryptionMap.put(PGPEncryptedData.BLOWFISH, "Blowfish"); + encryptionMap.put(PGPEncryptedData.TWOFISH, "Twofish"); + encryptionMap.put(PGPEncryptedData.CAST5, "CAST5"); + encryptionMap.put(PGPEncryptedData.DES, "DES"); + encryptionMap.put(PGPEncryptedData.TRIPLE_DES, "Triple DES"); + encryptionMap.put(PGPEncryptedData.IDEA, "IDEA"); + + encryptionAdapter = new KeyValueSpinnerAdapter(getActivity(), encryptionMap); + mEncryptionAlgorithm.setAdapter(encryptionAdapter); + mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + appSettings.setEncryptionAlgorithm((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + HashMap hashMap = new HashMap(); + hashMap.put(HashAlgorithmTags.MD5, "MD5"); + hashMap.put(HashAlgorithmTags.RIPEMD160, "RIPEMD-160"); + hashMap.put(HashAlgorithmTags.SHA1, "SHA-1"); + hashMap.put(HashAlgorithmTags.SHA224, "SHA-224"); + hashMap.put(HashAlgorithmTags.SHA256, "SHA-256"); + hashMap.put(HashAlgorithmTags.SHA384, "SHA-384"); + hashMap.put(HashAlgorithmTags.SHA512, "SHA-512"); + + hashAdapter = new KeyValueSpinnerAdapter(getActivity(), hashMap); + mHashAlgorithm.setAdapter(hashAdapter); + mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + appSettings.setHashAlgorithm((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + HashMap compressionMap = new HashMap(); + compressionMap.put(Id.choice.compression.none, getString(R.string.choice_none) + " (" + + getString(R.string.fast) + ")"); + compressionMap.put(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"); + compressionMap.put(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"); + compressionMap.put(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) + + ")"); + + compressionAdapter = new KeyValueSpinnerAdapter(getActivity(), compressionMap); + mCompression.setAdapter(compressionAdapter); + mCompression.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + appSettings.setCompression((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + mSelectKeyButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + selectSecretKey(); + } + }); + + 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); + // animation2.setDuration(150); + + mAdvancedSettingsButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) { + mAdvancedSettingsContainer.startAnimation(invisibleAnimation); + mAdvancedSettingsContainer.setVisibility(View.INVISIBLE); + mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced); + } else { + mAdvancedSettingsContainer.startAnimation(visibleAnimation); + mAdvancedSettingsContainer.setVisibility(View.VISIBLE); + mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced); + } + } + }); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + private void selectSecretKey() { + Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class); + startActivityForResult(intent, Id.request.secret_keys); + } + + 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 updateSelectedKeyView(long secretKeyId) { + if (secretKeyId == Id.key.none) { + mKeyUserId.setText(R.string.api_settings_no_key); + mKeyUserIdRest.setText(""); + } else { + String uid = getResources().getString(R.string.unknownUserId); + String uidExtra = ""; + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + getActivity(), secretKeyId); + if (keyRing != null) { + PGPSecretKey key = PgpHelper.getMasterKey(keyRing); + if (key != null) { + String userId = PgpHelper.getMainUserIdSafe(getActivity(), key); + String chunks[] = userId.split(" <", 2); + uid = chunks[0]; + if (chunks.length > 1) { + uidExtra = "<" + chunks[1]; + } + } + } + mKeyUserId.setText(uid); + mKeyUserIdRest.setText(uidExtra); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d(Constants.TAG, "onactivityresult " + requestCode + " " + resultCode); + switch (requestCode) { + + case Id.request.secret_keys: { + long secretKeyId; + if (resultCode == Activity.RESULT_OK) { + Bundle bundle = data.getExtras(); + secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + + } else { + secretKeyId = Id.key.none; + } + appSettings.setKeyId(secretKeyId); + updateSelectedKeyView(secretKeyId); + break; + } + + default: { + break; + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpService.java new file mode 100644 index 000000000..beefa56b4 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpService.java @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; + +import org.openintents.openpgp.IOpenPgpCallback; +import org.openintents.openpgp.IOpenPgpService; +import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +public class OpenPgpService extends Service { + Context mContext; + + final ArrayBlockingQueue mPoolQueue = new ArrayBlockingQueue(100); + // TODO: Are these parameters okay? + PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10, + TimeUnit.SECONDS, mPoolQueue); + + final Object userInputLock = new Object(); + + private class MyBaseCallback implements Handler.Callback { + public static final int OKAY = 1; + public static final int CANCEL = 0; + + @Override + public boolean handleMessage(Message msg) { + return false; + } + + } + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + Log.d(Constants.TAG, "CryptoService, onCreate()"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(Constants.TAG, "CryptoService, onDestroy()"); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + private String getCachedPassphrase(long keyId) { + String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId); + + if (passphrase == null) { + Log.d(Constants.TAG, "No passphrase! Activity required!"); + + // start passphrase dialog + Bundle extras = new Bundle(); + extras.putLong(OpenPgpServiceActivity.EXTRA_SECRET_KEY_ID, keyId); + + PassphraseActivityCallback callback = new PassphraseActivityCallback(); + Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); + + pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_CACHE_PASSPHRASE, + messenger, extras); + + if (callback.isSuccess()) { + Log.d(Constants.TAG, "New passphrase entered!"); + + // get again after it was entered + passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId); + } else { + Log.d(Constants.TAG, "Passphrase dialog canceled!"); + + return null; + } + + } + + return passphrase; + } + + public class PassphraseActivityCallback extends MyBaseCallback { + + private boolean success = false; + + public boolean isSuccess() { + return success; + } + + @Override + public boolean handleMessage(Message msg) { + if (msg.arg1 == OKAY) { + success = true; + } else { + success = false; + } + + // resume + synchronized (userInputLock) { + userInputLock.notifyAll(); + } + mThreadPool.resume(); + return true; + } + }; + + /** + * Search database for key ids based on emails. + * + * @param encryptionUserIds + * @return + */ + private long[] getKeyIdsFromEmails(String[] encryptionUserIds, long ownKeyId) { + // find key ids to given emails in database + ArrayList keyIds = new ArrayList(); + + boolean missingUserIdsCheck = false; + boolean dublicateUserIdsCheck = false; + ArrayList missingUserIds = new ArrayList(); + ArrayList dublicateUserIds = new ArrayList(); + + for (String email : encryptionUserIds) { + Uri uri = KeychainContract.KeyRings.buildPublicKeyRingsByEmailsUri(email); + Cursor cur = getContentResolver().query(uri, null, null, null, null); + if (cur.moveToFirst()) { + long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID)); + keyIds.add(id); + } else { + missingUserIdsCheck = true; + missingUserIds.add(email); + Log.d(Constants.TAG, "user id missing"); + } + if (cur.moveToNext()) { + dublicateUserIdsCheck = true; + dublicateUserIds.add(email); + Log.d(Constants.TAG, "more than one user id with the same email"); + } + } + + // also encrypt to our self (so that we can decrypt it later!) + keyIds.add(ownKeyId); + + // convert to long[] + long[] keyIdsArray = new long[keyIds.size()]; + for (int i = 0; i < keyIdsArray.length; i++) { + keyIdsArray[i] = keyIds.get(i); + } + + if (missingUserIdsCheck || dublicateUserIdsCheck) { + SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback(); + Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); + + Bundle extras = new Bundle(); + extras.putLongArray(OpenPgpServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray); + extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds); + extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_DUBLICATE_USER_IDS, + dublicateUserIds); + + pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_SELECT_PUB_KEYS, + messenger, extras); + + if (callback.isSuccess()) { + Log.d(Constants.TAG, "New selection of pub keys!"); + keyIdsArray = callback.getPubKeyIds(); + } else { + Log.d(Constants.TAG, "Pub key selection canceled!"); + return null; + } + } + + if (keyIdsArray.length == 0) { + return null; + } + return keyIdsArray; + } + + public class SelectPubKeysActivityCallback extends MyBaseCallback { + public static final String PUB_KEY_IDS = "pub_key_ids"; + + private boolean success = false; + private long[] pubKeyIds; + + public boolean isSuccess() { + return success; + } + + public long[] getPubKeyIds() { + return pubKeyIds; + } + + @Override + public boolean handleMessage(Message msg) { + if (msg.arg1 == OKAY) { + success = true; + pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS); + } else { + success = false; + } + + // resume + synchronized (userInputLock) { + userInputLock.notifyAll(); + } + mThreadPool.resume(); + return true; + } + }; + + private synchronized void encryptAndSignSafe(byte[] inputBytes, String[] encryptionUserIds, + boolean asciiArmor, IOpenPgpCallback callback, AppSettings appSettings, boolean sign) + throws RemoteException { + try { + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outputStream = new ByteArrayOutputStream(); + + long[] keyIds = getKeyIdsFromEmails(encryptionUserIds, appSettings.getKeyId()); + if (keyIds == null) { + callback.onError(new OpenPgpError(OpenPgpError.ID_NO_USER_IDS, "No user ids!")); + return; + } + + if (sign) { + String passphrase = getCachedPassphrase(appSettings.getKeyId()); + if (passphrase == null) { + callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, + "No or wrong passphrase!")); + return; + } + + PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor, + appSettings.getCompression(), keyIds, null, + appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(), + appSettings.getHashAlgorithm(), true, passphrase); + } else { + PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor, + appSettings.getCompression(), keyIds, null, + appSettings.getEncryptionAlgorithm(), Id.key.none, + appSettings.getHashAlgorithm(), true, null); + } + + outputStream.close(); + + byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); + + // return over handler on client side + callback.onSuccess(outputBytes, null); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + callback.onError(new OpenPgpError(0, e.getMessage())); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + // TODO: asciiArmor?! + private void signSafe(byte[] inputBytes, IOpenPgpCallback callback, AppSettings appSettings) + throws RemoteException { + try { + Log.d(Constants.TAG, "current therad id: " + Thread.currentThread().getId()); + + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outputStream = new ByteArrayOutputStream(); + + String passphrase = getCachedPassphrase(appSettings.getKeyId()); + if (passphrase == null) { + callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, + "No or wrong passphrase!")); + return; + } + + PgpMain.signText(this, null, inputData, outputStream, appSettings.getKeyId(), + passphrase, appSettings.getHashAlgorithm(), Preferences.getPreferences(this) + .getForceV3Signatures()); + + outputStream.close(); + + byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); + + // return over handler on client side + callback.onSuccess(outputBytes, null); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + callback.onError(new OpenPgpError(0, e.getMessage())); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + private synchronized void decryptAndVerifySafe(byte[] inputBytes, IOpenPgpCallback callback, + AppSettings appSettings) throws RemoteException { + try { + // TODO: this is not really needed + // checked if it is text with BEGIN and END tags + String message = new String(inputBytes); + Log.d(Constants.TAG, "in: " + message); + boolean signedOnly = false; + Matcher matcher = PgpMain.PGP_MESSAGE.matcher(message); + if (matcher.matches()) { + Log.d(Constants.TAG, "PGP_MESSAGE matched"); + message = matcher.group(1); + // replace non breakable spaces + message = message.replaceAll("\\xa0", " "); + + // overwrite inputBytes + inputBytes = message.getBytes(); + } else { + matcher = PgpMain.PGP_SIGNED_MESSAGE.matcher(message); + if (matcher.matches()) { + signedOnly = true; + Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched"); + message = matcher.group(1); + // replace non breakable spaces + message = message.replaceAll("\\xa0", " "); + + // overwrite inputBytes + inputBytes = message.getBytes(); + } else { + Log.d(Constants.TAG, "Nothing matched! Binary?"); + } + } + // END TODO + + Log.d(Constants.TAG, "in: " + new String(inputBytes)); + + // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the + // app, Fix this? + // long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream); + // if (secretKeyId == Id.key.none) { + // throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound)); + // } + + String passphrase = null; + boolean assumeSymmetricEncryption = false; + if (!signedOnly) { + // BEGIN Get key + // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it + // better! + InputStream inputStream2 = new ByteArrayInputStream(inputBytes); + + // TODO: duplicates functions from DecryptActivity! + // TODO: we need activity to input symmetric passphrase + long secretKeyId; + try { + if (inputStream2.markSupported()) { + inputStream2.mark(200); // should probably set this to the max size of two + // pgpF + // objects, if it even needs to be anything other + // than + // 0. + } + secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2); + if (secretKeyId == Id.key.none) { + throw new PgpMain.PgpGeneralException( + getString(R.string.error_noSecretKeyFound)); + } + assumeSymmetricEncryption = false; + } catch (PgpMain.NoAsymmetricEncryptionException e) { + if (inputStream2.markSupported()) { + inputStream2.reset(); + } + secretKeyId = Id.key.symmetric; + if (!PgpMain.hasSymmetricEncryption(this, inputStream2)) { + throw new PgpMain.PgpGeneralException( + getString(R.string.error_noKnownEncryptionFound)); + } + assumeSymmetricEncryption = true; + } + + Log.d(Constants.TAG, "secretKeyId " + secretKeyId); + + passphrase = getCachedPassphrase(secretKeyId); + if (passphrase == null) { + callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, + "No or wrong passphrase!")); + return; + } + } + + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outputStream = new ByteArrayOutputStream(); + + Bundle outputBundle; + if (signedOnly) { + // TODO: download missing keys from keyserver? + outputBundle = PgpMain.verifyText(this, null, inputData, outputStream, false); + } else { + // TODO: assume symmetric: callback to enter symmetric pass + outputBundle = PgpMain.decryptAndVerify(this, null, inputData, outputStream, + passphrase, assumeSymmetricEncryption); + } + + outputStream.close(); + + byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); + + // get signature informations from bundle + boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE); + long signatureKeyId = outputBundle + .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); + String signatureUserId = outputBundle + .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); + boolean signatureSuccess = outputBundle + .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS); + boolean signatureUnknown = outputBundle + .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN); + + OpenPgpSignatureResult sigResult = null; + if (signature) { + sigResult = new OpenPgpSignatureResult(signatureUserId, signatureSuccess, + signatureUnknown); + } + + // return over handler on client side + callback.onSuccess(outputBytes, sigResult); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + callback.onError(new OpenPgpError(0, e.getMessage())); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { + + @Override + public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds, + final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException { + + final AppSettings settings = getAppSettings(); + + Runnable r = new Runnable() { + + @Override + public void run() { + try { + encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback, + settings, false); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + }; + + checkAndEnqueue(r); + } + + @Override + public void encryptAndSign(final byte[] inputBytes, final String[] encryptionUserIds, + final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException { + + final AppSettings settings = getAppSettings(); + + Runnable r = new Runnable() { + + @Override + public void run() { + try { + encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback, + settings, true); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + }; + + checkAndEnqueue(r); + } + + @Override + public void sign(final byte[] inputBytes, boolean asciiArmor, + final IOpenPgpCallback callback) throws RemoteException { + final AppSettings settings = getAppSettings(); + + Runnable r = new Runnable() { + + @Override + public void run() { + try { + signSafe(inputBytes, callback, settings); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + }; + + checkAndEnqueue(r); + + } + + @Override + public void decryptAndVerify(final byte[] inputBytes, final IOpenPgpCallback callback) + throws RemoteException { + + final AppSettings settings = getAppSettings(); + + Runnable r = new Runnable() { + + @Override + public void run() { + try { + decryptAndVerifySafe(inputBytes, callback, settings); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + }; + + checkAndEnqueue(r); + } + + }; + + private void checkAndEnqueue(Runnable r) { + if (isCallerAllowed(false)) { + mThreadPool.execute(r); + + Log.d(Constants.TAG, "Enqueued runnable…"); + } else { + String[] callingPackages = getPackageManager() + .getPackagesForUid(Binder.getCallingUid()); + + Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!"); + Bundle extras = new Bundle(); + // TODO: currently simply uses first entry + extras.putString(OpenPgpServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]); + + RegisterActivityCallback callback = new RegisterActivityCallback(); + Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); + + pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_REGISTER, messenger, + extras); + + if (callback.isAllowed()) { + mThreadPool.execute(r); + Log.d(Constants.TAG, "Enqueued runnable…"); + } else { + Log.d(Constants.TAG, "User disallowed app!"); + } + } + } + + public class RegisterActivityCallback extends MyBaseCallback { + public static final String PACKAGE_NAME = "package_name"; + + private boolean allowed = false; + private String packageName; + + public boolean isAllowed() { + return allowed; + } + + public String getPackageName() { + return packageName; + } + + @Override + public boolean handleMessage(Message msg) { + if (msg.arg1 == OKAY) { + allowed = true; + packageName = msg.getData().getString(PACKAGE_NAME); + + // resume threads + if (isPackageAllowed(packageName, false)) { + synchronized (userInputLock) { + userInputLock.notifyAll(); + } + mThreadPool.resume(); + } else { + // Should not happen! + Log.e(Constants.TAG, "Should not happen! Emergency shutdown!"); + mThreadPool.shutdownNow(); + } + } else { + allowed = false; + + synchronized (userInputLock) { + userInputLock.notifyAll(); + } + mThreadPool.resume(); + } + return true; + } + + } + + /** + * Checks if process that binds to this service (i.e. the package name corresponding to the + * process) is in the list of allowed package names. + * + * @param allowOnlySelf + * allow only Keychain app itself + * @return true if process is allowed to use this service + */ + private boolean isCallerAllowed(boolean allowOnlySelf) { + String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + + // is calling package allowed to use this service? + for (int i = 0; i < callingPackages.length; i++) { + String currentPkg = callingPackages[i]; + + if (isPackageAllowed(currentPkg, allowOnlySelf)) { + return true; + } + } + + Log.d(Constants.TAG, "Caller is NOT allowed!"); + return false; + } + + private AppSettings getAppSettings() { + String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + + // is calling package allowed to use this service? + for (int i = 0; i < callingPackages.length; i++) { + String currentPkg = callingPackages[i]; + + Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg); + + AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); + + return settings; + } + + return null; + } + + /** + * Checks if packageName is a registered app for the API. + * + * @param packageName + * @param allowOnlySelf + * allow only Keychain app itself + * @return + */ + private boolean isPackageAllowed(String packageName, boolean allowOnlySelf) { + Log.d(Constants.TAG, "packageName: " + packageName); + + ArrayList allowedPkgs = ProviderHelper.getRegisteredApiApps(mContext); + Log.d(Constants.TAG, "allowed: " + allowedPkgs); + + // check if package is allowed to use our service + if (allowedPkgs.contains(packageName) && (!allowOnlySelf)) { + Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName); + + return true; + } else if (Constants.PACKAGE_NAME.equals(packageName)) { + Log.d(Constants.TAG, "Package is OpenPGP Keychain! -> allowed!"); + + return true; + } + + return false; + } + + private void pauseQueueAndStartServiceActivity(String action, Messenger messenger, Bundle extras) { + synchronized (userInputLock) { + mThreadPool.pause(); + + Log.d(Constants.TAG, "starting activity..."); + Intent intent = new Intent(getBaseContext(), OpenPgpServiceActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(action); + + extras.putParcelable(OpenPgpServiceActivity.EXTRA_MESSENGER, messenger); + intent.putExtras(extras); + + startActivity(intent); + + // lock current thread for user input + try { + userInputLock.wait(); + } catch (InterruptedException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpServiceActivity.java new file mode 100644 index 000000000..775828124 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/OpenPgpServiceActivity.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import java.util.ArrayList; + +import org.sufficientlysecure.htmltextview.HtmlTextView; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.view.View; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +public class OpenPgpServiceActivity extends SherlockFragmentActivity { + + public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER"; + 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 + + "API_ACTIVITY_SELECT_PUB_KEYS"; + + public static final String EXTRA_MESSENGER = "messenger"; + + // passphrase action + public static final String EXTRA_SECRET_KEY_ID = "secret_key_id"; + // register action + public static final String EXTRA_PACKAGE_NAME = "package_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"; + public static final String EXTRA_DUBLICATE_USER_IDS = "dublicate_user_ids"; + + private Messenger mMessenger; + + // register view + private AppSettingsFragment mSettingsFragment; + // select pub keys view + private SelectPublicKeyFragment mSelectFragment; + + // has the user clicked one of the buttons + // or do we need to handle the callback in onStop() + private boolean finishHandled; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + handleActions(getIntent(), savedInstanceState); + } + + @Override + protected void onStop() { + super.onStop(); + + if (!finishHandled) { + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + } + } + + protected void handleActions(Intent intent, Bundle savedInstanceState) { + finishHandled = false; + + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + mMessenger = extras.getParcelable(EXTRA_MESSENGER); + + /** + * com.android.crypto actions + */ + if (ACTION_REGISTER.equals(action)) { + final String packageName = extras.getString(EXTRA_PACKAGE_NAME); + + // Inflate a "Done"/"Cancel" custom action bar view + ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // Allow + + // user needs to select a key! + if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) { + Toast.makeText(OpenPgpServiceActivity.this, + R.string.api_register_error_select_key, Toast.LENGTH_LONG) + .show(); + } else { + ProviderHelper.insertApiApp(OpenPgpServiceActivity.this, + mSettingsFragment.getAppSettings()); + + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.RegisterActivityCallback.OKAY; + Bundle data = new Bundle(); + data.putString(OpenPgpService.RegisterActivityCallback.PACKAGE_NAME, + packageName); + msg.setData(data); + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + + finishHandled = true; + finish(); + } + } + }, R.string.api_register_disallow, new View.OnClickListener() { + @Override + public void onClick(View v) { + // Disallow + + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + + finishHandled = true; + finish(); + } + }); + + setContentView(R.layout.api_app_register_activity); + + mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( + R.id.api_app_settings_fragment); + + AppSettings settings = new AppSettings(packageName); + mSettingsFragment.setAppSettings(settings); + } else if (ACTION_CACHE_PASSPHRASE.equals(action)) { + long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID); + + showPassphraseDialog(secretKeyId); + } else if (ACTION_SELECT_PUB_KEYS.equals(action)) { + long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); + ArrayList missingUserIds = intent + .getStringArrayListExtra(EXTRA_MISSING_USER_IDS); + ArrayList dublicateUserIds = intent + .getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS); + + String text = new String(); + text += "" + getString(R.string.api_select_pub_keys_text) + ""; + text += "

"; + if (missingUserIds != null && missingUserIds.size() > 0) { + text += getString(R.string.api_select_pub_keys_missing_text); + text += "
"; + text += "
    "; + for (String userId : missingUserIds) { + text += "
  • " + userId + "
  • "; + } + text += "
"; + text += "
"; + } + if (dublicateUserIds != null && dublicateUserIds.size() > 0) { + text += getString(R.string.api_select_pub_keys_dublicates_text); + text += "
"; + text += "
    "; + for (String userId : dublicateUserIds) { + text += "
  • " + userId + "
  • "; + } + text += "
"; + } + + // Inflate a "Done"/"Cancel" custom action bar view + ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // ok + + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY; + Bundle data = new Bundle(); + data.putLongArray( + OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS, + mSelectFragment.getSelectedMasterKeyIds()); + msg.setData(data); + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + + finishHandled = true; + finish(); + } + }, R.string.btn_doNotSave, new View.OnClickListener() { + @Override + public void onClick(View v) { + // cancel + + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL; + ; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + + finishHandled = true; + finish(); + } + }); + + setContentView(R.layout.api_app_select_pub_keys_activity); + + // set text on view + HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text); + textView.setHtmlFromString(text); + + /* Load select pub keys fragment */ + // Check that the activity is using the layout version with + // the fragment_container FrameLayout + if (findViewById(R.id.api_select_pub_keys_fragment_container) != null) { + + // 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 + mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds); + + // Add the fragment to the 'fragment_container' FrameLayout + getSupportFragmentManager().beginTransaction() + .add(R.id.api_select_pub_keys_fragment_container, mSelectFragment).commit(); + } + } else { + Log.e(Constants.TAG, "Wrong action!"); + finish(); + } + } + + /** + * Shows passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks + * for a symmetric passphrase + */ + private void showPassphraseDialog(long secretKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + } else { + Message msg = Message.obtain(); + msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoServiceActivity", e); + } + } + + finishHandled = true; + finish(); + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, + messenger, secretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PgpMain.PgpGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsAdapter.java new file mode 100644 index 000000000..bddf3d4a8 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsAdapter.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; + +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; + +public class RegisteredAppsAdapter extends CursorAdapter { + + private LayoutInflater mInflater; + private PackageManager pm; + + public RegisteredAppsAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + pm = 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 = pm.getApplicationInfo(packageName, 0); + + text.setText(pm.getApplicationLabel(ai)); + icon.setImageDrawable(pm.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/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListActivity.java new file mode 100644 index 000000000..d8d7c3d5c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListActivity.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.MainActivity; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class RegisteredAppsListActivity extends SherlockFragmentActivity { + private ActionBar mActionBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mActionBar = getSupportActionBar(); + + setContentView(R.layout.api_apps_list_activity); + + mActionBar.setDisplayShowTitleEnabled(true); + mActionBar.setDisplayHomeAsUpEnabled(true); + } + + /** + * Menu Options + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListFragment.java new file mode 100644 index 000000000..391b86d72 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/openpgp_api/RegisteredAppsListFragment.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.openpgp_api; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; + +import android.content.ContentUris; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +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 com.actionbarsherlock.app.SherlockListFragment; + +public class RegisteredAppsListFragment extends SherlockListFragment implements + LoaderManager.LoaderCallbacks { + + // This is the Adapter being used to display the list's data. + RegisteredAppsAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + 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(KeychainContract.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_no_apps)); + + // 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 RegisteredAppsAdapter(getActivity(), null, 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID, + ApiApps.PACKAGE_NAME }; + + public Loader 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 = ApiApps.CONTENT_URI; + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null, + ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader 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 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); + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 4e9a81908..cb43155ab 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -32,12 +32,12 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.helper.PgpConversionHelper; import org.sufficientlysecure.keychain.helper.PgpHelper; import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.openpgp_api.AppSettings; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; 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.remote_api.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java deleted file mode 100644 index eb2527455..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.sufficientlysecure.keychain.Id; - -public class AppSettings { - private String packageName; - private long keyId = Id.key.none; - private int encryptionAlgorithm; - private int hashAlgorithm; - private int compression; - - public AppSettings() { - - } - - public AppSettings(String packageName) { - super(); - this.packageName = packageName; - // defaults: - this.encryptionAlgorithm = PGPEncryptedData.AES_128; - this.hashAlgorithm = HashAlgorithmTags.SHA256; - this.compression = Id.choice.compression.zlib; - } - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public long getKeyId() { - return keyId; - } - - public void setKeyId(long scretKeyId) { - this.keyId = scretKeyId; - } - - public int getEncryptionAlgorithm() { - return encryptionAlgorithm; - } - - public void setEncryptionAlgorithm(int encryptionAlgorithm) { - this.encryptionAlgorithm = encryptionAlgorithm; - } - - public int getHashAlgorithm() { - return hashAlgorithm; - } - - public void setHashAlgorithm(int hashAlgorithm) { - this.hashAlgorithm = hashAlgorithm; - } - - public int getCompression() { - return compression; - } - - public void setCompression(int compression) { - this.compression = compression; - } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java deleted file mode 100644 index 2d3b23c1b..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -public class AppSettingsActivity extends SherlockFragmentActivity { - private Uri mAppUri; - - private AppSettingsFragment settingsFragment; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Inflate a "Done" custom action bar view to serve as the "Up" affordance. - ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // "Done" - save(); - } - }); - - setContentView(R.layout.api_app_settings_activity); - - settingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( - R.id.api_app_settings_fragment); - - Intent intent = getIntent(); - mAppUri = intent.getData(); - if (mAppUri == null) { - Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); - finish(); - return; - } else { - Log.d(Constants.TAG, "uri: " + mAppUri); - loadData(mAppUri); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getSupportMenuInflater().inflate(R.menu.api_app_settings, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_api_settings_revoke: - revokeAccess(); - return true; - case R.id.menu_api_settings_cancel: - finish(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void loadData(Uri appUri) { - AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri); - settingsFragment.setAppSettings(settings); - } - - private void revokeAccess() { - if (getContentResolver().delete(mAppUri, null, null) <= 0) { - throw new RuntimeException(); - } - finish(); - } - - private void save() { - ProviderHelper.updateApiApp(this, settingsFragment.getAppSettings(), mAppUri); - - finish(); - } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java deleted file mode 100644 index a29b63e97..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import java.util.HashMap; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.SelectSecretKeyActivity; -import org.sufficientlysecure.keychain.util.KeyValueSpinnerAdapter; -import org.sufficientlysecure.keychain.util.Log; - -import android.app.Activity; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -public class AppSettingsFragment extends Fragment { - - // model - private AppSettings appSettings; - - // view - private LinearLayout mAdvancedSettingsContainer; - private Button mAdvancedSettingsButton; - private TextView mAppNameView; - private ImageView mAppIconView; - private TextView mKeyUserId; - private TextView mKeyUserIdRest; - private Button mSelectKeyButton; - private Spinner mEncryptionAlgorithm; - private Spinner mHashAlgorithm; - private Spinner mCompression; - - KeyValueSpinnerAdapter encryptionAdapter; - KeyValueSpinnerAdapter hashAdapter; - KeyValueSpinnerAdapter compressionAdapter; - - public AppSettings getAppSettings() { - return appSettings; - } - - public void setAppSettings(AppSettings appSettings) { - this.appSettings = appSettings; - setPackage(appSettings.getPackageName()); - updateSelectedKeyView(appSettings.getKeyId()); - mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings - .getEncryptionAlgorithm())); - mHashAlgorithm.setSelection(hashAdapter.getPosition(appSettings.getHashAlgorithm())); - mCompression.setSelection(compressionAdapter.getPosition(appSettings.getCompression())); - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false); - initView(view); - return view; - } - - private void initView(View view) { - mAdvancedSettingsButton = (Button) 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); - mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id); - mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest); - mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button); - 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); - - HashMap encryptionMap = new HashMap(); - encryptionMap.put(PGPEncryptedData.AES_128, "AES-128"); - encryptionMap.put(PGPEncryptedData.AES_192, "AES-192"); - encryptionMap.put(PGPEncryptedData.AES_256, "AES-256"); - encryptionMap.put(PGPEncryptedData.BLOWFISH, "Blowfish"); - encryptionMap.put(PGPEncryptedData.TWOFISH, "Twofish"); - encryptionMap.put(PGPEncryptedData.CAST5, "CAST5"); - encryptionMap.put(PGPEncryptedData.DES, "DES"); - encryptionMap.put(PGPEncryptedData.TRIPLE_DES, "Triple DES"); - encryptionMap.put(PGPEncryptedData.IDEA, "IDEA"); - - encryptionAdapter = new KeyValueSpinnerAdapter(getActivity(), encryptionMap); - mEncryptionAlgorithm.setAdapter(encryptionAdapter); - mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - appSettings.setEncryptionAlgorithm((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - HashMap hashMap = new HashMap(); - hashMap.put(HashAlgorithmTags.MD5, "MD5"); - hashMap.put(HashAlgorithmTags.RIPEMD160, "RIPEMD-160"); - hashMap.put(HashAlgorithmTags.SHA1, "SHA-1"); - hashMap.put(HashAlgorithmTags.SHA224, "SHA-224"); - hashMap.put(HashAlgorithmTags.SHA256, "SHA-256"); - hashMap.put(HashAlgorithmTags.SHA384, "SHA-384"); - hashMap.put(HashAlgorithmTags.SHA512, "SHA-512"); - - hashAdapter = new KeyValueSpinnerAdapter(getActivity(), hashMap); - mHashAlgorithm.setAdapter(hashAdapter); - mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - appSettings.setHashAlgorithm((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - HashMap compressionMap = new HashMap(); - compressionMap.put(Id.choice.compression.none, getString(R.string.choice_none) + " (" - + getString(R.string.fast) + ")"); - compressionMap.put(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"); - compressionMap.put(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"); - compressionMap.put(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) - + ")"); - - compressionAdapter = new KeyValueSpinnerAdapter(getActivity(), compressionMap); - mCompression.setAdapter(compressionAdapter); - mCompression.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - appSettings.setCompression((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - mSelectKeyButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - selectSecretKey(); - } - }); - - 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); - // animation2.setDuration(150); - - mAdvancedSettingsButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) { - mAdvancedSettingsContainer.startAnimation(invisibleAnimation); - mAdvancedSettingsContainer.setVisibility(View.INVISIBLE); - mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced); - } else { - mAdvancedSettingsContainer.startAnimation(visibleAnimation); - mAdvancedSettingsContainer.setVisibility(View.VISIBLE); - mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced); - } - } - }); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - } - - private void selectSecretKey() { - Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class); - startActivityForResult(intent, Id.request.secret_keys); - } - - 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 updateSelectedKeyView(long secretKeyId) { - if (secretKeyId == Id.key.none) { - mKeyUserId.setText(R.string.api_settings_no_key); - mKeyUserIdRest.setText(""); - } else { - String uid = getResources().getString(R.string.unknownUserId); - String uidExtra = ""; - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - getActivity(), secretKeyId); - if (keyRing != null) { - PGPSecretKey key = PgpHelper.getMasterKey(keyRing); - if (key != null) { - String userId = PgpHelper.getMainUserIdSafe(getActivity(), key); - String chunks[] = userId.split(" <", 2); - uid = chunks[0]; - if (chunks.length > 1) { - uidExtra = "<" + chunks[1]; - } - } - } - mKeyUserId.setText(uid); - mKeyUserIdRest.setText(uidExtra); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d(Constants.TAG, "onactivityresult " + requestCode + " " + resultCode); - switch (requestCode) { - - case Id.request.secret_keys: { - long secretKeyId; - if (resultCode == Activity.RESULT_OK) { - Bundle bundle = data.getExtras(); - secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); - - } else { - secretKeyId = Id.key.none; - } - appSettings.setKeyId(secretKeyId); - updateSelectedKeyView(secretKeyId); - break; - } - - default: { - break; - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpService.java deleted file mode 100644 index 7a44825bf..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpService.java +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; - -import org.openintents.openpgp.IOpenPgpCallback; -import org.openintents.openpgp.IOpenPgpService; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.util.InputData; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; - -public class OpenPgpService extends Service { - Context mContext; - - final ArrayBlockingQueue mPoolQueue = new ArrayBlockingQueue(100); - // TODO: Are these parameters okay? - PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10, - TimeUnit.SECONDS, mPoolQueue); - - final Object userInputLock = new Object(); - - private class MyBaseCallback implements Handler.Callback { - public static final int OKAY = 1; - public static final int CANCEL = 0; - - @Override - public boolean handleMessage(Message msg) { - return false; - } - - } - - @Override - public void onCreate() { - super.onCreate(); - mContext = this; - Log.d(Constants.TAG, "CryptoService, onCreate()"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(Constants.TAG, "CryptoService, onDestroy()"); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - private String getCachedPassphrase(long keyId) { - String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId); - - if (passphrase == null) { - Log.d(Constants.TAG, "No passphrase! Activity required!"); - - // start passphrase dialog - Bundle extras = new Bundle(); - extras.putLong(OpenPgpServiceActivity.EXTRA_SECRET_KEY_ID, keyId); - - PassphraseActivityCallback callback = new PassphraseActivityCallback(); - Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); - - pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_CACHE_PASSPHRASE, - messenger, extras); - - if (callback.isSuccess()) { - Log.d(Constants.TAG, "New passphrase entered!"); - - // get again after it was entered - passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId); - } else { - Log.d(Constants.TAG, "Passphrase dialog canceled!"); - - return null; - } - - } - - return passphrase; - } - - public class PassphraseActivityCallback extends MyBaseCallback { - - private boolean success = false; - - public boolean isSuccess() { - return success; - } - - @Override - public boolean handleMessage(Message msg) { - if (msg.arg1 == OKAY) { - success = true; - } else { - success = false; - } - - // resume - synchronized (userInputLock) { - userInputLock.notifyAll(); - } - mThreadPool.resume(); - return true; - } - }; - - /** - * Search database for key ids based on emails. - * - * @param encryptionUserIds - * @return - */ - private long[] getKeyIdsFromEmails(String[] encryptionUserIds, long ownKeyId) { - // find key ids to given emails in database - ArrayList keyIds = new ArrayList(); - - boolean missingUserIdsCheck = false; - boolean dublicateUserIdsCheck = false; - ArrayList missingUserIds = new ArrayList(); - ArrayList dublicateUserIds = new ArrayList(); - - for (String email : encryptionUserIds) { - Uri uri = KeychainContract.KeyRings.buildPublicKeyRingsByEmailsUri(email); - Cursor cur = getContentResolver().query(uri, null, null, null, null); - if (cur.moveToFirst()) { - long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID)); - keyIds.add(id); - } else { - missingUserIdsCheck = true; - missingUserIds.add(email); - Log.d(Constants.TAG, "user id missing"); - } - if (cur.moveToNext()) { - dublicateUserIdsCheck = true; - dublicateUserIds.add(email); - Log.d(Constants.TAG, "more than one user id with the same email"); - } - } - - // also encrypt to our self (so that we can decrypt it later!) - keyIds.add(ownKeyId); - - // convert to long[] - long[] keyIdsArray = new long[keyIds.size()]; - for (int i = 0; i < keyIdsArray.length; i++) { - keyIdsArray[i] = keyIds.get(i); - } - - if (missingUserIdsCheck || dublicateUserIdsCheck) { - SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback(); - Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); - - Bundle extras = new Bundle(); - extras.putLongArray(OpenPgpServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray); - extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds); - extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_DUBLICATE_USER_IDS, - dublicateUserIds); - - pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_SELECT_PUB_KEYS, - messenger, extras); - - if (callback.isSuccess()) { - Log.d(Constants.TAG, "New selection of pub keys!"); - keyIdsArray = callback.getPubKeyIds(); - } else { - Log.d(Constants.TAG, "Pub key selection canceled!"); - return null; - } - } - - if (keyIdsArray.length == 0) { - return null; - } - return keyIdsArray; - } - - public class SelectPubKeysActivityCallback extends MyBaseCallback { - public static final String PUB_KEY_IDS = "pub_key_ids"; - - private boolean success = false; - private long[] pubKeyIds; - - public boolean isSuccess() { - return success; - } - - public long[] getPubKeyIds() { - return pubKeyIds; - } - - @Override - public boolean handleMessage(Message msg) { - if (msg.arg1 == OKAY) { - success = true; - pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS); - } else { - success = false; - } - - // resume - synchronized (userInputLock) { - userInputLock.notifyAll(); - } - mThreadPool.resume(); - return true; - } - }; - - private synchronized void encryptAndSignSafe(byte[] inputBytes, String[] encryptionUserIds, - boolean asciiArmor, IOpenPgpCallback callback, AppSettings appSettings, boolean sign) - throws RemoteException { - try { - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData inputData = new InputData(inputStream, inputLength); - - OutputStream outputStream = new ByteArrayOutputStream(); - - long[] keyIds = getKeyIdsFromEmails(encryptionUserIds, appSettings.getKeyId()); - if (keyIds == null) { - callback.onError(new OpenPgpError(OpenPgpError.ID_NO_USER_IDS, "No user ids!")); - return; - } - - if (sign) { - String passphrase = getCachedPassphrase(appSettings.getKeyId()); - if (passphrase == null) { - callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, - "No or wrong passphrase!")); - return; - } - - PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor, - appSettings.getCompression(), keyIds, null, - appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(), - appSettings.getHashAlgorithm(), true, passphrase); - } else { - PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor, - appSettings.getCompression(), keyIds, null, - appSettings.getEncryptionAlgorithm(), Id.key.none, - appSettings.getHashAlgorithm(), true, null); - } - - outputStream.close(); - - byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); - - // return over handler on client side - callback.onSuccess(outputBytes, null); - } catch (Exception e) { - Log.e(Constants.TAG, "KeychainService, Exception!", e); - - try { - callback.onError(new OpenPgpError(0, e.getMessage())); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - // TODO: asciiArmor?! - private void signSafe(byte[] inputBytes, IOpenPgpCallback callback, AppSettings appSettings) - throws RemoteException { - try { - Log.d(Constants.TAG, "current therad id: " + Thread.currentThread().getId()); - - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData inputData = new InputData(inputStream, inputLength); - - OutputStream outputStream = new ByteArrayOutputStream(); - - String passphrase = getCachedPassphrase(appSettings.getKeyId()); - if (passphrase == null) { - callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, - "No or wrong passphrase!")); - return; - } - - PgpMain.signText(this, null, inputData, outputStream, appSettings.getKeyId(), - passphrase, appSettings.getHashAlgorithm(), Preferences.getPreferences(this) - .getForceV3Signatures()); - - outputStream.close(); - - byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); - - // return over handler on client side - callback.onSuccess(outputBytes, null); - } catch (Exception e) { - Log.e(Constants.TAG, "KeychainService, Exception!", e); - - try { - callback.onError(new OpenPgpError(0, e.getMessage())); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - private synchronized void decryptAndVerifySafe(byte[] inputBytes, IOpenPgpCallback callback, - AppSettings appSettings) throws RemoteException { - try { - // TODO: this is not really needed - // checked if it is text with BEGIN and END tags - String message = new String(inputBytes); - Log.d(Constants.TAG, "in: " + message); - boolean signedOnly = false; - Matcher matcher = PgpMain.PGP_MESSAGE.matcher(message); - if (matcher.matches()) { - Log.d(Constants.TAG, "PGP_MESSAGE matched"); - message = matcher.group(1); - // replace non breakable spaces - message = message.replaceAll("\\xa0", " "); - - // overwrite inputBytes - inputBytes = message.getBytes(); - } else { - matcher = PgpMain.PGP_SIGNED_MESSAGE.matcher(message); - if (matcher.matches()) { - signedOnly = true; - Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched"); - message = matcher.group(1); - // replace non breakable spaces - message = message.replaceAll("\\xa0", " "); - - // overwrite inputBytes - inputBytes = message.getBytes(); - } else { - Log.d(Constants.TAG, "Nothing matched! Binary?"); - } - } - // END TODO - - Log.d(Constants.TAG, "in: " + new String(inputBytes)); - - // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the - // app, Fix this? - // long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream); - // if (secretKeyId == Id.key.none) { - // throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound)); - // } - - String passphrase = null; - boolean assumeSymmetricEncryption = false; - if (!signedOnly) { - // BEGIN Get key - // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it - // better! - InputStream inputStream2 = new ByteArrayInputStream(inputBytes); - - // TODO: duplicates functions from DecryptActivity! - // TODO: we need activity to input symmetric passphrase - long secretKeyId; - try { - if (inputStream2.markSupported()) { - inputStream2.mark(200); // should probably set this to the max size of two - // pgpF - // objects, if it even needs to be anything other - // than - // 0. - } - secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2); - if (secretKeyId == Id.key.none) { - throw new PgpMain.PgpGeneralException( - getString(R.string.error_noSecretKeyFound)); - } - assumeSymmetricEncryption = false; - } catch (PgpMain.NoAsymmetricEncryptionException e) { - if (inputStream2.markSupported()) { - inputStream2.reset(); - } - secretKeyId = Id.key.symmetric; - if (!PgpMain.hasSymmetricEncryption(this, inputStream2)) { - throw new PgpMain.PgpGeneralException( - getString(R.string.error_noKnownEncryptionFound)); - } - assumeSymmetricEncryption = true; - } - - Log.d(Constants.TAG, "secretKeyId " + secretKeyId); - - passphrase = getCachedPassphrase(secretKeyId); - if (passphrase == null) { - callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE, - "No or wrong passphrase!")); - return; - } - } - - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData inputData = new InputData(inputStream, inputLength); - - OutputStream outputStream = new ByteArrayOutputStream(); - - Bundle outputBundle; - if (signedOnly) { - // TODO: download missing keys from keyserver? - outputBundle = PgpMain.verifyText(this, null, inputData, outputStream, false); - } else { - // TODO: assume symmetric: callback to enter symmetric pass - outputBundle = PgpMain.decryptAndVerify(this, null, inputData, outputStream, - passphrase, assumeSymmetricEncryption); - } - - outputStream.close(); - - byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); - - // get signature informations from bundle - boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE); - long signatureKeyId = outputBundle - .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); - String signatureUserId = outputBundle - .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); - boolean signatureSuccess = outputBundle - .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS); - boolean signatureUnknown = outputBundle - .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN); - - OpenPgpSignatureResult sigResult = null; - if (signature) { - sigResult = new OpenPgpSignatureResult(signatureUserId, signatureSuccess, - signatureUnknown); - } - - // return over handler on client side - callback.onSuccess(outputBytes, sigResult); - } catch (Exception e) { - Log.e(Constants.TAG, "KeychainService, Exception!", e); - - try { - callback.onError(new OpenPgpError(0, e.getMessage())); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { - - @Override - public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds, - final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException { - - final AppSettings settings = getAppSettings(); - - Runnable r = new Runnable() { - - @Override - public void run() { - try { - encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback, - settings, false); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoService", e); - } - } - }; - - checkAndEnqueue(r); - } - - @Override - public void encryptAndSign(final byte[] inputBytes, final String[] encryptionUserIds, - final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException { - - final AppSettings settings = getAppSettings(); - - Runnable r = new Runnable() { - - @Override - public void run() { - try { - encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback, - settings, true); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoService", e); - } - } - }; - - checkAndEnqueue(r); - } - - @Override - public void sign(final byte[] inputBytes, boolean asciiArmor, - final IOpenPgpCallback callback) throws RemoteException { - final AppSettings settings = getAppSettings(); - - Runnable r = new Runnable() { - - @Override - public void run() { - try { - signSafe(inputBytes, callback, settings); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoService", e); - } - } - }; - - checkAndEnqueue(r); - - } - - @Override - public void decryptAndVerify(final byte[] inputBytes, final IOpenPgpCallback callback) - throws RemoteException { - - final AppSettings settings = getAppSettings(); - - Runnable r = new Runnable() { - - @Override - public void run() { - try { - decryptAndVerifySafe(inputBytes, callback, settings); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoService", e); - } - } - }; - - checkAndEnqueue(r); - } - - }; - - private void checkAndEnqueue(Runnable r) { - if (isCallerAllowed(false)) { - mThreadPool.execute(r); - - Log.d(Constants.TAG, "Enqueued runnable…"); - } else { - String[] callingPackages = getPackageManager() - .getPackagesForUid(Binder.getCallingUid()); - - Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!"); - Bundle extras = new Bundle(); - // TODO: currently simply uses first entry - extras.putString(OpenPgpServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]); - - RegisterActivityCallback callback = new RegisterActivityCallback(); - Messenger messenger = new Messenger(new Handler(getMainLooper(), callback)); - - pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_REGISTER, messenger, - extras); - - if (callback.isAllowed()) { - mThreadPool.execute(r); - Log.d(Constants.TAG, "Enqueued runnable…"); - } else { - Log.d(Constants.TAG, "User disallowed app!"); - } - } - } - - public class RegisterActivityCallback extends MyBaseCallback { - public static final String PACKAGE_NAME = "package_name"; - - private boolean allowed = false; - private String packageName; - - public boolean isAllowed() { - return allowed; - } - - public String getPackageName() { - return packageName; - } - - @Override - public boolean handleMessage(Message msg) { - if (msg.arg1 == OKAY) { - allowed = true; - packageName = msg.getData().getString(PACKAGE_NAME); - - // resume threads - if (isPackageAllowed(packageName, false)) { - synchronized (userInputLock) { - userInputLock.notifyAll(); - } - mThreadPool.resume(); - } else { - // Should not happen! - Log.e(Constants.TAG, "Should not happen! Emergency shutdown!"); - mThreadPool.shutdownNow(); - } - } else { - allowed = false; - - synchronized (userInputLock) { - userInputLock.notifyAll(); - } - mThreadPool.resume(); - } - return true; - } - - } - - /** - * Checks if process that binds to this service (i.e. the package name corresponding to the - * process) is in the list of allowed package names. - * - * @param allowOnlySelf - * allow only Keychain app itself - * @return true if process is allowed to use this service - */ - private boolean isCallerAllowed(boolean allowOnlySelf) { - String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); - - // is calling package allowed to use this service? - for (int i = 0; i < callingPackages.length; i++) { - String currentPkg = callingPackages[i]; - - if (isPackageAllowed(currentPkg, allowOnlySelf)) { - return true; - } - } - - Log.d(Constants.TAG, "Caller is NOT allowed!"); - return false; - } - - private AppSettings getAppSettings() { - String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); - - // is calling package allowed to use this service? - for (int i = 0; i < callingPackages.length; i++) { - String currentPkg = callingPackages[i]; - - Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg); - - AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); - - return settings; - } - - return null; - } - - /** - * Checks if packageName is a registered app for the API. - * - * @param packageName - * @param allowOnlySelf - * allow only Keychain app itself - * @return - */ - private boolean isPackageAllowed(String packageName, boolean allowOnlySelf) { - Log.d(Constants.TAG, "packageName: " + packageName); - - ArrayList allowedPkgs = ProviderHelper.getRegisteredApiApps(mContext); - Log.d(Constants.TAG, "allowed: " + allowedPkgs); - - // check if package is allowed to use our service - if (allowedPkgs.contains(packageName) && (!allowOnlySelf)) { - Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName); - - return true; - } else if (Constants.PACKAGE_NAME.equals(packageName)) { - Log.d(Constants.TAG, "Package is OpenPGP Keychain! -> allowed!"); - - return true; - } - - return false; - } - - private void pauseQueueAndStartServiceActivity(String action, Messenger messenger, Bundle extras) { - synchronized (userInputLock) { - mThreadPool.pause(); - - Log.d(Constants.TAG, "starting activity..."); - Intent intent = new Intent(getBaseContext(), OpenPgpServiceActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setAction(action); - - extras.putParcelable(OpenPgpServiceActivity.EXTRA_MESSENGER, messenger); - intent.putExtras(extras); - - startActivity(intent); - - // lock current thread for user input - try { - userInputLock.wait(); - } catch (InterruptedException e) { - Log.e(Constants.TAG, "CryptoService", e); - } - } - - } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpServiceActivity.java deleted file mode 100644 index 3d3d046e1..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/OpenPgpServiceActivity.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import java.util.ArrayList; - -import org.sufficientlysecure.htmltextview.HtmlTextView; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; -import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; -import org.sufficientlysecure.keychain.util.Log; - -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.view.View; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragmentActivity; - -public class OpenPgpServiceActivity extends SherlockFragmentActivity { - - public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER"; - 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 - + "API_ACTIVITY_SELECT_PUB_KEYS"; - - public static final String EXTRA_MESSENGER = "messenger"; - - // passphrase action - public static final String EXTRA_SECRET_KEY_ID = "secret_key_id"; - // register action - public static final String EXTRA_PACKAGE_NAME = "package_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"; - public static final String EXTRA_DUBLICATE_USER_IDS = "dublicate_user_ids"; - - private Messenger mMessenger; - - // register view - private AppSettingsFragment mSettingsFragment; - // select pub keys view - private SelectPublicKeyFragment mSelectFragment; - - // has the user clicked one of the buttons - // or do we need to handle the callback in onStop() - private boolean finishHandled; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - handleActions(getIntent(), savedInstanceState); - } - - @Override - protected void onStop() { - super.onStop(); - - if (!finishHandled) { - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL; - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - } - } - - protected void handleActions(Intent intent, Bundle savedInstanceState) { - finishHandled = false; - - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - - if (extras == null) { - extras = new Bundle(); - } - - mMessenger = extras.getParcelable(EXTRA_MESSENGER); - - /** - * com.android.crypto actions - */ - if (ACTION_REGISTER.equals(action)) { - final String packageName = extras.getString(EXTRA_PACKAGE_NAME); - - // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // Allow - - // user needs to select a key! - if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) { - Toast.makeText(OpenPgpServiceActivity.this, - R.string.api_register_error_select_key, Toast.LENGTH_LONG) - .show(); - } else { - ProviderHelper.insertApiApp(OpenPgpServiceActivity.this, - mSettingsFragment.getAppSettings()); - - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.RegisterActivityCallback.OKAY; - Bundle data = new Bundle(); - data.putString(OpenPgpService.RegisterActivityCallback.PACKAGE_NAME, - packageName); - msg.setData(data); - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - - finishHandled = true; - finish(); - } - } - }, R.string.api_register_disallow, new View.OnClickListener() { - @Override - public void onClick(View v) { - // Disallow - - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL; - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - - finishHandled = true; - finish(); - } - }); - - setContentView(R.layout.api_app_register_activity); - - mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( - R.id.api_app_settings_fragment); - - AppSettings settings = new AppSettings(packageName); - mSettingsFragment.setAppSettings(settings); - } else if (ACTION_CACHE_PASSPHRASE.equals(action)) { - long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID); - - showPassphraseDialog(secretKeyId); - } else if (ACTION_SELECT_PUB_KEYS.equals(action)) { - long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); - ArrayList missingUserIds = intent - .getStringArrayListExtra(EXTRA_MISSING_USER_IDS); - ArrayList dublicateUserIds = intent - .getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS); - - String text = new String(); - text += "" + getString(R.string.api_select_pub_keys_text) + ""; - text += "

"; - if (missingUserIds != null && missingUserIds.size() > 0) { - text += getString(R.string.api_select_pub_keys_missing_text); - text += "
"; - text += "
    "; - for (String userId : missingUserIds) { - text += "
  • " + userId + "
  • "; - } - text += "
"; - text += "
"; - } - if (dublicateUserIds != null && dublicateUserIds.size() > 0) { - text += getString(R.string.api_select_pub_keys_dublicates_text); - text += "
"; - text += "
    "; - for (String userId : dublicateUserIds) { - text += "
  • " + userId + "
  • "; - } - text += "
"; - } - - // Inflate a "Done"/"Cancel" custom action bar view - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // ok - - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY; - Bundle data = new Bundle(); - data.putLongArray( - OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS, - mSelectFragment.getSelectedMasterKeyIds()); - msg.setData(data); - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - - finishHandled = true; - finish(); - } - }, R.string.btn_doNotSave, new View.OnClickListener() { - @Override - public void onClick(View v) { - // cancel - - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL; - ; - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - - finishHandled = true; - finish(); - } - }); - - setContentView(R.layout.api_app_select_pub_keys_activity); - - // set text on view - HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text); - textView.setHtmlFromString(text); - - /* Load select pub keys fragment */ - // Check that the activity is using the layout version with - // the fragment_container FrameLayout - if (findViewById(R.id.api_select_pub_keys_fragment_container) != null) { - - // 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 - mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds); - - // Add the fragment to the 'fragment_container' FrameLayout - getSupportFragmentManager().beginTransaction() - .add(R.id.api_select_pub_keys_fragment_container, mSelectFragment).commit(); - } - } else { - Log.e(Constants.TAG, "Wrong action!"); - finish(); - } - } - - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks - * for a symmetric passphrase - */ - private void showPassphraseDialog(long secretKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY; - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - } else { - Message msg = Message.obtain(); - msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL; - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.e(Constants.TAG, "CryptoServiceActivity", e); - } - } - - finishHandled = true; - finish(); - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, - messenger, secretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PgpMain.PgpGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java deleted file mode 100644 index 39fca58f7..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; - -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; - -public class RegisteredAppsAdapter extends CursorAdapter { - - private LayoutInflater mInflater; - private PackageManager pm; - - public RegisteredAppsAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - pm = 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 = pm.getApplicationInfo(packageName, 0); - - text.setText(pm.getApplicationLabel(ai)); - icon.setImageDrawable(pm.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/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java deleted file mode 100644 index 352ffe918..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.MainActivity; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class RegisteredAppsListActivity extends SherlockFragmentActivity { - private ActionBar mActionBar; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mActionBar = getSupportActionBar(); - - setContentView(R.layout.api_apps_list_activity); - - mActionBar.setDisplayShowTitleEnabled(true); - mActionBar.setDisplayHomeAsUpEnabled(true); - } - - /** - * Menu Options - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java deleted file mode 100644 index 84234fb13..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * 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.remote_api; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; - -import android.content.ContentUris; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -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 com.actionbarsherlock.app.SherlockListFragment; - -public class RegisteredAppsListFragment extends SherlockListFragment implements - LoaderManager.LoaderCallbacks { - - // This is the Adapter being used to display the list's data. - RegisteredAppsAdapter mAdapter; - - // If non-null, this is the current filter the user has provided. - String mCurFilter; - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - 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(KeychainContract.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_no_apps)); - - // 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 RegisteredAppsAdapter(getActivity(), null, 0); - setListAdapter(mAdapter); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - // These are the Contacts rows that we will retrieve. - static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID, - ApiApps.PACKAGE_NAME }; - - public Loader 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 = ApiApps.CONTENT_URI; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null, - ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC"); - } - - public void onLoadFinished(Loader 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 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); - } - -} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java index 702d40519..e97f6b392 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.remote_api.RegisteredAppsListActivity; +import org.sufficientlysecure.keychain.openpgp_api.RegisteredAppsListActivity; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockActivity; -- cgit v1.2.3