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