From 587fb3cca327a3422c3fc7bfd7f16dcf8b33487d Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 13 Jun 2016 13:32:14 +0200 Subject: refactored pgp decryption --- .../conversations/crypto/PgpDecryptionService.java | 305 +++++++++++---------- 1 file changed, 162 insertions(+), 143 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java') diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java index ed67dc65..2bc270c1 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java @@ -1,162 +1,181 @@ package eu.siacs.conversations.crypto; import android.app.PendingIntent; +import android.content.Intent; + +import org.openintents.openpgp.util.OpenPgpApi; + +import java.io.ByteArrayInputStream; +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 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; -import eu.siacs.conversations.ui.UiCallback; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; public class PgpDecryptionService { - private final XmppConnectionService xmppConnectionService; - private final ConcurrentHashMap> messages = new ConcurrentHashMap<>(); - private final ConcurrentHashMap decryptingMessages = new ConcurrentHashMap<>(); - private Boolean keychainLocked = false; - private final Object keychainLockedLock = new Object(); - - public PgpDecryptionService(XmppConnectionService xmppConnectionService) { - this.xmppConnectionService = xmppConnectionService; - } - - public void add(Message message) { - if (isRunning()) { - decryptDirectly(message); - } else { - store(message); - } - } - - public void addAll(List messagesList) { - if (!messagesList.isEmpty()) { - String conversationUuid = messagesList.get(0).getConversation().getUuid(); - if (!messages.containsKey(conversationUuid)) { - List list = Collections.synchronizedList(new LinkedList()); - messages.put(conversationUuid, list); - } - synchronized (messages.get(conversationUuid)) { - messages.get(conversationUuid).addAll(messagesList); - } - decryptAllMessages(); - } - } - - public void onKeychainUnlocked() { - synchronized (keychainLockedLock) { - keychainLocked = false; - } - decryptAllMessages(); - } - - public void onKeychainLocked() { - synchronized (keychainLockedLock) { - keychainLocked = true; - } - xmppConnectionService.updateConversationUi(); - } + private final XmppConnectionService mXmppConnectionService; + private OpenPgpApi openPgpApi = null; - public void onOpenPgpServiceBound() { - decryptAllMessages(); - } - - public boolean isRunning() { - synchronized (keychainLockedLock) { - return !keychainLocked; - } - } + protected final ArrayDeque messages = new ArrayDeque(); + Message currentMessage; + private PendingIntent pendingIntent; - private void store(Message message) { - if (messages.containsKey(message.getConversation().getUuid())) { - messages.get(message.getConversation().getUuid()).add(message); - } else { - List messageList = Collections.synchronizedList(new LinkedList()); - messageList.add(message); - messages.put(message.getConversation().getUuid(), messageList); - } - } - private void decryptAllMessages() { - for (String uuid : messages.keySet()) { - decryptMessages(uuid); - } - } + public PgpDecryptionService(XmppConnectionService service) { + this.mXmppConnectionService = service; + } - private void decryptMessages(final String uuid) { - synchronized (decryptingMessages) { - Boolean decrypting = decryptingMessages.get(uuid); - if ((decrypting != null && !decrypting) || decrypting == null) { - decryptingMessages.put(uuid, true); - decryptMessage(uuid); - } - } + public synchronized void decrypt(final Message message) { + messages.add(message); + continueDecryption(); } - 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() { - - @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); - } + public synchronized void decrypt(final List list) { + for(Message message : list) { + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + messages.add(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(); } } - private void decryptDirectly(final Message message) { - if (message.getEncryption() == Message.ENCRYPTION_PGP && xmppConnectionService.getPgpEngine() != null) { - xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback() { - - @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(); - } - }); - } - } + public synchronized void continueDecryption(boolean resetPending) { + if (resetPending) { + this.pendingIntent = null; + } + continueDecryption(); + } + + public synchronized void continueDecryption() { + if (currentMessage == null) { + decryptNext(); + } + } + + private synchronized OpenPgpApi getOpenPgpApi() { + if (openPgpApi == null) { + this.openPgpApi = mXmppConnectionService.getOpenPgpApi(); + } + return this.openPgpApi; + } + + private void executeApi(Message message) { + 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); + } + } + } + + private void storePendingIntent(PendingIntent pendingIntent) { + this.pendingIntent = pendingIntent; + mXmppConnectionService.updateConversationUi(); + } + + public synchronized boolean hasPendingIntent(Conversation conversation) { + if (pendingIntent == null) { + return false; + } else { + for(Message message : messages) { + if (message.getConversation() == conversation) { + return true; + } + } + return false; + } + } + + public PendingIntent getPendingIntent() { + return pendingIntent; + } + + public boolean isConnected() { + return getOpenPgpApi() != null; + } } -- cgit v1.2.3