1package eu.siacs.conversations.crypto;
2
3import android.app.PendingIntent;
4
5import eu.siacs.conversations.entities.Message;
6import eu.siacs.conversations.services.XmppConnectionService;
7import eu.siacs.conversations.ui.UiCallback;
8
9import java.util.Collections;
10import java.util.LinkedList;
11import java.util.List;
12import java.util.concurrent.ConcurrentHashMap;
13
14public class PgpDecryptionService {
15
16 private final XmppConnectionService xmppConnectionService;
17 private final ConcurrentHashMap<String, List<Message>> messages = new ConcurrentHashMap<>();
18 private final ConcurrentHashMap<String, Boolean> decryptingMessages = new ConcurrentHashMap<>();
19 private Boolean keychainLocked = false;
20 private final Object keychainLockedLock = new Object();
21
22 public PgpDecryptionService(XmppConnectionService xmppConnectionService) {
23 this.xmppConnectionService = xmppConnectionService;
24 }
25
26 public void add(Message message) {
27 if (isRunning()) {
28 decryptDirectly(message);
29 } else {
30 store(message);
31 }
32 }
33
34 public void addAll(List<Message> messagesList) {
35 if (!messagesList.isEmpty()) {
36 String conversationUuid = messagesList.get(0).getConversation().getUuid();
37 if (!messages.containsKey(conversationUuid)) {
38 List<Message> list = Collections.synchronizedList(new LinkedList<Message>());
39 messages.put(conversationUuid, list);
40 }
41 synchronized (messages.get(conversationUuid)) {
42 messages.get(conversationUuid).addAll(messagesList);
43 }
44 decryptAllMessages();
45 }
46 }
47
48 public void onKeychainUnlocked() {
49 synchronized (keychainLockedLock) {
50 keychainLocked = false;
51 }
52 decryptAllMessages();
53 }
54
55 public void onKeychainLocked() {
56 synchronized (keychainLockedLock) {
57 keychainLocked = true;
58 }
59 xmppConnectionService.updateConversationUi();
60 }
61
62 public void onOpenPgpServiceBound() {
63 decryptAllMessages();
64 }
65
66 public boolean isRunning() {
67 synchronized (keychainLockedLock) {
68 return !keychainLocked;
69 }
70 }
71
72 private void store(Message message) {
73 if (messages.containsKey(message.getConversation().getUuid())) {
74 messages.get(message.getConversation().getUuid()).add(message);
75 } else {
76 List<Message> messageList = Collections.synchronizedList(new LinkedList<Message>());
77 messageList.add(message);
78 messages.put(message.getConversation().getUuid(), messageList);
79 }
80 }
81
82 private void decryptAllMessages() {
83 for (String uuid : messages.keySet()) {
84 decryptMessages(uuid);
85 }
86 }
87
88 private void decryptMessages(final String uuid) {
89 synchronized (decryptingMessages) {
90 Boolean decrypting = decryptingMessages.get(uuid);
91 if ((decrypting != null && !decrypting) || decrypting == null) {
92 decryptingMessages.put(uuid, true);
93 decryptMessage(uuid);
94 }
95 }
96 }
97
98 private void decryptMessage(final String uuid) {
99 Message message = null;
100 synchronized (messages.get(uuid)) {
101 while (!messages.get(uuid).isEmpty()) {
102 if (messages.get(uuid).get(0).getEncryption() == Message.ENCRYPTION_PGP) {
103 if (isRunning()) {
104 message = messages.get(uuid).remove(0);
105 }
106 break;
107 } else {
108 messages.get(uuid).remove(0);
109 }
110 }
111 if (message != null && xmppConnectionService.getPgpEngine() != null) {
112 xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback<Message>() {
113
114 @Override
115 public void userInputRequried(PendingIntent pi, Message message) {
116 messages.get(uuid).add(0, message);
117 decryptingMessages.put(uuid, false);
118 }
119
120 @Override
121 public void success(Message message) {
122 xmppConnectionService.updateConversationUi();
123 decryptMessage(uuid);
124 }
125
126 @Override
127 public void error(int error, Message message) {
128 message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
129 xmppConnectionService.updateConversationUi();
130 decryptMessage(uuid);
131 }
132 });
133 } else {
134 decryptingMessages.put(uuid, false);
135 }
136 }
137 }
138
139 private void decryptDirectly(final Message message) {
140 if (message.getEncryption() == Message.ENCRYPTION_PGP && xmppConnectionService.getPgpEngine() != null) {
141 xmppConnectionService.getPgpEngine().decrypt(message, new UiCallback<Message>() {
142
143 @Override
144 public void userInputRequried(PendingIntent pi, Message message) {
145 store(message);
146 }
147
148 @Override
149 public void success(Message message) {
150 xmppConnectionService.updateConversationUi();
151 xmppConnectionService.getNotificationService().updateNotification(false);
152 }
153
154 @Override
155 public void error(int error, Message message) {
156 message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
157 xmppConnectionService.updateConversationUi();
158 }
159 });
160 }
161 }
162}