refactored pgp decryption

This commit is contained in:
Daniel Gultsch 2016-06-13 13:32:14 +02:00
parent 490a1ca3cf
commit 587fb3cca3
11 changed files with 207 additions and 336 deletions

View File

@ -1,162 +1,181 @@
package eu.siacs.conversations.crypto; package eu.siacs.conversations.crypto;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Intent;
import eu.siacs.conversations.entities.Message; import org.openintents.openpgp.util.OpenPgpApi;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
import java.util.Collections; import java.io.ByteArrayInputStream;
import java.util.LinkedList; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
public class PgpDecryptionService { public class PgpDecryptionService {
private final XmppConnectionService xmppConnectionService; private final XmppConnectionService mXmppConnectionService;
private final ConcurrentHashMap<String, List<Message>> messages = new ConcurrentHashMap<>(); private OpenPgpApi openPgpApi = null;
private final ConcurrentHashMap<String, Boolean> decryptingMessages = new ConcurrentHashMap<>();
private Boolean keychainLocked = false;
private final Object keychainLockedLock = new Object();
public PgpDecryptionService(XmppConnectionService xmppConnectionService) { protected final ArrayDeque<Message> messages = new ArrayDeque();
this.xmppConnectionService = xmppConnectionService; Message currentMessage;
private PendingIntent pendingIntent;
public PgpDecryptionService(XmppConnectionService service) {
this.mXmppConnectionService = service;
}
public synchronized void decrypt(final Message message) {
messages.add(message);
continueDecryption();
} }
public void add(Message message) { public synchronized void decrypt(final List<Message> list) {
if (isRunning()) { for(Message message : list) {
decryptDirectly(message); if (message.getEncryption() == Message.ENCRYPTION_PGP) {
} else { messages.add(message);
store(message); }
}
continueDecryption();
}
protected synchronized void decryptNext() {
if (pendingIntent == null
&& getOpenPgpApi() != null
&& (currentMessage = messages.poll()) != null) {
new Thread(new Runnable() {
@Override
public void run() {
executeApi(currentMessage);
decryptNext();
}
}).start();
} }
} }
public void addAll(List<Message> messagesList) { public synchronized void continueDecryption(boolean resetPending) {
if (!messagesList.isEmpty()) { if (resetPending) {
String conversationUuid = messagesList.get(0).getConversation().getUuid(); this.pendingIntent = null;
if (!messages.containsKey(conversationUuid)) { }
List<Message> list = Collections.synchronizedList(new LinkedList<Message>()); continueDecryption();
messages.put(conversationUuid, list); }
}
synchronized (messages.get(conversationUuid)) {
messages.get(conversationUuid).addAll(messagesList);
}
decryptAllMessages();
}
}
public void onKeychainUnlocked() { public synchronized void continueDecryption() {
synchronized (keychainLockedLock) { if (currentMessage == null) {
keychainLocked = false; decryptNext();
} }
decryptAllMessages(); }
}
public void onKeychainLocked() { private synchronized OpenPgpApi getOpenPgpApi() {
synchronized (keychainLockedLock) { if (openPgpApi == null) {
keychainLocked = true; this.openPgpApi = mXmppConnectionService.getOpenPgpApi();
} }
xmppConnectionService.updateConversationUi(); return this.openPgpApi;
} }
public void onOpenPgpServiceBound() { private void executeApi(Message message) {
decryptAllMessages(); Intent params = new Intent();
} params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
if (message.getType() == Message.TYPE_TEXT) {
InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
final OutputStream os = new ByteArrayOutputStream();
Intent result = getOpenPgpApi().executeApi(params, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
message.setBody(os.toString());
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
if (message.trusted()
&& message.treatAsDownloadable() != Message.Decision.NEVER
&& manager.getAutoAcceptFileSize() > 0) {
manager.createNewDownloadConnection(message);
}
} catch (IOException e) {
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
}
mXmppConnectionService.updateMessage(message);
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
messages.addFirst(message);
currentMessage = null;
storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
break;
case OpenPgpApi.RESULT_CODE_ERROR:
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
mXmppConnectionService.updateMessage(message);
break;
}
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
try {
final DownloadableFile inputFile = mXmppConnectionService.getFileBackend().getFile(message, false);
final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
Intent result = getOpenPgpApi().executeApi(params, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
URL url = message.getFileParams().url;
mXmppConnectionService.getFileBackend().updateFileParams(message,url);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
inputFile.delete();
mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile);
mXmppConnectionService.updateMessage(message);
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
messages.addFirst(message);
currentMessage = null;
storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
break;
case OpenPgpApi.RESULT_CODE_ERROR:
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
mXmppConnectionService.updateMessage(message);
break;
}
} catch (final IOException e) {
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
mXmppConnectionService.updateMessage(message);
}
}
}
public boolean isRunning() { private void storePendingIntent(PendingIntent pendingIntent) {
synchronized (keychainLockedLock) { this.pendingIntent = pendingIntent;
return !keychainLocked; mXmppConnectionService.updateConversationUi();
} }
}
private void store(Message message) { public synchronized boolean hasPendingIntent(Conversation conversation) {
if (messages.containsKey(message.getConversation().getUuid())) { if (pendingIntent == null) {
messages.get(message.getConversation().getUuid()).add(message); return false;
} else { } else {
List<Message> messageList = Collections.synchronizedList(new LinkedList<Message>()); for(Message message : messages) {
messageList.add(message); if (message.getConversation() == conversation) {
messages.put(message.getConversation().getUuid(), messageList); return true;
} }
} }
return false;
}
}
private void decryptAllMessages() { public PendingIntent getPendingIntent() {
for (String uuid : messages.keySet()) { return pendingIntent;
decryptMessages(uuid); }
}
}
private void decryptMessages(final String uuid) { public boolean isConnected() {
synchronized (decryptingMessages) { return getOpenPgpApi() != null;
Boolean decrypting = decryptingMessages.get(uuid); }
if ((decrypting != null && !decrypting) || decrypting == null) {
decryptingMessages.put(uuid, true);
decryptMessage(uuid);
}
}
}
private void decryptMessage(final String uuid) {
Message message = null;
synchronized (messages.get(uuid)) {
while (!messages.get(uuid).isEmpty()) {
if (messages.get(uuid).get(0).getEncryption() == Message.ENCRYPTION_PGP) {
if (isRunning()) {
message = messages.get(uuid).remove(0);
}
break;
} else {
messages.get(uuid).remove(0);
}
}
if (message != null && xmppConnectionService.getPgpEngine() != null) {
xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback<Message>() {
@Override
public void userInputRequried(PendingIntent pi, Message message) {
messages.get(uuid).add(0, message);
decryptingMessages.put(uuid, false);
}
@Override
public void success(Message message) {
xmppConnectionService.updateConversationUi();
decryptMessage(uuid);
}
@Override
public void error(int error, Message message) {
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
xmppConnectionService.updateConversationUi();
decryptMessage(uuid);
}
});
} else {
decryptingMessages.put(uuid, false);
}
}
}
private void decryptDirectly(final Message message) {
if (message.getEncryption() == Message.ENCRYPTION_PGP && xmppConnectionService.getPgpEngine() != null) {
xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback<Message>() {
@Override
public void userInputRequried(PendingIntent pi, Message message) {
store(message);
}
@Override
public void success(Message message) {
xmppConnectionService.updateConversationUi();
xmppConnectionService.getNotificationService().updateNotification(false);
}
@Override
public void error(int error, Message message) {
message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
xmppConnectionService.updateConversationUi();
}
});
}
}
} }

View File

@ -38,96 +38,6 @@ public class PgpEngine {
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
} }
public void decrypt(final Message message, final UiCallback<Message> callback) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
final String uuid = message.getUuid();
if (message.getType() == Message.TYPE_TEXT) {
InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
notifyPgpDecryptionService(message.getConversation().getAccount(), OpenPgpApi.ACTION_DECRYPT_VERIFY, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
if (message.getEncryption() == Message.ENCRYPTION_PGP
&& message.getUuid().equals(uuid)) {
message.setBody(os.toString());
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
if (message.trusted()
&& message.treatAsDownloadable() != Message.Decision.NEVER
&& manager.getAutoAcceptFileSize() > 0) {
manager.createNewDownloadConnection(message);
}
mXmppConnectionService.updateMessage(message);
callback.success(message);
}
} catch (IOException e) {
callback.error(R.string.openpgp_error, message);
return;
}
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, message);
}
}
});
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
try {
final DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, false);
final DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, true);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
notifyPgpDecryptionService(message.getConversation().getAccount(), OpenPgpApi.ACTION_DECRYPT_VERIFY, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
URL url = message.getFileParams().url;
mXmppConnectionService.getFileBackend().updateFileParams(message,url);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
inputFile.delete();
mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile);
callback.success(message);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried(
(PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, message);
}
}
});
} catch (final IOException e) {
callback.error(R.string.error_decrypting_file, message);
}
}
}
public void encrypt(final Message message, final UiCallback<Message> callback) { public void encrypt(final Message message, final UiCallback<Message> callback) {
Intent params = new Intent(); Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_ENCRYPT); params.setAction(OpenPgpApi.ACTION_ENCRYPT);
@ -156,7 +66,6 @@ public class PgpEngine {
@Override @Override
public void onReturn(Intent result) { public void onReturn(Intent result) {
notifyPgpDecryptionService(message.getConversation().getAccount(), OpenPgpApi.ACTION_ENCRYPT, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) { OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: case OpenPgpApi.RESULT_CODE_SUCCESS:
@ -202,7 +111,6 @@ public class PgpEngine {
@Override @Override
public void onReturn(Intent result) { public void onReturn(Intent result) {
notifyPgpDecryptionService(message.getConversation().getAccount(), OpenPgpApi.ACTION_ENCRYPT, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) { OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: case OpenPgpApi.RESULT_CODE_SUCCESS:
@ -257,7 +165,6 @@ public class PgpEngine {
InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes()); InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
Intent result = api.executeApi(params, is, os); Intent result = api.executeApi(params, is, os);
notifyPgpDecryptionService(account, OpenPgpApi.ACTION_DECRYPT_VERIFY, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) { OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: case OpenPgpApi.RESULT_CODE_SUCCESS:
@ -315,7 +222,6 @@ public class PgpEngine {
@Override @Override
public void onReturn(Intent result) { public void onReturn(Intent result) {
notifyPgpDecryptionService(account, OpenPgpApi.ACTION_SIGN, result);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: case OpenPgpApi.RESULT_CODE_SUCCESS:
StringBuilder signatureBuilder = new StringBuilder(); StringBuilder signatureBuilder = new StringBuilder();
@ -397,17 +303,4 @@ public class PgpEngine {
return (PendingIntent) result return (PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT); .getParcelableExtra(OpenPgpApi.RESULT_INTENT);
} }
private void notifyPgpDecryptionService(Account account, String action, final Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
account.getPgpDecryptionService().onKeychainUnlocked();
}
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
account.getPgpDecryptionService().onKeychainLocked();
break;
}
}
} }

View File

@ -6,6 +6,7 @@ import android.os.SystemClock;
import android.util.Pair; import android.util.Pair;
import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.crypto.PgpDecryptionService;
import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException; import net.java.otr4j.crypto.OtrCryptoException;
@ -20,7 +21,6 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrService; import eu.siacs.conversations.crypto.OtrService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@ -83,6 +83,14 @@ public class Account extends AbstractEntity {
return getRoster().getContact(jid); return getRoster().getContact(jid);
} }
public boolean hasPendingPgpIntent(Conversation conversation) {
return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
}
public boolean isPgpDecryptionServiceConnected() {
return pgpDecryptionService != null && pgpDecryptionService.isConnected();
}
public enum State { public enum State {
DISABLED, DISABLED,
OFFLINE, OFFLINE,
@ -398,10 +406,10 @@ public class Account extends AbstractEntity {
public void initAccountServices(final XmppConnectionService context) { public void initAccountServices(final XmppConnectionService context) {
this.mOtrService = new OtrService(context, this); this.mOtrService = new OtrService(context, this);
this.axolotlService = new AxolotlService(this, context); this.axolotlService = new AxolotlService(this, context);
this.pgpDecryptionService = new PgpDecryptionService(context);
if (xmppConnection != null) { if (xmppConnection != null) {
xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
} }
this.pgpDecryptionService = new PgpDecryptionService(context);
} }
public OtrService getOtrService() { public OtrService getOtrService() {
@ -409,7 +417,7 @@ public class Account extends AbstractEntity {
} }
public PgpDecryptionService getPgpDecryptionService() { public PgpDecryptionService getPgpDecryptionService() {
return pgpDecryptionService; return this.pgpDecryptionService;
} }
public XmppConnection getXmppConnection() { public XmppConnection getXmppConnection() {

View File

@ -932,7 +932,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
synchronized (this.messages) { synchronized (this.messages) {
this.messages.addAll(index, messages); this.messages.addAll(index, messages);
} }
account.getPgpDecryptionService().addAll(messages); account.getPgpDecryptionService().decrypt(messages);
} }
public void sort() { public void sort() {

View File

@ -127,7 +127,7 @@ public class HttpDownloadConnection implements Transferable {
message.setTransferable(null); message.setTransferable(null);
mHttpConnectionManager.finishConnection(this); mHttpConnectionManager.finishConnection(this);
if (message.getEncryption() == Message.ENCRYPTION_PGP) { if (message.getEncryption() == Message.ENCRYPTION_PGP) {
message.getConversation().getAccount().getPgpDecryptionService().add(message); message.getConversation().getAccount().getPgpDecryptionService().decrypt(message);
} }
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
if (acceptedAutomatically) { if (acceptedAutomatically) {

View File

@ -7,7 +7,6 @@ import android.util.Pair;
import net.java.otr4j.session.Session; import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -32,7 +31,6 @@ import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.chatstate.ChatState;
@ -484,7 +482,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
sendMessageReceipts(account, packet); sendMessageReceipts(account, packet);
} }
if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) { if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
conversation.getAccount().getPgpDecryptionService().add(replacedMessage); conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage);
} }
return; return;
} else { } else {
@ -508,7 +506,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
if (message.getEncryption() == Message.ENCRYPTION_PGP) { if (message.getEncryption() == Message.ENCRYPTION_PGP) {
conversation.getAccount().getPgpDecryptionService().add(message); conversation.getAccount().getPgpDecryptionService().decrypt(message);
} }
if (query == null || query.getWith() == null) { //either no mam or catchup if (query == null || query.getWith() == null) { //either no mam or catchup

View File

@ -49,7 +49,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
@ -62,6 +61,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import de.duenndns.ssl.MemorizingTrustManager; import de.duenndns.ssl.MemorizingTrustManager;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
@ -383,6 +383,16 @@ public class XmppConnectionService extends Service {
} }
public OpenPgpApi getOpenPgpApi() {
if (!Config.supportOpenPgp()) {
return null;
} else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
return new OpenPgpApi(this, pgpServiceConnection.getService());
} else {
return null;
}
}
public FileBackend getFileBackend() { public FileBackend getFileBackend() {
return this.fileBackend; return this.fileBackend;
} }
@ -754,8 +764,9 @@ public class XmppConnectionService extends Service {
@Override @Override
public void onBound(IOpenPgpService2 service) { public void onBound(IOpenPgpService2 service) {
for (Account account : accounts) { for (Account account : accounts) {
if (account.getPgpDecryptionService() != null) { final PgpDecryptionService pgp = account.getPgpDecryptionService();
account.getPgpDecryptionService().onOpenPgpServiceBound(); if(pgp != null) {
pgp.continueDecryption(true);
} }
} }
} }

View File

@ -9,12 +9,9 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.content.res.TypedArray;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.Nullable;
import android.text.InputType; import android.text.InputType;
import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity; import android.view.Gravity;
@ -214,49 +211,25 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} }
return -1; return -1;
} }
private final int KEYCHAIN_UNLOCK_NOT_REQUIRED = 0;
private final int KEYCHAIN_UNLOCK_REQUIRED = 1;
private final int KEYCHAIN_UNLOCK_PENDING = 2;
private int keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
protected OnClickListener clickToDecryptListener = new OnClickListener() { protected OnClickListener clickToDecryptListener = new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (keychainUnlock == KEYCHAIN_UNLOCK_REQUIRED PendingIntent pendingIntent = conversation.getAccount().getPgpDecryptionService().getPendingIntent();
&& activity.hasPgp() && !conversation.getAccount().getPgpDecryptionService().isRunning()) { if (pendingIntent != null) {
keychainUnlock = KEYCHAIN_UNLOCK_PENDING; try {
updateSnackBar(conversation); activity.startIntentSenderForResult(pendingIntent.getIntentSender(),
Message message = getLastPgpDecryptableMessage(); ConversationActivity.REQUEST_DECRYPT_PGP,
if (message != null) { null,
activity.xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback<Message>() { 0,
@Override 0,
public void success(Message object) { 0);
conversation.getAccount().getPgpDecryptionService().onKeychainUnlocked(); } catch (SendIntentException e) {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED; Toast.makeText(activity,R.string.unable_to_connect_to_keychain, Toast.LENGTH_SHORT).show();
} conversation.getAccount().getPgpDecryptionService().continueDecryption(true);
@Override
public void error(int errorCode, Message object) {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
}
@Override
public void userInputRequried(PendingIntent pi, Message object) {
try {
activity.startIntentSenderForResult(pi.getIntentSender(),
ConversationActivity.REQUEST_DECRYPT_PGP, null, 0, 0, 0);
} catch (SendIntentException e) {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
updatePgpMessages();
}
}
});
} }
} else {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
updatePgpMessages();
} }
updateSnackBar(conversation);
} }
}; };
protected OnClickListener clickToVerify = new OnClickListener() { protected OnClickListener clickToVerify = new OnClickListener() {
@ -722,7 +695,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
message.setEncryption(Message.ENCRYPTION_PGP); message.setEncryption(Message.ENCRYPTION_PGP);
activity.updateConversationList(); activity.updateConversationList();
updateMessages(); updateMessages();
conversation.getAccount().getPgpDecryptionService().add(message); conversation.getAccount().getPgpDecryptionService().decrypt(message);
} }
protected void privateMessageWith(final Jid counterpart) { protected void privateMessageWith(final Jid counterpart) {
@ -789,7 +762,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.conversation.trim(); this.conversation.trim();
} }
this.keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
this.conversation = conversation; this.conversation = conversation;
boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating(); boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
this.mEditMessage.setEnabled(canWrite); this.mEditMessage.setEnabled(canWrite);
@ -909,7 +881,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
default: default:
break; break;
} }
} else if (keychainUnlock == KEYCHAIN_UNLOCK_REQUIRED) { } else if (account.hasPendingPgpIntent(conversation)) {
showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener); showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
} else if (mode == Conversation.MODE_SINGLE } else if (mode == Conversation.MODE_SINGLE
&& conversation.smpRequested()) { && conversation.smpRequested()) {
@ -932,7 +904,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final ConversationActivity activity = (ConversationActivity) getActivity(); final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) { if (this.conversation != null) {
conversation.populateWithMessages(ConversationFragment.this.messageList); conversation.populateWithMessages(ConversationFragment.this.messageList);
updatePgpMessages();
updateSnackBar(conversation); updateSnackBar(conversation);
updateStatusMessages(); updateStatusMessages();
this.messageListAdapter.notifyDataSetChanged(); this.messageListAdapter.notifyDataSetChanged();
@ -945,29 +916,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} }
} }
public void updatePgpMessages() {
if (keychainUnlock != KEYCHAIN_UNLOCK_PENDING) {
if (getLastPgpDecryptableMessage() != null
&& !conversation.getAccount().getPgpDecryptionService().isRunning()) {
keychainUnlock = KEYCHAIN_UNLOCK_REQUIRED;
} else {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
}
}
}
@Nullable
private Message getLastPgpDecryptableMessage() {
for (final Message message : this.messageList) {
if (message.getEncryption() == Message.ENCRYPTION_PGP
&& (message.getStatus() == Message.STATUS_RECEIVED || message.getStatus() >= Message.STATUS_SEND)
&& message.getTransferable() == null) {
return message;
}
}
return null;
}
private void messageSent() { private void messageSent() {
mEditMessage.setText(""); mEditMessage.setText("");
updateChatMsgHint(); updateChatMsgHint();
@ -1424,9 +1372,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final Intent data) { final Intent data) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) { if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
activity.getSelectedConversation().getAccount().getPgpDecryptionService().onKeychainUnlocked(); activity.getSelectedConversation().getAccount().getPgpDecryptionService().continueDecryption(true);
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
updatePgpMessages();
} else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) { } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) {
final String body = mEditMessage.getText().toString(); final String body = mEditMessage.getText().toString();
Message message = new Message(conversation, body, conversation.getNextEncryption()); Message message = new Message(conversation, body, conversation.getNextEncryption());
@ -1435,11 +1381,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID); int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID);
activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption()); activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption());
} }
} else {
if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
updatePgpMessages();
}
} }
} }

View File

@ -631,8 +631,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
displayOpenableMessage(viewHolder, message); displayOpenableMessage(viewHolder, message);
} }
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) { } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
if (activity.hasPgp()) { if (account.isPgpDecryptionServiceConnected()) {
if (account.getPgpDecryptionService().isRunning()) { if (!account.hasPendingPgpIntent(conversation)) {
displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
} else { } else {
displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);

View File

@ -110,7 +110,7 @@ public class JingleConnection implements Transferable {
if (message.getEncryption() != Message.ENCRYPTION_PGP) { if (message.getEncryption() != Message.ENCRYPTION_PGP) {
mXmppConnectionService.getFileBackend().updateMediaScanner(file); mXmppConnectionService.getFileBackend().updateMediaScanner(file);
} else { } else {
account.getPgpDecryptionService().add(message); account.getPgpDecryptionService().decrypt(message);
} }
} }

View File

@ -662,4 +662,5 @@
<string name="pref_theme_dark">Dark theme</string> <string name="pref_theme_dark">Dark theme</string>
<string name="pref_use_green_background">Green Background</string> <string name="pref_use_green_background">Green Background</string>
<string name="pref_use_green_background_summary">Use green background for received messages</string> <string name="pref_use_green_background_summary">Use green background for received messages</string>
<string name="unable_to_connect_to_keychain">Unable to connect to OpenKeychain</string>
</resources> </resources>