Bind to PGP service if needed

pull/194/merge
M66B 4 years ago
parent d25c1a2784
commit 76fa6c5adf

@ -162,7 +162,6 @@ import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode; import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.jsoup.select.NodeFilter; import org.jsoup.select.NodeFilter;
import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
@ -295,7 +294,6 @@ public class FragmentCompose extends FragmentBase {
private int pickRequest; private int pickRequest;
private Uri pickUri; private Uri pickUri;
private OpenPgpServiceConnection pgpService;
private String[] pgpUserIds; private String[] pgpUserIds;
private long[] pgpKeyIds; private long[] pgpKeyIds;
private long pgpSignKeyId; private long pgpSignKeyId;
@ -1346,13 +1344,6 @@ public class FragmentCompose extends FragmentBase {
@Override @Override
public void onDestroyView() { public void onDestroyView() {
adapter = null; adapter = null;
if (pgpService != null && pgpService.isBound()) {
Log.i("PGP unbinding");
pgpService.unbindFromService();
}
pgpService = null;
super.onDestroyView(); super.onDestroyView();
} }
@ -1374,97 +1365,66 @@ public class FragmentCompose extends FragmentBase {
state = State.NONE; state = State.NONE;
Runnable load = new Runnable() { try {
private boolean once = false; if (savedInstanceState == null) {
if (working < 0) {
@Override Bundle a = getArguments();
public void run() { if (a == null) {
if (once) a = new Bundle();
return; a.putString("action", "new");
once = true; setArguments(a);
}
try {
if (savedInstanceState == null) {
if (working < 0) {
Bundle a = getArguments();
if (a == null) {
a = new Bundle();
a.putString("action", "new");
setArguments(a);
}
Bundle args = new Bundle();
args.putString("action", a.getString("action"));
args.putLong("id", a.getLong("id", -1));
args.putLong("account", a.getLong("account", -1));
args.putLong("identity", a.getLong("identity", -1));
args.putLong("reference", a.getLong("reference", -1));
args.putInt("dsn", a.getInt("dsn", -1));
args.putSerializable("ics", a.getSerializable("ics"));
args.putString("status", a.getString("status"));
args.putBoolean("raw", a.getBoolean("raw", false));
args.putLong("answer", a.getLong("answer", -1));
args.putString("to", a.getString("to"));
args.putString("cc", a.getString("cc"));
args.putString("bcc", a.getString("bcc"));
args.putString("inreplyto", a.getString("inreplyto"));
args.putString("subject", a.getString("subject"));
args.putString("body", a.getString("body"));
args.putString("text", a.getString("text"));
args.putString("selected", a.getString("selected"));
if (a.containsKey("attachments")) {
args.putParcelableArrayList("attachments", a.getParcelableArrayList("attachments"));
a.remove("attachments");
setArguments(a);
}
draftLoader.execute(FragmentCompose.this, args, "compose:new");
} else {
Bundle args = new Bundle();
args.putString("action", "edit");
args.putLong("id", working);
draftLoader.execute(FragmentCompose.this, args, "compose:edit");
}
} else {
working = savedInstanceState.getLong("fair:working");
show_images = savedInstanceState.getBoolean("fair:show_images");
photoURI = savedInstanceState.getParcelable("fair:photo");
pickRequest = savedInstanceState.getInt("fair:pickRequest"); Bundle args = new Bundle();
pickUri = savedInstanceState.getParcelable("fair:pickUri");
Bundle args = new Bundle(); args.putString("action", a.getString("action"));
args.putString("action", working < 0 ? "new" : "edit"); args.putLong("id", a.getLong("id", -1));
args.putLong("id", working); args.putLong("account", a.getLong("account", -1));
draftLoader.execute(FragmentCompose.this, args, "compose:instance"); args.putLong("identity", a.getLong("identity", -1));
args.putLong("reference", a.getLong("reference", -1));
args.putInt("dsn", a.getInt("dsn", -1));
args.putSerializable("ics", a.getSerializable("ics"));
args.putString("status", a.getString("status"));
args.putBoolean("raw", a.getBoolean("raw", false));
args.putLong("answer", a.getLong("answer", -1));
args.putString("to", a.getString("to"));
args.putString("cc", a.getString("cc"));
args.putString("bcc", a.getString("bcc"));
args.putString("inreplyto", a.getString("inreplyto"));
args.putString("subject", a.getString("subject"));
args.putString("body", a.getString("body"));
args.putString("text", a.getString("text"));
args.putString("selected", a.getString("selected"));
if (a.containsKey("attachments")) {
args.putParcelableArrayList("attachments", a.getParcelableArrayList("attachments"));
a.remove("attachments");
setArguments(a);
} }
} catch (Throwable ex) {
Log.e(ex); draftLoader.execute(FragmentCompose.this, args, "compose:new");
} else {
Bundle args = new Bundle();
args.putString("action", "edit");
args.putLong("id", working);
draftLoader.execute(FragmentCompose.this, args, "compose:edit");
} }
} } else {
}; working = savedInstanceState.getLong("fair:working");
show_images = savedInstanceState.getBoolean("fair:show_images");
photoURI = savedInstanceState.getParcelable("fair:photo");
final String pkg = Helper.getOpenKeychainPackage(getContext()); pickRequest = savedInstanceState.getInt("fair:pickRequest");
Log.i("PGP binding to " + pkg); pickUri = savedInstanceState.getParcelable("fair:pickUri");
pgpService = new OpenPgpServiceConnection(getContext(), pkg, new OpenPgpServiceConnection.OnBound() {
@Override
public void onBound(IOpenPgpService2 service) {
Log.i("Bound to " + pkg);
load.run();
}
@Override Bundle args = new Bundle();
public void onError(Exception ex) { args.putString("action", working < 0 ? "new" : "edit");
Log.i(ex.getMessage()); args.putLong("id", working);
load.run(); draftLoader.execute(FragmentCompose.this, args, "compose:instance");
} }
}); } catch (Throwable ex) {
pgpService.bindToService(); Log.e(ex);
}
// Fail-safe
getMainHandler().postDelayed(load, MAX_PGP_BIND_DELAY);
} }
@Override @Override
@ -1475,9 +1435,6 @@ public class FragmentCompose extends FragmentBase {
NetworkRequest.Builder builder = new NetworkRequest.Builder(); NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
cm.registerNetworkCallback(builder.build(), networkCallback); cm.registerNetworkCallback(builder.build(), networkCallback);
if (!pgpService.isBound())
pgpService.bindToService();
} }
@Override @Override
@ -2530,67 +2487,55 @@ public class FragmentCompose extends FragmentBase {
} }
}.setExecutor(executor).execute(this, args, "compose:alias"); }.setExecutor(executor).execute(this, args, "compose:alias");
} else { } else {
if (pgpService.isBound()) try {
try { List<Address> recipients = new ArrayList<>();
List<Address> recipients = new ArrayList<>(); if (draft.from != null)
if (draft.from != null) recipients.addAll(Arrays.asList(draft.from));
recipients.addAll(Arrays.asList(draft.from)); if (draft.to != null)
if (draft.to != null) recipients.addAll(Arrays.asList(draft.to));
recipients.addAll(Arrays.asList(draft.to)); if (draft.cc != null)
if (draft.cc != null) recipients.addAll(Arrays.asList(draft.cc));
recipients.addAll(Arrays.asList(draft.cc)); if (draft.bcc != null)
if (draft.bcc != null) recipients.addAll(Arrays.asList(draft.bcc));
recipients.addAll(Arrays.asList(draft.bcc));
if (recipients.size() == 0)
throw new IllegalArgumentException(getString(R.string.title_to_missing));
List<String> emails = new ArrayList<>();
for (int i = 0; i < recipients.size(); i++) {
InternetAddress recipient = (InternetAddress) recipients.get(i);
String email = recipient.getAddress();
if (!emails.contains(email))
emails.add(email);
}
pgpUserIds = emails.toArray(new String[0]);
Intent intent;
if (EntityMessage.PGP_SIGNONLY.equals(draft.ui_encrypt))
intent = new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
else if (EntityMessage.PGP_SIGNENCRYPT.equals(draft.ui_encrypt)) {
intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, pgpUserIds);
} else
throw new IllegalArgumentException("Invalid encrypt=" + draft.ui_encrypt);
Bundle largs = new Bundle(); if (recipients.size() == 0)
largs.putLong("id", working); throw new IllegalArgumentException(getString(R.string.title_to_missing));
largs.putString("session", UUID.randomUUID().toString());
largs.putInt("action", action);
largs.putBundle("extras", extras);
largs.putBoolean("interactive", interactive);
intent.putExtra(BuildConfig.APPLICATION_ID, largs);
onPgp(intent); List<String> emails = new ArrayList<>();
} catch (Throwable ex) { for (int i = 0; i < recipients.size(); i++) {
if (ex instanceof IllegalArgumentException) InternetAddress recipient = (InternetAddress) recipients.get(i);
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG) String email = recipient.getAddress();
.setGestureInsetBottomIgnored(true).show(); if (!emails.contains(email))
else { emails.add(email);
Log.e(ex); }
Log.unexpectedError(getParentFragmentManager(), ex); pgpUserIds = emails.toArray(new String[0]);
}
Intent intent;
if (EntityMessage.PGP_SIGNONLY.equals(draft.ui_encrypt))
intent = new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
else if (EntityMessage.PGP_SIGNENCRYPT.equals(draft.ui_encrypt)) {
intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, pgpUserIds);
} else
throw new IllegalArgumentException("Invalid encrypt=" + draft.ui_encrypt);
Bundle largs = new Bundle();
largs.putLong("id", working);
largs.putString("session", UUID.randomUUID().toString());
largs.putInt("action", action);
largs.putBundle("extras", extras);
largs.putBoolean("interactive", interactive);
intent.putExtra(BuildConfig.APPLICATION_ID, largs);
onPgp(intent);
} catch (Throwable ex) {
if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG)
.setGestureInsetBottomIgnored(true).show();
else {
Log.e(ex);
Log.unexpectedError(getParentFragmentManager(), ex);
} }
else {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_LONG)
.setGestureInsetBottomIgnored(true);
snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.viewFAQ(v.getContext(), 12);
}
});
snackbar.show();
} }
} }
} }
@ -3204,10 +3149,20 @@ public class FragmentCompose extends FragmentBase {
result.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, identity.sign_key); result.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, identity.sign_key);
} else { } else {
// Call OpenPGP // Call OpenPGP
Log.i("Executing " + data.getAction()); OpenPgpServiceConnection pgpService = null;
Log.logExtras(data); try {
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); pgpService = PgpHelper.getConnection(context);
result = api.executeApi(data, new FileInputStream(input), new FileOutputStream(output)); if (!pgpService.isBound())
throw new OperationCanceledException();
Log.i("Executing " + data.getAction());
Log.logExtras(data);
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
result = api.executeApi(data, new FileInputStream(input), new FileOutputStream(output));
} finally {
if (pgpService != null && pgpService.isBound())
pgpService.unbindFromService();
}
} }
// Process result // Process result
@ -3423,6 +3378,17 @@ public class FragmentCompose extends FragmentBase {
Log.i(ex); Log.i(ex);
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG) Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG)
.setGestureInsetBottomIgnored(true).show(); .setGestureInsetBottomIgnored(true).show();
} else if (ex instanceof OperationCanceledException) {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_INDEFINITE)
.setGestureInsetBottomIgnored(true);
snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
@Override
public void onClick(View v) {
snackbar.dismiss();
Helper.viewFAQ(v.getContext(), 12);
}
});
snackbar.show();
} else } else
Log.unexpectedError(getParentFragmentManager(), ex); Log.unexpectedError(getParentFragmentManager(), ex);
} }
@ -6481,30 +6447,38 @@ public class FragmentCompose extends FragmentBase {
} }
private boolean hasPgpKey(Context context, List<Address> recipients) { private boolean hasPgpKey(Context context, List<Address> recipients) {
if (pgpService == null || !pgpService.isBound())
return false;
if (recipients == null || recipients.size() == 0) if (recipients == null || recipients.size() == 0)
return false; return false;
String[] userIds = new String[recipients.size()]; OpenPgpServiceConnection pgpService = null;
for (int i = 0; i < recipients.size(); i++) { try {
InternetAddress recipient = (InternetAddress) recipients.get(i); pgpService = PgpHelper.getConnection(context, MAX_PGP_BIND_DELAY);
userIds[i] = recipient.getAddress(); if (!pgpService.isBound())
} return false;
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS); String[] userIds = new String[recipients.size()];
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds); for (int i = 0; i < recipients.size(); i++) {
InternetAddress recipient = (InternetAddress) recipients.get(i);
userIds[i] = recipient.getAddress();
}
try { Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds);
Intent result = api.executeApi(intent, (InputStream) null, (OutputStream) null);
int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); try {
if (resultCode == OpenPgpApi.RESULT_CODE_SUCCESS) { OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
long[] keyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); Intent result = api.executeApi(intent, (InputStream) null, (OutputStream) null);
return (keyIds.length > 0); int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
if (resultCode == OpenPgpApi.RESULT_CODE_SUCCESS) {
long[] keyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
return (keyIds.length > 0);
}
} catch (Throwable ex) {
Log.w(ex);
} }
} catch (Throwable ex) { } finally {
Log.w(ex); if (pgpService != null && pgpService.isBound())
pgpService.unbindFromService();
} }
return false; return false;

@ -65,6 +65,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Debug; import android.os.Debug;
import android.os.OperationCanceledException;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.print.PrintAttributes; import android.print.PrintAttributes;
@ -301,8 +302,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
private WebView printWebView = null; private WebView printWebView = null;
private OpenPgpServiceConnection pgpService;
private boolean cards; private boolean cards;
private boolean date; private boolean date;
private boolean date_fixed; private boolean date_fixed;
@ -1585,11 +1584,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
*/ */
} }
final String pkg = Helper.getOpenKeychainPackage(getContext());
Log.i("PGP binding to " + pkg);
pgpService = new OpenPgpServiceConnection(getContext(), pkg);
pgpService.bindToService();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
IntentFilter iff = new IntentFilter(SimpleTask.ACTION_TASK_COUNT); IntentFilter iff = new IntentFilter(SimpleTask.ACTION_TASK_COUNT);
lbm.registerReceiver(treceiver, iff); lbm.registerReceiver(treceiver, iff);
@ -1602,12 +1596,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
lbm.unregisterReceiver(treceiver); lbm.unregisterReceiver(treceiver);
if (pgpService != null && pgpService.isBound()) {
Log.i("PGP unbinding");
pgpService.unbindFromService();
}
pgpService = null;
super.onDestroyView(); super.onDestroyView();
} }
@ -4165,9 +4153,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (!pgpService.isBound())
pgpService.bindToService();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
IntentFilter iff = new IntentFilter(); IntentFilter iff = new IntentFilter();
iff.addAction(ACTION_STORE_RAW); iff.addAction(ACTION_STORE_RAW);
@ -6740,22 +6725,10 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
} }
}.execute(this, args, "messages:alias"); }.execute(this, args, "messages:alias");
} else { } else {
if (pgpService.isBound()) { Intent data = new Intent();
Intent data = new Intent(); data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); data.putExtra(BuildConfig.APPLICATION_ID, id);
data.putExtra(BuildConfig.APPLICATION_ID, id); onPgp(data, auto);
onPgp(data, auto);
} else {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_LONG)
.setGestureInsetBottomIgnored(true);
snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.viewFAQ(v.getContext(), 12);
}
});
snackbar.show();
}
} }
} }
@ -7121,8 +7094,22 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
// Decrypt message // Decrypt message
Log.i("Executing " + data.getAction()); Log.i("Executing " + data.getAction());
Log.logExtras(data); Log.logExtras(data);
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
result = api.executeApi(data, in, out); // Call OpenPGP
OpenPgpServiceConnection pgpService = null;
try {
pgpService = PgpHelper.getConnection(context);
if (!pgpService.isBound())
throw new OperationCanceledException();
Log.i("Executing " + data.getAction());
Log.logExtras(data);
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
result = api.executeApi(data, in, out);
} finally {
if (pgpService != null && pgpService.isBound())
pgpService.unbindFromService();
}
int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
Log.i("Result action=" + data.getAction() + " code=" + resultCode); Log.i("Result action=" + data.getAction() + " code=" + resultCode);
@ -7322,6 +7309,17 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
Log.i(ex); Log.i(ex);
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG) Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG)
.setGestureInsetBottomIgnored(true).show(); .setGestureInsetBottomIgnored(true).show();
} else if (ex instanceof OperationCanceledException) {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_INDEFINITE)
.setGestureInsetBottomIgnored(true);
snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
@Override
public void onClick(View v) {
snackbar.dismiss();
Helper.viewFAQ(v.getContext(), 12);
}
});
snackbar.show();
} else } else
Log.unexpectedError(getParentFragmentManager(), ex); Log.unexpectedError(getParentFragmentManager(), ex);
} }

@ -0,0 +1,70 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail 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.
FairEmail 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 FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2021 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class PgpHelper {
private static final long CONNECT_TIMEOUT = 5000L;
static OpenPgpServiceConnection getConnection(Context context) {
return getConnection(context, CONNECT_TIMEOUT);
}
static OpenPgpServiceConnection getConnection(Context context, long timeout) {
final String pkg = Helper.getOpenKeychainPackage(context);
Log.i("PGP binding to " + pkg + " timeout=" + timeout);
CountDownLatch latch = new CountDownLatch(1);
OpenPgpServiceConnection pgpService = new OpenPgpServiceConnection(context, pkg,
new OpenPgpServiceConnection.OnBound() {
@Override
public void onBound(IOpenPgpService2 service) {
Log.i("Bound to " + pkg);
latch.countDown();
}
@Override
public void onError(Exception ex) {
Log.i(ex.getMessage());
latch.countDown();
}
});
pgpService.bindToService();
try {
latch.await(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
Log.w(ex);
}
Log.i("PGP bound=" + pgpService.isBound());
return pgpService;
}
}
Loading…
Cancel
Save