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