1
0
Fork 0
mirror of https://github.com/M66B/FairEmail.git synced 2025-02-23 14:41:08 +00:00

Bind to PGP service if needed

This commit is contained in:
M66B 2021-11-26 15:33:19 +01:00
parent d25c1a2784
commit 76fa6c5adf
3 changed files with 255 additions and 213 deletions

View file

@ -162,7 +162,6 @@ import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeFilter;
import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
@ -295,7 +294,6 @@ public class FragmentCompose extends FragmentBase {
private int pickRequest;
private Uri pickUri;
private OpenPgpServiceConnection pgpService;
private String[] pgpUserIds;
private long[] pgpKeyIds;
private long pgpSignKeyId;
@ -1346,13 +1344,6 @@ public class FragmentCompose extends FragmentBase {
@Override
public void onDestroyView() {
adapter = null;
if (pgpService != null && pgpService.isBound()) {
Log.i("PGP unbinding");
pgpService.unbindFromService();
}
pgpService = null;
super.onDestroyView();
}
@ -1374,97 +1365,66 @@ public class FragmentCompose extends FragmentBase {
state = State.NONE;
Runnable load = new Runnable() {
private boolean once = false;
@Override
public void run() {
if (once)
return;
once = true;
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");
pickUri = savedInstanceState.getParcelable("fair:pickUri");
Bundle args = new Bundle();
args.putString("action", working < 0 ? "new" : "edit");
args.putLong("id", working);
draftLoader.execute(FragmentCompose.this, args, "compose:instance");
try {
if (savedInstanceState == null) {
if (working < 0) {
Bundle a = getArguments();
if (a == null) {
a = new Bundle();
a.putString("action", "new");
setArguments(a);
}
} catch (Throwable ex) {
Log.e(ex);
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");
final String pkg = Helper.getOpenKeychainPackage(getContext());
Log.i("PGP binding to " + pkg);
pgpService = new OpenPgpServiceConnection(getContext(), pkg, new OpenPgpServiceConnection.OnBound() {
@Override
public void onBound(IOpenPgpService2 service) {
Log.i("Bound to " + pkg);
load.run();
}
pickRequest = savedInstanceState.getInt("fair:pickRequest");
pickUri = savedInstanceState.getParcelable("fair:pickUri");
@Override
public void onError(Exception ex) {
Log.i(ex.getMessage());
load.run();
Bundle args = new Bundle();
args.putString("action", working < 0 ? "new" : "edit");
args.putLong("id", working);
draftLoader.execute(FragmentCompose.this, args, "compose:instance");
}
});
pgpService.bindToService();
// Fail-safe
getMainHandler().postDelayed(load, MAX_PGP_BIND_DELAY);
} catch (Throwable ex) {
Log.e(ex);
}
}
@Override
@ -1475,9 +1435,6 @@ public class FragmentCompose extends FragmentBase {
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
cm.registerNetworkCallback(builder.build(), networkCallback);
if (!pgpService.isBound())
pgpService.bindToService();
}
@Override
@ -2530,67 +2487,55 @@ public class FragmentCompose extends FragmentBase {
}
}.setExecutor(executor).execute(this, args, "compose:alias");
} else {
if (pgpService.isBound())
try {
List<Address> recipients = new ArrayList<>();
if (draft.from != null)
recipients.addAll(Arrays.asList(draft.from));
if (draft.to != null)
recipients.addAll(Arrays.asList(draft.to));
if (draft.cc != null)
recipients.addAll(Arrays.asList(draft.cc));
if (draft.bcc != null)
recipients.addAll(Arrays.asList(draft.bcc));
try {
List<Address> recipients = new ArrayList<>();
if (draft.from != null)
recipients.addAll(Arrays.asList(draft.from));
if (draft.to != null)
recipients.addAll(Arrays.asList(draft.to));
if (draft.cc != null)
recipients.addAll(Arrays.asList(draft.cc));
if (draft.bcc != null)
recipients.addAll(Arrays.asList(draft.bcc));
if (recipients.size() == 0)
throw new IllegalArgumentException(getString(R.string.title_to_missing));
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();
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);
}
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();
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);
} else {
// Call OpenPGP
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));
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, new FileInputStream(input), new FileOutputStream(output));
} finally {
if (pgpService != null && pgpService.isBound())
pgpService.unbindFromService();
}
}
// Process result
@ -3423,6 +3378,17 @@ public class FragmentCompose extends FragmentBase {
Log.i(ex);
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG)
.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
Log.unexpectedError(getParentFragmentManager(), ex);
}
@ -6481,30 +6447,38 @@ public class FragmentCompose extends FragmentBase {
}
private boolean hasPgpKey(Context context, List<Address> recipients) {
if (pgpService == null || !pgpService.isBound())
return false;
if (recipients == null || recipients.size() == 0)
return false;
String[] userIds = new String[recipients.size()];
for (int i = 0; i < recipients.size(); i++) {
InternetAddress recipient = (InternetAddress) recipients.get(i);
userIds[i] = recipient.getAddress();
}
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds);
OpenPgpServiceConnection pgpService = null;
try {
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
Intent result = api.executeApi(intent, (InputStream) null, (OutputStream) null);
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);
pgpService = PgpHelper.getConnection(context, MAX_PGP_BIND_DELAY);
if (!pgpService.isBound())
return false;
String[] userIds = new String[recipients.size()];
for (int i = 0; i < recipients.size(); i++) {
InternetAddress recipient = (InternetAddress) recipients.get(i);
userIds[i] = recipient.getAddress();
}
} catch (Throwable ex) {
Log.w(ex);
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds);
try {
OpenPgpApi api = new OpenPgpApi(context, pgpService.getService());
Intent result = api.executeApi(intent, (InputStream) null, (OutputStream) null);
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);
}
} finally {
if (pgpService != null && pgpService.isBound())
pgpService.unbindFromService();
}
return false;

View file

@ -65,6 +65,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.OperationCanceledException;
import android.os.Parcel;
import android.os.Parcelable;
import android.print.PrintAttributes;
@ -301,8 +302,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
private WebView printWebView = null;
private OpenPgpServiceConnection pgpService;
private boolean cards;
private boolean date;
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());
IntentFilter iff = new IntentFilter(SimpleTask.ACTION_TASK_COUNT);
lbm.registerReceiver(treceiver, iff);
@ -1602,12 +1596,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
lbm.unregisterReceiver(treceiver);
if (pgpService != null && pgpService.isBound()) {
Log.i("PGP unbinding");
pgpService.unbindFromService();
}
pgpService = null;
super.onDestroyView();
}
@ -4165,9 +4153,6 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
public void onResume() {
super.onResume();
if (!pgpService.isBound())
pgpService.bindToService();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
IntentFilter iff = new IntentFilter();
iff.addAction(ACTION_STORE_RAW);
@ -6740,22 +6725,10 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
}
}.execute(this, args, "messages:alias");
} else {
if (pgpService.isBound()) {
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
data.putExtra(BuildConfig.APPLICATION_ID, id);
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();
}
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
data.putExtra(BuildConfig.APPLICATION_ID, id);
onPgp(data, auto);
}
}
@ -7121,8 +7094,22 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
// Decrypt message
Log.i("Executing " + data.getAction());
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);
Log.i("Result action=" + data.getAction() + " code=" + resultCode);
@ -7322,6 +7309,17 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
Log.i(ex);
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG)
.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
Log.unexpectedError(getParentFragmentManager(), ex);
}

View file

@ -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;
}
}