aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2016-02-29 13:18:07 +0100
committerDaniel Gultsch <daniel@gultsch.de>2016-02-29 13:18:07 +0100
commit9e0466d1e643c07d1c3de088ddf2e5655899a50c (patch)
tree8b77dc7bce5f8aeadac51124a3275465ca6f3c9e /src/main/java/eu/siacs/conversations
parent199ae3a4d8253fb00cdef5556763a46a319a2d46 (diff)
refactored omemo to take multiple recipients
Diffstat (limited to 'src/main/java/eu/siacs/conversations')
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java154
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java20
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java11
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java7
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java14
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java8
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java40
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java21
-rw-r--r--src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java156
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java2
10 files changed, 286 insertions, 147 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 949975aa4..8658dfe20 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -81,13 +81,14 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
- public boolean fetchMapHasErrors(Contact contact) {
- Jid jid = contact.getJid().toBareJid();
- if (deviceIds.get(jid) != null) {
- for (Integer foreignId : this.deviceIds.get(jid)) {
- AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
- if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) {
- return true;
+ public boolean fetchMapHasErrors(List<Jid> jids) {
+ for(Jid jid : jids) {
+ if (deviceIds.get(jid) != null) {
+ for (Integer foreignId : this.deviceIds.get(jid)) {
+ AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
+ if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) {
+ return true;
+ }
}
}
}
@@ -242,12 +243,29 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust);
}
- public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Contact contact) {
- return axolotlStore.getContactKeysWithTrust(contact.getJid().toBareJid().toString(), trust);
+ public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) {
+ return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toString(), trust);
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) {
+ Set<IdentityKey> keys = new HashSet<>();
+ for(Jid jid : jids) {
+ keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toString(), trust));
+ }
+ return keys;
}
- public long getNumTrustedKeys(Contact contact) {
- return axolotlStore.getContactNumTrustedKeys(contact.getJid().toBareJid().toString());
+ public long getNumTrustedKeys(Jid jid) {
+ return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString());
+ }
+
+ public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
+ for(Jid jid : jids) {
+ if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()) == 0) {
+ return true;
+ }
+ }
+ return false;
}
private AxolotlAddress getAddressForJid(Jid jid) {
@@ -259,11 +277,19 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return new HashSet<>(this.sessions.getAll(ownAddress).values());
}
- private Set<XmppAxolotlSession> findSessionsforContact(Contact contact) {
+ private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) {
AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
return new HashSet<>(this.sessions.getAll(contactAddress).values());
}
+ private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) {
+ HashSet<XmppAxolotlSession> sessions = new HashSet<>();
+ for(Jid jid : getCryptoTargets(conversation)) {
+ sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values());
+ }
+ return sessions;
+ }
+
public Set<String> getFingerprintsForOwnSessions() {
Set<String> fingerprints = new HashSet<>();
for (XmppAxolotlSession session : findOwnSessions()) {
@@ -274,15 +300,14 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
public Set<String> getFingerprintsForContact(final Contact contact) {
Set<String> fingerprints = new HashSet<>();
- for (XmppAxolotlSession session : findSessionsforContact(contact)) {
+ for (XmppAxolotlSession session : findSessionsForContact(contact)) {
fingerprints.add(session.getFingerprint());
}
return fingerprints;
}
- private boolean hasAny(Contact contact) {
- AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
- return sessions.hasAny(contactAddress);
+ private boolean hasAny(Jid jid) {
+ return sessions.hasAny(getAddressForJid(jid));
}
public boolean isPepBroken() {
@@ -594,10 +619,25 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
});
}
- public boolean isContactAxolotlCapable(Contact contact) {
- Jid jid = contact.getJid().toBareJid();
- return hasAny(contact) ||
- (deviceIds.containsKey(jid) && !deviceIds.get(jid).isEmpty());
+ public boolean isConversationAxolotlCapable(Conversation conversation) {
+ final List<Jid> jids = getCryptoTargets(conversation);
+ for(Jid jid : jids) {
+ if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
+ return false;
+ }
+ }
+ return jids.size() > 0;
+ }
+
+ public List<Jid> getCryptoTargets(Conversation conversation) {
+ final List<Jid> jids;
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ jids = Arrays.asList(conversation.getJid().toBareJid());
+ } else {
+ jids = conversation.getMucOptions().getMembers();
+ jids.remove(account.getJid().toBareJid());
+ }
+ return jids;
}
public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) {
@@ -753,33 +793,31 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) {
- return findDevicesWithoutSession(conversation.getContact().getJid().toBareJid());
- }
-
- public Set<AxolotlAddress> findDevicesWithoutSession(final Jid contactJid) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + contactJid);
Set<AxolotlAddress> addresses = new HashSet<>();
- if (deviceIds.get(contactJid) != null) {
- for (Integer foreignId : this.deviceIds.get(contactJid)) {
- AxolotlAddress address = new AxolotlAddress(contactJid.toString(), foreignId);
- if (sessions.get(address) == null) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- if (identityKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
- sessions.put(address, session);
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + contactJid + ":" + foreignId);
- if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
- addresses.add(address);
+ for(Jid jid : getCryptoTargets(conversation)) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid);
+ if (deviceIds.get(jid) != null) {
+ for (Integer foreignId : this.deviceIds.get(jid)) {
+ AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
+ if (sessions.get(address) == null) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ if (identityKey != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
+ sessions.put(address, session);
} else {
- Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken");
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + jid + ":" + foreignId);
+ if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
+ addresses.add(address);
+ } else {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
+ }
}
}
}
+ } else {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
}
- } else {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
}
if (deviceIds.get(account.getJid().toBareJid()) != null) {
for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) {
@@ -827,7 +865,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
public boolean trustedSessionVerified(final Conversation conversation) {
- Set<XmppAxolotlSession> sessions = findSessionsforContact(conversation.getContact());
+ Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation);
sessions.addAll(findOwnSessions());
boolean verified = false;
for(XmppAxolotlSession session : sessions) {
@@ -842,26 +880,32 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return verified;
}
- public boolean hasPendingKeyFetches(Account account, Contact contact) {
+ public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
- AxolotlAddress foreignAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
- return fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
- || fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING);
-
+ if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
+ return true;
+ }
+ for(Jid jid : jids) {
+ AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toString(), 0);
+ if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) {
+ return true;
+ }
+ }
+ return false;
}
@Nullable
- private XmppAxolotlMessage buildHeader(Contact contact) {
+ private XmppAxolotlMessage buildHeader(Conversation conversation) {
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(
- contact.getJid().toBareJid(), getOwnDeviceId());
+ account.getJid().toBareJid(), getOwnDeviceId());
- Set<XmppAxolotlSession> contactSessions = findSessionsforContact(contact);
+ Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation);
Set<XmppAxolotlSession> ownSessions = findOwnSessions();
- if (contactSessions.isEmpty()) {
+ if (remoteSessions.isEmpty()) {
return null;
}
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
- for (XmppAxolotlSession session : contactSessions) {
+ for (XmppAxolotlSession session : remoteSessions) {
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
axolotlMessage.addDevice(session);
}
@@ -876,7 +920,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
@Nullable
public XmppAxolotlMessage encrypt(Message message) {
- XmppAxolotlMessage axolotlMessage = buildHeader(message.getContact());
+ XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation());
if (axolotlMessage != null) {
final String content;
@@ -913,11 +957,11 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
});
}
- public void prepareKeyTransportMessage(final Contact contact, final OnMessageCreatedCallback onMessageCreatedCallback) {
+ public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
executor.execute(new Runnable() {
@Override
public void run() {
- XmppAxolotlMessage axolotlMessage = buildHeader(contact);
+ XmppAxolotlMessage axolotlMessage = buildHeader(conversation);
onMessageCreatedCallback.run(axolotlMessage);
}
});
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 796b36f9f..f05b3f911 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -659,8 +659,8 @@ public class Conversation extends AbstractEntity implements Blockable {
final AxolotlService axolotlService = getAccount().getAxolotlService();
int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1);
if (next == -1) {
- if (Config.X509_VERIFICATION && mode == MODE_SINGLE) {
- if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) {
+ if (Config.X509_VERIFICATION) {
+ if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) {
return Message.ENCRYPTION_AXOLOTL;
} else {
return Message.ENCRYPTION_NONE;
@@ -673,16 +673,20 @@ public class Conversation extends AbstractEntity implements Blockable {
next = outgoing;
}
}
- if (!Config.supportUnencrypted()
- && (mode == MODE_SINGLE || Config.supportOpenPgpOnly())
- && next <= 0) {
- if (Config.supportOmemo() && (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact()) || !Config.multipleEncryptionChoices())) {
+
+ if (!Config.supportUnencrypted() && next <= 0) {
+ if (Config.supportOmemo()
+ && (axolotlService != null && axolotlService.isConversationAxolotlCapable(this) || !Config.multipleEncryptionChoices())) {
return Message.ENCRYPTION_AXOLOTL;
- } else if (Config.supportOtr()) {
+ } else if (Config.supportOtr() && mode == MODE_SINGLE) {
return Message.ENCRYPTION_OTR;
- } else if (Config.supportOpenPgp()) {
+ } else if (Config.supportOpenPgp()
+ && (mode == MODE_SINGLE) || !Config.multipleEncryptionChoices()) {
return Message.ENCRYPTION_PGP;
}
+ } else if (next == Message.ENCRYPTION_AXOLOTL
+ && (!Config.supportOmemo() || axolotlService == null || !axolotlService.isConversationAxolotlCapable(this))) {
+ next = Message.ENCRYPTION_NONE;
}
return next;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 3567c14f4..059573c63 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -4,9 +4,11 @@ import android.annotation.SuppressLint;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Data;
@@ -245,6 +247,7 @@ public class MucOptions {
private Account account;
private final Map<String, User> users = Collections.synchronizedMap(new LinkedHashMap<String, User>());
+ private final Set<Jid> members = Collections.synchronizedSet(new HashSet<Jid>());
private List<String> features = new ArrayList<>();
private Data form = new Data();
private Conversation conversation;
@@ -501,4 +504,12 @@ public class MucOptions {
public Conversation getConversation() {
return this.conversation;
}
+
+ public void putMember(Jid jid) {
+ members.add(jid);
+ }
+
+ public List<Jid> getMembers() {
+ return new ArrayList<>(members);
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 4395a5460..a3b0f6506 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -335,4 +335,11 @@ public class IqGenerator extends AbstractGenerator {
enable.addChild(data);
return packet;
}
+
+ public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation);
+ 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 aa9d7f6c3..fd2d070ce 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -103,7 +103,7 @@ public class MessageParser extends AbstractParser implements
}
}
- private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation, int status) {
+ private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
Message finishedMessage = null;
AxolotlService service = conversation.getAccount().getAxolotlService();
XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
@@ -357,7 +357,17 @@ public class MessageParser extends AbstractParser implements
} else if (pgpEncrypted != null && Config.supportOpenPgp()) {
message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
} else if (axolotlEncrypted != null && Config.supportOmemo()) {
- message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation, status);
+ Jid origin;
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ origin = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart());
+ if (origin == null) {
+ Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received");
+ return;
+ }
+ } else {
+ origin = from;
+ }
+ message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
if (message == null) {
return;
}
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 63d28c97b..c2782d232 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -64,10 +64,14 @@ public class PresenceParser extends AbstractParser implements
Element item = x.findChild("item");
if (item != null && !from.isBareJid()) {
mucOptions.setError(MucOptions.Error.NONE);
- MucOptions.User user = new MucOptions.User(mucOptions,from);
+ MucOptions.User user = new MucOptions.User(mucOptions, from);
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
- user.setJid(item.getAttributeAsJid("jid"));
+ Jid real = item.getAttributeAsJid("jid");
+ if (real != null) {
+ user.setJid(real);
+ mucOptions.putMember(real.toBareJid());
+ }
if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
mucOptions.setOnline();
mucOptions.setSelf(user);
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index dde8ad280..0518f992e 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -1784,6 +1784,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Account account = conversation.getAccount();
final String nick = conversation.getMucOptions().getProposedNick();
final Jid joinJid = conversation.getMucOptions().createJoinJid(nick);
+ final MucOptions mucOptions = conversation.getMucOptions();
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
PresencePacket packet = new PresencePacket();
packet.setFrom(conversation.getAccount().getJid());
@@ -1793,7 +1794,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
x.addChild("password").setContent(conversation.getMucOptions().getPassword());
}
- if (conversation.getMucOptions().mamSupport()) {
+ if (mucOptions.mamSupport()) {
// Use MAM instead of the limited muc history to get history
x.addChild("history").setAttribute("maxchars", "0");
} else {
@@ -1812,9 +1813,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
conversation.setContactJid(joinJid);
databaseBackend.updateConversation(conversation);
}
- if (conversation.getMucOptions().mamSupport()) {
+
+ if (mucOptions.mamSupport()) {
getMessageArchiveService().catchupMUC(conversation);
}
+ if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
+ fetchConferenceMembers(conversation);
+ }
sendUnsentMessages(conversation);
}
@@ -1838,6 +1843,37 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
+ private void fetchConferenceMembers(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final String[] affiliations = {"member","admin","owner"};
+ OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ private int i = 0;
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.query("http://jabber.org/protocol/muc#admin");
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ for(Element child : query.getChildren()) {
+ if ("item".equals(child.getName())) {
+ conversation.getMucOptions().putMember(child.getAttributeAsJid("jid"));
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid());
+ }
+ ++i;
+ if (i >= affiliations.length) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers());
+ }
+ }
+ };
+ for(String affiliation : affiliations) {
+ sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
+ }
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName());
+ }
+
public void providePasswordForMuc(Conversation conversation, String password) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.getMucOptions().setPassword(password);
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 55a127109..e4ffb3e46 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -408,7 +408,7 @@ public class ConversationActivity extends XmppActivity
menuContactDetails.setVisible(false);
menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating());
menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
- menuSecure.setVisible(Config.supportOpenPgp() && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
+ menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
} else {
menuMucDetails.setVisible(false);
menuSecure.setVisible(Config.multipleEncryptionChoices());
@@ -856,8 +856,8 @@ public class ConversationActivity extends XmppActivity
axolotl.setVisible(Config.supportOmemo());
if (conversation.getMode() == Conversation.MODE_MULTI) {
otr.setVisible(false);
- axolotl.setVisible(false);
- } else if (!conversation.getAccount().getAxolotlService().isContactAxolotlCapable(conversation.getContact())) {
+ }
+ if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) {
axolotl.setEnabled(false);
}
switch (conversation.getNextEncryption()) {
@@ -1530,18 +1530,21 @@ public class ConversationActivity extends XmppActivity
protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
- Contact contact = mSelectedConversation.getContact();
+ final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED).isEmpty();
- boolean hasUndecidedContact = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED,contact).isEmpty();
+ boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, targets).isEmpty();
boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
- boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0;
- if(hasUndecidedOwn || hasUndecidedContact || hasPendingKeys || hasNoTrustedKeys) {
+ boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
+ if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys) {
axolotlService.createSessionsIfNeeded(mSelectedConversation);
Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
- intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString());
+ String[] contacts = new String[targets.size()];
+ for(int i = 0; i < contacts.length; ++i) {
+ contacts[i] = targets.get(i).toString();
+ }
+ intent.putExtra("contacts", contacts);
intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString());
intent.putExtra("choice", attachmentChoice);
- intent.putExtra("has_no_trusted", hasNoTrustedKeys);
startActivityForResult(intent, requestCode);
return true;
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
index eec30798e..8919ac79c 100644
--- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
@@ -12,7 +12,9 @@ import android.widget.Toast;
import org.whispersystems.libaxolotl.IdentityKey;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -20,32 +22,28 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated {
private Jid accountJid;
- private Jid contactJid;
+ private List<Jid> contactJids;
- private Contact contact;
private Account mAccount;
private TextView keyErrorMessage;
private LinearLayout keyErrorMessageCard;
private TextView ownKeysTitle;
private LinearLayout ownKeys;
private LinearLayout ownKeysCard;
- private TextView foreignKeysTitle;
private LinearLayout foreignKeys;
- private LinearLayout foreignKeysCard;
private Button mSaveButton;
private Button mCancelButton;
private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
- private final Map<String, Boolean> foreignKeysToTrust = new HashMap<>();
+ private final Map<Jid,Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
private final OnClickListener mSaveButtonListener = new OnClickListener() {
@Override
@@ -70,15 +68,6 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
}
@Override
- protected String getShareableUri() {
- if (contact != null) {
- return contact.getShareableUri();
- } else {
- return "";
- }
- }
-
- @Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trust_keys);
@@ -86,9 +75,13 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT));
} catch (final InvalidJidException ignored) {
}
- try {
- this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
- } catch (final InvalidJidException ignored) {
+ this.contactJids = new ArrayList<>();
+ for(String jid : getIntent().getStringArrayExtra("contacts")) {
+ try {
+ this.contactJids.add(Jid.fromString(jid));
+ } catch (InvalidJidException e) {
+ e.printStackTrace();
+ }
}
keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card);
@@ -96,9 +89,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
- foreignKeysTitle = (TextView) findViewById(R.id.foreign_keys_title);
- foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys_details);
- foreignKeysCard = (LinearLayout) findViewById(R.id.foreign_keys_card);
+ foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys);
mCancelButton = (Button) findViewById(R.id.cancel_button);
mCancelButton.setOnClickListener(mCancelButtonListener);
mSaveButton = (Button) findViewById(R.id.save_button);
@@ -119,7 +110,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
boolean hasForeignKeys = false;
for(final String fingerprint : ownKeysToTrust.keySet()) {
hasOwnKeys = true;
- addFingerprintRowWithListeners(ownKeys, contact.getAccount(), fingerprint, false,
+ addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint)), false,
new CompoundButton.OnCheckedChangeListener() {
@Override
@@ -132,30 +123,36 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
null
);
}
- for(final String fingerprint : foreignKeysToTrust.keySet()) {
- hasForeignKeys = true;
- addFingerprintRowWithListeners(foreignKeys, contact.getAccount(), fingerprint, false,
- XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(fingerprint)), false,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- foreignKeysToTrust.put(fingerprint, isChecked);
- lockOrUnlockAsNeeded();
- }
- },
- null,
- null
- );
- }
- if(hasOwnKeys) {
- ownKeysTitle.setText(accountJid.toString());
- ownKeysCard.setVisibility(View.VISIBLE);
- }
- if(hasForeignKeys) {
- foreignKeysTitle.setText(contactJid.toString());
- foreignKeysCard.setVisibility(View.VISIBLE);
+ synchronized (this.foreignKeysToTrust) {
+ for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+ final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
+ final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title);
+ final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details);
+ header.setText(entry.getKey().toString());
+ final Map<String, Boolean> fingerprints = entry.getValue();
+ for (final String fingerprint : fingerprints.keySet()) {
+ hasForeignKeys = true;
+ addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
+ XmppAxolotlSession.Trust.fromBoolean(fingerprints.get(fingerprint)), false,
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ fingerprints.put(fingerprint, isChecked);
+ lockOrUnlockAsNeeded();
+ }
+ },
+ null,
+ null
+ );
+ }
+ foreignKeys.addView(layout);
+ }
}
+
+ ownKeysTitle.setText(accountJid.toString());
+ ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
+ foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
if(hasPendingKeyFetches()) {
setFetching();
lock();
@@ -163,13 +160,15 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
keyErrorMessageCard.setVisibility(View.VISIBLE);
if (lastFetchReport == AxolotlService.FetchStatus.ERROR
- || contact.getAccount().getAxolotlService().fetchMapHasErrors(contact)) {
+ || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) {
keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error);
} else {
keyErrorMessage.setText(R.string.error_no_keys_to_trust);
}
- ownKeys.removeAllViews(); ownKeysCard.setVisibility(View.GONE);
- foreignKeys.removeAllViews(); foreignKeysCard.setVisibility(View.GONE);
+ ownKeys.removeAllViews();
+ ownKeysCard.setVisibility(View.GONE);
+ foreignKeys.removeAllViews();
+ foreignKeys.setVisibility(View.GONE);
}
lockOrUnlockAsNeeded();
setDone();
@@ -178,45 +177,56 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
private boolean reloadFingerprints() {
ownKeysToTrust.clear();
- foreignKeysToTrust.clear();
AxolotlService service = this.mAccount.getAxolotlService();
Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED);
- Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact);
- if (hasNoOtherTrustedKeys() && ownKeysSet.size() == 0) {
- foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact));
- }
for(final IdentityKey identityKey : ownKeysSet) {
if(!ownKeysToTrust.containsKey(identityKey)) {
ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
}
}
- for(final IdentityKey identityKey : foreignKeysSet) {
- if(!foreignKeysToTrust.containsKey(identityKey)) {
- foreignKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ synchronized (this.foreignKeysToTrust) {
+ foreignKeysToTrust.clear();
+ for (Jid jid : contactJids) {
+ Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, jid);
+ if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
+ foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, jid));
+ }
+ Map<String, Boolean> foreignFingerprints = new HashMap<>();
+ for (final IdentityKey identityKey : foreignKeysSet) {
+ if (!foreignFingerprints.containsKey(identityKey)) {
+ foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ }
+ }
+ if (foreignFingerprints.size() > 0) {
+ foreignKeysToTrust.put(jid, foreignFingerprints);
+ }
}
}
- return ownKeysSet.size() + foreignKeysSet.size() > 0;
+ return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
}
@Override
public void onBackendConnected() {
- if ((accountJid != null) && (contactJid != null)) {
+ if (accountJid != null) {
this.mAccount = xmppConnectionService.findAccountByJid(accountJid);
if (this.mAccount == null) {
return;
}
- this.contact = this.mAccount.getRoster().getContact(contactJid);
reloadFingerprints();
populateView();
}
}
private boolean hasNoOtherTrustedKeys() {
+ return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
+ }
+
+ private boolean hasNoOtherTrustedKeys(Jid contact) {
return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
}
private boolean hasPendingKeyFetches() {
- return mAccount != null && contact != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount,contact);
+ return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
}
@@ -262,14 +272,18 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
private void commitTrusts() {
for(final String fingerprint :ownKeysToTrust.keySet()) {
- contact.getAccount().getAxolotlService().setFingerprintTrust(
+ mAccount.getAxolotlService().setFingerprintTrust(
fingerprint,
XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint)));
}
- for(final String fingerprint:foreignKeysToTrust.keySet()) {
- contact.getAccount().getAxolotlService().setFingerprintTrust(
- fingerprint,
- XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(fingerprint)));
+ synchronized (this.foreignKeysToTrust) {
+ for (Map<String, Boolean> value : foreignKeysToTrust.values()) {
+ for (final String fingerprint : value.keySet()) {
+ mAccount.getAxolotlService().setFingerprintTrust(
+ fingerprint,
+ XmppAxolotlSession.Trust.fromBoolean(value.get(fingerprint)));
+ }
+ }
}
}
@@ -284,11 +298,17 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
}
private void lockOrUnlockAsNeeded() {
- if (hasNoOtherTrustedKeys() && !foreignKeysToTrust.values().contains(true)){
- lock();
- } else {
- unlock();
+ synchronized (this.foreignKeysToTrust) {
+ for (Jid jid : contactJids) {
+ Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid);
+ if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.values().contains(true))) {
+ lock();
+ return;
+ }
+ }
}
+ unlock();
+
}
private void setDone() {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 56582f974..a8e081a72 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -218,7 +218,7 @@ public class JingleConnection implements Transferable {
public void init(final Message message) {
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
Conversation conversation = message.getConversation();
- conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation.getContact(), new OnMessageCreatedCallback() {
+ conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
@Override
public void run(XmppAxolotlMessage xmppAxolotlMessage) {
if (xmppAxolotlMessage != null) {