PgpDecryptionService.java

  1package eu.siacs.conversations.crypto;
  2
  3import android.app.PendingIntent;
  4import android.content.Intent;
  5
  6import org.openintents.openpgp.util.OpenPgpApi;
  7
  8import java.io.ByteArrayInputStream;
  9import java.io.ByteArrayOutputStream;
 10import java.io.FileInputStream;
 11import java.io.FileOutputStream;
 12import java.io.IOException;
 13import java.io.InputStream;
 14import java.io.OutputStream;
 15import java.net.URL;
 16import java.util.ArrayDeque;
 17import java.util.HashSet;
 18import java.util.List;
 19
 20import eu.siacs.conversations.entities.Conversation;
 21import eu.siacs.conversations.entities.DownloadableFile;
 22import eu.siacs.conversations.entities.Message;
 23import eu.siacs.conversations.http.HttpConnectionManager;
 24import eu.siacs.conversations.services.XmppConnectionService;
 25
 26public class PgpDecryptionService {
 27
 28    private final XmppConnectionService mXmppConnectionService;
 29    private OpenPgpApi openPgpApi = null;
 30
 31	protected final ArrayDeque<Message> messages = new ArrayDeque();
 32    protected final HashSet<Message> pendingNotifications = new HashSet<>();
 33	Message currentMessage;
 34    private PendingIntent pendingIntent;
 35
 36
 37    public PgpDecryptionService(XmppConnectionService service) {
 38        this.mXmppConnectionService = service;
 39    }
 40
 41	public synchronized boolean decrypt(final Message message, boolean notify) {
 42        messages.add(message);
 43        if (notify && pendingIntent == null) {
 44            pendingNotifications.add(message);
 45            continueDecryption();
 46            return false;
 47        } else {
 48            continueDecryption();
 49            return notify;
 50        }
 51	}
 52
 53    public synchronized void decrypt(final List<Message> list) {
 54        for(Message message : list) {
 55            if (message.getEncryption() == Message.ENCRYPTION_PGP) {
 56                messages.add(message);
 57            }
 58        }
 59        continueDecryption();
 60    }
 61
 62    public synchronized void discard(List<Message> discards) {
 63        this.messages.removeAll(discards);
 64        this.pendingNotifications.removeAll(discards);
 65    }
 66
 67	protected synchronized void decryptNext() {
 68		if (pendingIntent == null
 69                && getOpenPgpApi() != null
 70                && (currentMessage =  messages.poll()) != null) {
 71			new Thread(new Runnable() {
 72                @Override
 73                public void run() {
 74                    executeApi(currentMessage);
 75                    decryptNext();
 76                }
 77            }).start();
 78		}
 79	}
 80
 81    public synchronized void continueDecryption(boolean resetPending) {
 82        if (resetPending) {
 83            this.pendingIntent = null;
 84        }
 85        continueDecryption();
 86    }
 87
 88    public synchronized void continueDecryption() {
 89        if (currentMessage == null) {
 90            decryptNext();
 91        }
 92    }
 93
 94    private synchronized OpenPgpApi getOpenPgpApi() {
 95        if (openPgpApi == null) {
 96            this.openPgpApi = mXmppConnectionService.getOpenPgpApi();
 97        }
 98        return this.openPgpApi;
 99    }
100
101    private void executeApi(Message message) {
102        Intent params = new Intent();
103        params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
104        if (message.getType() == Message.TYPE_TEXT) {
105            InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
106            final OutputStream os = new ByteArrayOutputStream();
107            Intent result = getOpenPgpApi().executeApi(params, is, os);
108            switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
109                case OpenPgpApi.RESULT_CODE_SUCCESS:
110                    try {
111                        os.flush();
112                        message.setBody(os.toString());
113                        message.setEncryption(Message.ENCRYPTION_DECRYPTED);
114                        final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
115                        if (message.trusted()
116                                && message.treatAsDownloadable() != Message.Decision.NEVER
117                                && manager.getAutoAcceptFileSize() > 0) {
118                            manager.createNewDownloadConnection(message);
119                        }
120                    } catch (IOException e) {
121                        message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
122                    }
123                    mXmppConnectionService.updateMessage(message);
124                    break;
125                case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
126                    synchronized (PgpDecryptionService.this) {
127                        messages.addFirst(message);
128                        currentMessage = null;
129                        storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
130                    }
131                    break;
132                case OpenPgpApi.RESULT_CODE_ERROR:
133                    message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
134                    mXmppConnectionService.updateMessage(message);
135                    break;
136            }
137        } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
138            try {
139                final DownloadableFile inputFile = mXmppConnectionService.getFileBackend().getFile(message, false);
140                final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true);
141                outputFile.getParentFile().mkdirs();
142                outputFile.createNewFile();
143                InputStream is = new FileInputStream(inputFile);
144                OutputStream os = new FileOutputStream(outputFile);
145                Intent result = getOpenPgpApi().executeApi(params, is, os);
146                switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
147                    case OpenPgpApi.RESULT_CODE_SUCCESS:
148                        URL url = message.getFileParams().url;
149                        mXmppConnectionService.getFileBackend().updateFileParams(message,url);
150                        message.setEncryption(Message.ENCRYPTION_DECRYPTED);
151                        inputFile.delete();
152                        mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile);
153                        mXmppConnectionService.updateMessage(message);
154                        break;
155                    case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
156                        synchronized (PgpDecryptionService.this) {
157                            messages.addFirst(message);
158                            currentMessage = null;
159                            storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
160                        }
161                        break;
162                    case OpenPgpApi.RESULT_CODE_ERROR:
163                        message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
164                        mXmppConnectionService.updateMessage(message);
165                        break;
166                }
167            } catch (final IOException e) {
168                message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
169                mXmppConnectionService.updateMessage(message);
170            }
171        }
172        notifyIfPending(message);
173    }
174
175    private synchronized void notifyIfPending(Message message) {
176        if (pendingNotifications.remove(message)) {
177            mXmppConnectionService.getNotificationService().push(message);
178        }
179    }
180
181    private void storePendingIntent(PendingIntent pendingIntent) {
182        this.pendingIntent = pendingIntent;
183        mXmppConnectionService.updateConversationUi();
184    }
185
186    public synchronized boolean hasPendingIntent(Conversation conversation) {
187        if (pendingIntent == null) {
188            return false;
189        } else {
190            for(Message message : messages) {
191                if (message.getConversation() == conversation) {
192                    return true;
193                }
194            }
195            return false;
196        }
197    }
198
199    public PendingIntent getPendingIntent() {
200        return pendingIntent;
201    }
202
203    public boolean isConnected() {
204        return getOpenPgpApi() != null;
205    }
206}