|
|
|
@ -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");
|
|
|
|
|
try {
|
|
|
|
|
if (savedInstanceState == null) {
|
|
|
|
|
if (working < 0) {
|
|
|
|
|
Bundle a = getArguments();
|
|
|
|
|
if (a == null) {
|
|
|
|
|
a = new Bundle();
|
|
|
|
|
a.putString("action", "new");
|
|
|
|
|
setArguments(a);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pickRequest = savedInstanceState.getInt("fair:pickRequest");
|
|
|
|
|
pickUri = savedInstanceState.getParcelable("fair:pickUri");
|
|
|
|
|
Bundle args = new Bundle();
|
|
|
|
|
|
|
|
|
|
Bundle args = new Bundle();
|
|
|
|
|
args.putString("action", working < 0 ? "new" : "edit");
|
|
|
|
|
args.putLong("id", working);
|
|
|
|
|
draftLoader.execute(FragmentCompose.this, args, "compose:instance");
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
} 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());
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
if (recipients.size() == 0)
|
|
|
|
|
throw new IllegalArgumentException(getString(R.string.title_to_missing));
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
OpenPgpServiceConnection pgpService = null;
|
|
|
|
|
try {
|
|
|
|
|
pgpService = PgpHelper.getConnection(context, MAX_PGP_BIND_DELAY);
|
|
|
|
|
if (!pgpService.isBound())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
|
|
|
|
|
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds);
|
|
|
|
|
String[] userIds = new String[recipients.size()];
|
|
|
|
|
for (int i = 0; i < recipients.size(); i++) {
|
|
|
|
|
InternetAddress recipient = (InternetAddress) recipients.get(i);
|
|
|
|
|
userIds[i] = recipient.getAddress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
|
Log.w(ex);
|
|
|
|
|
} finally {
|
|
|
|
|
if (pgpService != null && pgpService.isBound())
|
|
|
|
|
pgpService.unbindFromService();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|