aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs
diff options
context:
space:
mode:
authorAndreas Straub <andy@strb.org>2015-06-29 14:22:26 +0200
committerAndreas Straub <andy@strb.org>2015-07-19 21:32:26 +0200
commit3815d4efa378846c8aef840ad659268a0bef1536 (patch)
treeb13fffaeb22e0b4fcfb3b362319569f89fd6641d /src/main/java/eu/siacs
parentcb7980c65ed5d91296e3ad571298dbed434707c0 (diff)
Fetch bundles on-demand, encrypt in background
Bundles are now fetched on demand when a session needs to be established. This should lessen the chance of changes to the bundles occuring before they're used, as well as lessen the load of fetching bundles. Also, the message encryption is now done in a background thread, as this can be somewhat costly if many sessions are present. This is probably not going to be an issue in real use, but it's good practice anyway.
Diffstat (limited to 'src/main/java/eu/siacs')
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java133
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java6
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java10
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java12
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java13
5 files changed, 109 insertions, 65 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
index e4c0480a..54394dd2 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.crypto.axolotl;
+import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.Log;
@@ -42,13 +43,16 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class AxolotlService {
@@ -62,8 +66,9 @@ public class AxolotlService {
private final XmppConnectionService mXmppConnectionService;
private final SQLiteAxolotlStore axolotlStore;
private final SessionMap sessions;
- private final BundleMap bundleCache;
private final Map<Jid, Set<Integer>> deviceIds;
+ private final FetchStatusMap fetchStatusMap;
+ private final SerialSingleThreadExecutor executor;
private int ownDeviceId;
public static class SQLiteAxolotlStore implements AxolotlStore {
@@ -560,7 +565,13 @@ public class AxolotlService {
}
- private static class BundleMap extends AxolotlAddressMap<PreKeyBundle> {
+ private static enum FetchStatus {
+ PENDING,
+ SUCCESS,
+ ERROR
+ }
+
+ private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
}
@@ -570,7 +581,8 @@ public class AxolotlService {
this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
this.deviceIds = new HashMap<>();
this.sessions = new SessionMap(axolotlStore, account);
- this.bundleCache = new BundleMap();
+ this.fetchStatusMap = new FetchStatusMap();
+ this.executor = new SerialSingleThreadExecutor();
this.ownDeviceId = axolotlStore.getLocalRegistrationId();
}
@@ -793,56 +805,93 @@ public class AxolotlService {
}
}
- private void createSessionsIfNeeded(Contact contact) throws NoSessionsCreatedException {
+ private boolean createSessionsIfNeeded(Conversation conversation) {
+ boolean newSessions = false;
Log.d(Config.LOGTAG, "Creating axolotl sessions if needed...");
- AxolotlAddress address = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
- for(Integer deviceId: bundleCache.getAll(address).keySet()) {
- Log.d(Config.LOGTAG, "Processing device ID: " + deviceId);
- AxolotlAddress remoteAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), deviceId);
- if(sessions.get(remoteAddress) == null) {
- Log.d(Config.LOGTAG, "Building new sesstion for " + deviceId);
- SessionBuilder builder = new SessionBuilder(this.axolotlStore, remoteAddress);
- try {
- builder.process(bundleCache.get(remoteAddress));
- XmppAxolotlSession session = new XmppAxolotlSession(this.axolotlStore, remoteAddress);
- sessions.put(remoteAddress, session);
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": InvalidKeyException, " +e.getMessage());
- } catch (UntrustedIdentityException e) {
- Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": UntrustedIdentityException, " +e.getMessage());
- }
- } else {
- Log.d(Config.LOGTAG, "Already have session for " + deviceId);
+ Jid contactJid = conversation.getContact().getJid().toBareJid();
+ Set<AxolotlAddress> addresses = new HashSet<>();
+ if(deviceIds.get(contactJid) != null) {
+ for(Integer foreignId:this.deviceIds.get(contactJid)) {
+ Log.d(Config.LOGTAG, "Found device "+account.getJid().toBareJid()+":"+foreignId);
+ addresses.add(new AxolotlAddress(contactJid.toString(), foreignId));
+ }
+ } else {
+ Log.e(Config.LOGTAG, "Have no target devices in PEP!");
+ }
+ Log.d(Config.LOGTAG, "Checking own account "+account.getJid().toBareJid());
+ if(deviceIds.get(account.getJid().toBareJid()) != null) {
+ for(Integer ownId:this.deviceIds.get(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, "Found device "+account.getJid().toBareJid()+":"+ownId);
+ addresses.add(new AxolotlAddress(account.getJid().toBareJid().toString(), ownId));
}
}
- if(!this.hasAny(contact)) {
- Log.e(Config.LOGTAG, "No Axolotl sessions available!");
- throw new NoSessionsCreatedException(); // FIXME: proper error handling
+ for (AxolotlAddress address : addresses) {
+ Log.d(Config.LOGTAG, "Processing device: " + address.toString());
+ FetchStatus status = fetchStatusMap.get(address);
+ XmppAxolotlSession session = sessions.get(address);
+ if ( session == null && ( status == null || status == FetchStatus.ERROR) ) {
+ fetchStatusMap.put(address, FetchStatus.PENDING);
+ this.buildSessionFromPEP(conversation, address);
+ newSessions = true;
+ } else {
+ Log.d(Config.LOGTAG, "Already have session for " + address.toString());
+ }
}
+ return newSessions;
}
- public XmppAxolotlMessage processSending(Contact contact, String outgoingMessage) throws NoSessionsCreatedException {
- XmppAxolotlMessage message = new XmppAxolotlMessage(contact, ownDeviceId, outgoingMessage);
- createSessionsIfNeeded(contact);
- Log.d(Config.LOGTAG, "Building axolotl foreign headers...");
+ @Nullable
+ public XmppAxolotlMessage encrypt(Message message ){
+ final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(message.getContact(),
+ ownDeviceId, message.getBody());
- for(XmppAxolotlSession session : findSessionsforContact(contact)) {
-// if(!session.isTrusted()) {
- // TODO: handle this properly
- // continue;
- // }
- message.addHeader(session.processSending(message.getInnerKey()));
+ if(findSessionsforContact(axolotlMessage.getContact()).isEmpty()) {
+ return null;
+ }
+ Log.d(Config.LOGTAG, "Building axolotl foreign headers...");
+ for (XmppAxolotlSession session : findSessionsforContact(axolotlMessage.getContact())) {
+ Log.d(Config.LOGTAG, session.remoteAddress.toString());
+ //if(!session.isTrusted()) {
+ // TODO: handle this properly
+ // continue;
+ // }
+ axolotlMessage.addHeader(session.processSending(axolotlMessage.getInnerKey()));
}
Log.d(Config.LOGTAG, "Building axolotl own headers...");
- for(XmppAxolotlSession session : findOwnSessions()) {
- // if(!session.isTrusted()) {
- // TODO: handle this properly
- // continue;
- // }
- message.addHeader(session.processSending(message.getInnerKey()));
+ for (XmppAxolotlSession session : findOwnSessions()) {
+ Log.d(Config.LOGTAG, session.remoteAddress.toString());
+ // if(!session.isTrusted()) {
+ // TODO: handle this properly
+ // continue;
+ // }
+ axolotlMessage.addHeader(session.processSending(axolotlMessage.getInnerKey()));
}
- return message;
+ return axolotlMessage;
+ }
+
+ private void processSending(final Message message) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ MessagePacket packet = mXmppConnectionService.getMessageGenerator()
+ .generateAxolotlChat(message);
+ if (packet == null) {
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ } else {
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ mXmppConnectionService.sendMessagePacket(account, packet);
+ }
+ }
+ });
+ }
+
+ public void sendMessage(Message message) {
+ boolean newSessions = createSessionsIfNeeded(message.getConversation());
+
+ if (!newSessions) {
+ this.processSending(message);
+ }
}
public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceiving(XmppAxolotlMessage message) {
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 289ed4ea..2efd8a29 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -179,13 +179,13 @@ public class Conversation extends AbstractEntity implements Blockable {
}
}
- public void findUnsentMessagesWithOtrEncryption(OnMessageFound onMessageFound) {
+ public void findUnsentMessagesWithEncryption(int encryptionType, OnMessageFound onMessageFound) {
synchronized (this.messages) {
for (Message message : this.messages) {
if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
- && (message.getEncryption() == Message.ENCRYPTION_OTR)) {
+ && (message.getEncryption() == encryptionType)) {
onMessageFound.onMessageFound(message);
- }
+ }
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index 0b6a7c61..b0727690 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -66,16 +66,18 @@ public class MessageGenerator extends AbstractGenerator {
delay.setAttribute("stamp", mDateFormat.format(date));
}
- public MessagePacket generateAxolotlChat(Message message) throws NoSessionsCreatedException{
+ public MessagePacket generateAxolotlChat(Message message) {
return generateAxolotlChat(message, false);
}
- public MessagePacket generateAxolotlChat(Message message, boolean addDelay) throws NoSessionsCreatedException{
+ public MessagePacket generateAxolotlChat(Message message, boolean addDelay) {
MessagePacket packet = preparePacket(message, addDelay);
AxolotlService service = message.getConversation().getAccount().getAxolotlService();
Log.d(Config.LOGTAG, "Submitting message to axolotl service for send processing...");
- XmppAxolotlMessage axolotlMessage = service.processSending(message.getContact(),
- message.getBody());
+ XmppAxolotlMessage axolotlMessage = service.encrypt(message);
+ if (axolotlMessage == null) {
+ return null;
+ }
packet.setAxolotlMessage(axolotlMessage.toXml());
return packet;
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index de705730..31f70b97 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -7,6 +7,7 @@ import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import java.util.List;
+import java.util.Set;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -105,6 +106,7 @@ public class MessageParser extends AbstractParser implements
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
if(plaintextMessage != null) {
finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, Message.STATUS_RECEIVED);
+ finishedMessage.setAxolotlSession(plaintextMessage.getSession());
}
return finishedMessage;
@@ -189,15 +191,9 @@ public class MessageParser extends AbstractParser implements
} else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
Log.d(Config.LOGTAG, "Received PEP device list update from "+ from + ", processing...");
Element item = items.findChild("item");
- List<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
AxolotlService axolotlService = account.getAxolotlService();
- if(account.getJid().toBareJid().equals(from)) {
- } else {
- Contact contact = account.getRoster().getContact(from);
- for (Integer deviceId : deviceIds) {
- axolotlService.fetchBundleIfNeeded(contact, deviceId);
- }
- }
+ axolotlService.registerDevices(from, deviceIds);
}
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index f96e5d7e..8b716121 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -703,7 +703,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) {
message.getConversation().endOtrIfNeeded();
- message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR,
+ new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
markMessage(message,Message.STATUS_SEND_FAILED);
@@ -758,12 +759,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
break;
case Message.ENCRYPTION_AXOLOTL:
- try {
- packet = mMessageGenerator.generateAxolotlChat(message);
- Log.d(Config.LOGTAG, "Succeeded generating axolotl chat message!");
- } catch (NoSessionsCreatedException e) {
- message.setStatus(Message.STATUS_WAITING);
- }
+ message.setStatus(Message.STATUS_WAITING);
+ account.getAxolotlService().sendMessage(message);
break;
}
@@ -1797,7 +1794,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
account.getJid().toBareJid() + " otr session established with "
+ conversation.getJid() + "/"
+ otrSession.getSessionID().getUserID());
- conversation.findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {