get rid of legacy Jid wrapper around jxmpp

This commit is contained in:
Daniel Gultsch 2023-02-16 10:33:18 +01:00
parent 6845380be5
commit 3c42066a7c
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
74 changed files with 372 additions and 319 deletions

View file

@ -5,7 +5,6 @@ import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.Iterables;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.IDs;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.entity.AccountEntity;
@ -28,12 +27,16 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;
@RunWith(AndroidJUnit4.class)
public class TransformationTest {
private static final Jid ACCOUNT = Jid.of("user@example.com");
private static final Jid REMOTE = Jid.of("juliet@example.com");
private static final BareJid ACCOUNT = JidCreate.bareFromOrThrowUnchecked("user@example.com");
private static final BareJid REMOTE = JidCreate.bareFromOrThrowUnchecked("juliet@example.com");
private static final String GREETING = "Hi Juliet. How are you?";
@ -55,11 +58,11 @@ public class TransformationTest {
}
@Test
public void reactionBeforeOriginal() {
public void reactionBeforeOriginal() throws XmppStringprepException {
final var reactionMessage = new Message();
reactionMessage.setId("2");
reactionMessage.setTo(ACCOUNT);
reactionMessage.setFrom(REMOTE.withResource("junit"));
reactionMessage.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
final var reactions = reactionMessage.addExtension(new Reactions());
reactions.setId("1");
final var reaction = reactions.addExtension(new Reaction());
@ -69,7 +72,7 @@ public class TransformationTest {
final var originalMessage = new Message();
originalMessage.setId("1");
originalMessage.setTo(REMOTE);
originalMessage.setFrom(ACCOUNT.withResource("junit"));
originalMessage.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from(("junit"))));
final var body = originalMessage.addExtension(new Body());
body.setContent(GREETING);
this.transformer.transform(
@ -86,28 +89,28 @@ public class TransformationTest {
}
@Test
public void multipleReactions() {
final var group = Jid.ofEscaped("a@group.example.com");
public void multipleReactions() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var message = new Message(Message.Type.GROUPCHAT);
message.addExtension(new Body("Please give me a thumbs up"));
message.setFrom(group.withResource("user-a"));
message.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(message, Instant.now(), REMOTE, "stanza-a", "id-user-a"));
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(group.withResource("user-b"));
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(reactionA, Instant.now(), REMOTE, "stanza-b", "id-user-b"));
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(group.withResource("user-c"));
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(reactionB, Instant.now(), REMOTE, "stanza-c", "id-user-c"));
final var reactionC = new Message(Message.Type.GROUPCHAT);
reactionC.setFrom(group.withResource("user-d"));
reactionC.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-d")));
final var reactions = reactionC.addExtension(Reactions.to("stanza-a"));
reactions.addExtension(new Reaction("Y"));
reactions.addExtension(new Reaction("Z"));
@ -128,12 +131,12 @@ public class TransformationTest {
}
@Test
public void correctionBeforeOriginal() {
public void correctionBeforeOriginal() throws XmppStringprepException {
final var messageCorrection = new Message();
messageCorrection.setId("2");
messageCorrection.setTo(ACCOUNT);
messageCorrection.setFrom(REMOTE.withResource("junit"));
messageCorrection.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageCorrection.addExtension(new Body()).setContent("Hi example!");
messageCorrection.addExtension(new Replace()).setId("1");
@ -146,7 +149,7 @@ public class TransformationTest {
final var messageWithTypo = new Message();
messageWithTypo.setId("1");
messageWithTypo.setTo(ACCOUNT);
messageWithTypo.setFrom(REMOTE.withResource("junit"));
messageWithTypo.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageWithTypo.addExtension(new Body()).setContent("Hii example!");
this.transformer.transform(
@ -163,12 +166,12 @@ public class TransformationTest {
}
@Test
public void correctionAfterOriginal() {
public void correctionAfterOriginal() throws XmppStringprepException {
final var messageWithTypo = new Message();
messageWithTypo.setId("1");
messageWithTypo.setTo(ACCOUNT);
messageWithTypo.setFrom(REMOTE.withResource("junit"));
messageWithTypo.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageWithTypo.addExtension(new Body()).setContent("Hii example!");
this.transformer.transform(
@ -179,7 +182,7 @@ public class TransformationTest {
final var messageCorrection = new Message();
messageCorrection.setId("2");
messageCorrection.setTo(ACCOUNT);
messageCorrection.setFrom(REMOTE.withResource("junit"));
messageCorrection.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageCorrection.addExtension(new Body()).setContent("Hi example!");
messageCorrection.addExtension(new Replace()).setId("1");
@ -197,22 +200,22 @@ public class TransformationTest {
}
@Test
public void replacingReactions() {
final var group = Jid.ofEscaped("a@group.example.com");
public void replacingReactions() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var message = new Message(Message.Type.GROUPCHAT);
message.addExtension(new Body("Please give me a thumbs up"));
message.setFrom(group.withResource("user-a"));
message.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(message, Instant.now(), REMOTE, "stanza-a", "id-user-a"));
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(group.withResource("user-b"));
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("N"));
this.transformer.transform(
Transformation.of(reactionA, Instant.now(), REMOTE, "stanza-b", "id-user-b"));
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(group.withResource("user-b"));
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionB.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(reactionB, Instant.now(), REMOTE, "stanza-c", "id-user-b"));
@ -224,8 +227,9 @@ public class TransformationTest {
}
@Test
public void twoCorrectionsOneReactionBeforeOriginalInGroupChat() {
final var group = Jid.ofEscaped("a@group.example.com");
public void twoCorrectionsOneReactionBeforeOriginalInGroupChat()
throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
@ -234,7 +238,7 @@ public class TransformationTest {
// m1.setId(ogMessageId);
m1.addExtension(new Body("Please give me an thumbs up"));
m1.addExtension(new Replace()).setId(ogMessageId);
m1.setFrom(group.withResource("user-a"));
m1.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(
m1,
@ -248,7 +252,7 @@ public class TransformationTest {
// m2.setId(ogMessageId);
m2.addExtension(new Body("Please give me a thumbs up"));
m2.addExtension(new Replace()).setId(ogMessageId);
m2.setFrom(group.withResource("user-a"));
m2.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(
m2,
@ -259,7 +263,7 @@ public class TransformationTest {
// a reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(group.withResource("user-b"));
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(
@ -269,7 +273,7 @@ public class TransformationTest {
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me thumbs up"));
m4.setFrom(group.withResource("user-a"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, "id-user-a"));
@ -283,14 +287,15 @@ public class TransformationTest {
}
@Test
public void twoReactionsOneCorrectionBeforeOriginalInGroupChat() {
final var group = Jid.ofEscaped("a@group.example.com");
public void twoReactionsOneCorrectionBeforeOriginalInGroupChat()
throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
// first reaction
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(group.withResource("user-b"));
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(
@ -298,7 +303,7 @@ public class TransformationTest {
// second reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(group.withResource("user-c"));
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(
@ -308,7 +313,7 @@ public class TransformationTest {
final var m1 = new Message(Message.Type.GROUPCHAT);
m1.addExtension(new Body("Please give me a thumbs up"));
m1.addExtension(new Replace()).setId(ogMessageId);
m1.setFrom(group.withResource("user-a"));
m1.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(
m1,
@ -321,7 +326,7 @@ public class TransformationTest {
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me thumbs up (Typo)"));
m4.setFrom(group.withResource("user-a"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, "id-user-a"));
@ -337,8 +342,8 @@ public class TransformationTest {
}
@Test
public void twoReactionsInGroupChat() {
final var group = Jid.ofEscaped("a@group.example.com");
public void twoReactionsInGroupChat() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
@ -346,13 +351,13 @@ public class TransformationTest {
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me a thumbs up"));
m4.setFrom(group.withResource("user-a"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
Transformation.of(m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, "id-user-a"));
// first reaction
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(group.withResource("user-b"));
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(
@ -360,7 +365,7 @@ public class TransformationTest {
// second reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(group.withResource("user-c"));
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
Transformation.of(
@ -378,11 +383,11 @@ public class TransformationTest {
}
@Test
public void inReplyTo() {
public void inReplyTo() throws XmppStringprepException {
final var m1 = new Message();
m1.setId("1");
m1.setTo(ACCOUNT);
m1.setFrom(REMOTE.withResource("junit"));
m1.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m1.addExtension(new Body("Hi. How are you?"));
this.transformer.transform(Transformation.of(m1, Instant.now(), REMOTE, "stanza-a", null));
@ -411,18 +416,18 @@ public class TransformationTest {
}
@Test
public void messageWithReceipt() {
public void messageWithReceipt() throws XmppStringprepException {
final var m1 = new Message();
m1.setId("1");
m1.setTo(REMOTE);
m1.setFrom(ACCOUNT.withResource("junit"));
m1.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m1.addExtension(new Body("Hi. How are you?"));
this.transformer.transform(Transformation.of(m1, Instant.now(), REMOTE, null, null));
final var m2 = new Message();
m2.setTo(ACCOUNT.withResource("junit"));
m2.setFrom(REMOTE.withResource("junit"));
m2.setTo(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m2.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m2.addExtension(new Received()).setId("1");
this.transformer.transform(Transformation.of(m2, Instant.now(), REMOTE, null, null));
@ -434,10 +439,10 @@ public class TransformationTest {
}
@Test
public void messageAndRetraction() {
public void messageAndRetraction() throws XmppStringprepException {
final var m1 = new Message();
m1.setTo(ACCOUNT);
m1.setFrom(REMOTE.withResource("junit"));
m1.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m1.setId("m1");
m1.addExtension(new Body("It is raining outside"));
@ -445,7 +450,7 @@ public class TransformationTest {
final var m2 = new Message();
m2.setTo(ACCOUNT);
m2.setFrom(REMOTE.withResource("junit"));
m2.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m2.addExtension(new Retract()).setId("m1");
this.transformer.transform(Transformation.of(m2, Instant.now(), REMOTE, null, null));

View file

@ -10,6 +10,12 @@ public class IDs {
private static final long UUID_VERSION_MASK = 4 << 12;
public static String huge() {
final var random = new byte[96];
Conversations.SECURE_RANDOM.nextBytes(random);
return BaseEncoding.base64Url().encode(random);
}
public static String medium() {
final var random = new byte[9];
Conversations.SECURE_RANDOM.nextBytes(random);

View file

@ -1,9 +1,13 @@
package im.conversations.android.database;
import androidx.room.TypeConverter;
import eu.siacs.conversations.xmpp.Jid;
import com.google.common.base.Strings;
import java.io.IOException;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
@ -26,13 +30,35 @@ public final class Converters {
}
@TypeConverter
public static Jid toJid(final String input) {
return input == null ? null : Jid.ofEscaped(input);
public static BareJid toBareJid(final String input) {
return input == null ? null : JidCreate.bareFromOrThrowUnchecked(input);
}
@TypeConverter
public static String fromBareJid(final BareJid jid) {
return jid == null ? null : jid.toString();
}
@TypeConverter
public static String fromJid(final Jid jid) {
return jid == null ? null : jid.toEscapedString();
return jid == null ? null : jid.toString();
}
@TypeConverter
public static Jid toJid(final String input) {
return input == null ? null : JidCreate.fromOrThrowUnchecked(input);
}
@TypeConverter
public static Resourcepart toResourcePart(final String input) {
return Strings.isNullOrEmpty(input)
? Resourcepart.EMPTY
: Resourcepart.fromOrThrowUnchecked(input);
}
@TypeConverter
public static String fromResourcePart(final Resourcepart resourcepart) {
return resourcepart == null ? null : resourcepart.toString();
}
@TypeConverter

View file

@ -159,14 +159,14 @@ public class CredentialStore {
private Credential getOrEmpty(final Account account) {
final Map<String, Credential> store = loadOrEmpty();
final Credential credential = store.get(account.address.toEscapedString());
final Credential credential = store.get(account.address.toString());
return credential == null ? Credential.empty() : credential;
}
private void set(@NonNull final Account account, @NonNull final Credential credential)
throws GeneralSecurityException, IOException {
final HashMap<String, Credential> credentialStore = new HashMap<>(loadOrEmpty());
credentialStore.put(account.address.toEscapedString(), credential);
credentialStore.put(account.address.toString(), credential);
store(credentialStore);
}

View file

@ -5,11 +5,11 @@ import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.google.common.util.concurrent.ListenableFuture;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Connection;
import java.util.List;
import org.jxmpp.jid.BareJid;
@Dao
public interface AccountDao {
@ -21,7 +21,7 @@ public interface AccountDao {
ListenableFuture<List<Account>> getEnabledAccounts();
@Query("SELECT id,address,randomSeed FROM account WHERE address=:address AND enabled=1")
ListenableFuture<Account> getEnabledAccount(Jid address);
ListenableFuture<Account> getEnabledAccount(BareJid address);
@Query("SELECT id,address,randomSeed FROM account WHERE id=:id AND enabled=1")
ListenableFuture<Account> getEnabledAccount(long id);

View file

@ -4,12 +4,12 @@ import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.AvatarAdditionalEntity;
import im.conversations.android.database.entity.AvatarEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.avatar.Info;
import java.util.Collection;
import org.jxmpp.jid.BareJid;
@Dao
public abstract class AvatarDao {
@ -22,7 +22,7 @@ public abstract class AvatarDao {
public void set(
final Account account,
final Jid address,
final BareJid address,
final Info thumbnail,
final Collection<Info> additional) {
final long id = insert(AvatarEntity.of(account, address, thumbnail));

View file

@ -6,7 +6,6 @@ import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.AxolotlDeviceListEntity;
import im.conversations.android.database.entity.AxolotlDeviceListItemEntity;
import im.conversations.android.database.entity.AxolotlIdentityEntity;
@ -19,6 +18,7 @@ import im.conversations.android.xmpp.model.error.Condition;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.ecc.Curve;
@ -36,7 +36,7 @@ public abstract class AxolotlDao {
protected abstract void insert(Collection<AxolotlDeviceListItemEntity> entities);
@Transaction
public void setDeviceList(Account account, Jid from, Set<Integer> deviceIds) {
public void setDeviceList(Account account, BareJid from, Set<Integer> deviceIds) {
final var listId = insert(AxolotlDeviceListEntity.of(account.id, from));
insert(
Collections2.transform(
@ -47,15 +47,17 @@ public abstract class AxolotlDao {
"SELECT EXISTS(SELECT deviceId FROM axolotl_device_list JOIN axolotl_device_list_item"
+ " ON axolotl_device_list.id=axolotl_device_list_item.deviceListId WHERE"
+ " accountId=:account AND address=:address AND deviceId=:deviceId)")
public abstract boolean hasDeviceId(final long account, final Jid address, final int deviceId);
public abstract boolean hasDeviceId(
final long account, final BareJid address, final int deviceId);
@Transaction
public void setDeviceListError(final Account account, final Jid address, Condition condition) {
public void setDeviceListError(
final Account account, final BareJid address, Condition condition) {
insert(AxolotlDeviceListEntity.of(account.id, address, condition.getName()));
}
@Transaction
public void setDeviceListParsingError(final Account account, final Jid address) {
public void setDeviceListParsingError(final Account account, final BareJid address) {
insert(AxolotlDeviceListEntity.ofParsingIssue(account.id, address));
}
@ -96,7 +98,7 @@ public abstract class AxolotlDao {
@Transaction
public boolean setIdentity(
Account account, Jid address, int deviceId, IdentityKey identityKey) {
Account account, BareJid address, int deviceId, IdentityKey identityKey) {
final var existing = getIdentityKey(account.id, address, deviceId);
if (existing == null || !existing.equals(identityKey)) {
insert(AxolotlIdentityEntity.of(account, address, deviceId, identityKey));
@ -112,7 +114,7 @@ public abstract class AxolotlDao {
@Query(
"SELECT identityKey FROM AXOLOTL_IDENTITY WHERE accountId=:account AND"
+ " address=:address AND deviceId=:deviceId")
protected abstract IdentityKey getIdentityKey(long account, Jid address, int deviceId);
protected abstract IdentityKey getIdentityKey(long account, BareJid address, int deviceId);
@Query(
"SELECT preKeyRecord FROM axolotl_pre_key WHERE accountId=:account AND"
@ -165,24 +167,25 @@ public abstract class AxolotlDao {
@Query(
"SELECT sessionRecord FROM axolotl_session WHERE accountId=:account AND"
+ " address=:address AND deviceId=:deviceId")
public abstract SessionRecord getSessionRecord(long account, Jid address, int deviceId);
public abstract SessionRecord getSessionRecord(long account, BareJid address, int deviceId);
@Query("SELECT deviceId FROM axolotl_session WHERE accountId=:account AND address=:address")
public abstract List<Integer> getSessionDeviceIds(long account, String address);
public void setSessionRecord(Account account, Jid address, int deviceId, SessionRecord record) {
public void setSessionRecord(
Account account, BareJid address, int deviceId, SessionRecord record) {
insert(AxolotlSessionEntity.of(account, address, deviceId, record));
}
@Query(
"SELECT EXISTS(SELECT id FROM axolotl_session WHERE accountId=:account AND"
+ " address=:address AND deviceId=:deviceId)")
public abstract boolean hasSession(long account, Jid address, int deviceId);
public abstract boolean hasSession(long account, BareJid address, int deviceId);
@Query(
"DELETE FROM axolotl_session WHERE accountId=:account AND address=:address AND"
+ " deviceId=:deviceId")
public abstract void deleteSession(long account, Jid address, int deviceId);
public abstract void deleteSession(long account, BareJid address, int deviceId);
@Query("DELETE FROM axolotl_session WHERE accountId=:account AND address=:address")
public abstract void deleteSessions(long account, String address);

View file

@ -6,11 +6,11 @@ import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.BlockedItemEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.blocking.Item;
import java.util.Collection;
import org.jxmpp.jid.Jid;
@Dao
public abstract class BlockingDao {

View file

@ -5,13 +5,14 @@ import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.BookmarkEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.bookmark.Conference;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
@Dao
public abstract class BookmarkDao {
@ -28,7 +29,7 @@ public abstract class BookmarkDao {
@Transaction
public void updateItems(final Account account, Map<String, Conference> items) {
final Collection<Jid> addresses =
Collections2.transform(items.keySet(), BookmarkEntity::jidOrNull);
Collections2.transform(items.keySet(), JidCreate::fromOrNull);
delete(account.id, addresses);
final var entities =
Collections2.transform(

View file

@ -4,13 +4,13 @@ import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.ChatEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.ChatIdentifier;
import im.conversations.android.database.model.ChatType;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Arrays;
import org.jxmpp.jid.Jid;
@Dao
public abstract class ChatDao {
@ -38,7 +38,7 @@ public abstract class ChatDao {
// TODO do not create entity for 'error'
final var entity = new ChatEntity();
entity.accountId = account.id;
entity.address = address.toEscapedString();
entity.address = address.toString();
entity.type = chatType;
entity.archived = true;
final long id = insert(entity);

View file

@ -8,7 +8,6 @@ import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.DiscoEntity;
import im.conversations.android.database.entity.DiscoExtensionEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldEntity;
@ -27,6 +26,8 @@ import im.conversations.android.xmpp.model.disco.info.Identity;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import im.conversations.android.xmpp.model.disco.items.Item;
import java.util.Collection;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
@Dao
public abstract class DiscoDao {
@ -56,7 +57,7 @@ public abstract class DiscoDao {
"UPDATE presence SET discoId=:discoId WHERE accountId=:account AND address=:address"
+ " AND resource=:resource")
protected abstract void updateDiscoIdInPresence(
long account, Jid address, String resource, long discoId);
long account, Jid address, Resourcepart resource, long discoId);
@Query(
"UPDATE disco_item SET discoId=:discoId WHERE accountId=:account AND address=:address"
@ -131,7 +132,7 @@ public abstract class DiscoDao {
updateDiscoIdInPresence(
account,
entity.address.asBareJid(),
Strings.nullToEmpty(entity.address.getResource()),
entity.address.getResourceOrEmpty(),
discoId);
}
}

View file

@ -9,7 +9,6 @@ import androidx.room.Update;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.MessageContentEntity;
import im.conversations.android.database.entity.MessageEntity;
import im.conversations.android.database.entity.MessageReactionEntity;
@ -27,6 +26,9 @@ import im.conversations.android.xmpp.model.reactions.Reactions;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Collection;
import java.util.List;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,14 +40,17 @@ public abstract class MessageDao {
@Query(
"UPDATE message SET acknowledged=1 WHERE messageId=:messageId AND toBare=:toBare AND"
+ " toResource=NULL AND chatId IN (SELECT id FROM chat WHERE accountId=:account)")
abstract int acknowledge(long account, String messageId, final String toBare);
abstract int acknowledge(long account, String messageId, final BareJid toBare);
@Query(
"UPDATE message SET acknowledged=1 WHERE messageId=:messageId AND toBare=:toBare AND"
+ " toResource=:toResource AND chatId IN (SELECT id FROM chat WHERE"
+ " accountId=:account)")
abstract int acknowledge(
long account, final String messageId, final String toBare, final String toResource);
long account,
final String messageId,
final BareJid toBare,
final Resourcepart toResource);
public boolean acknowledge(
final Account account, @NonNull final String messageId, @NonNull final Jid to) {
@ -54,12 +59,10 @@ public abstract class MessageDao {
public boolean acknowledge(
final long account, @NonNull final String messageId, @NonNull final Jid to) {
if (to.isBareJid()) {
return acknowledge(account, messageId, to.toEscapedString()) > 0;
if (to.hasResource()) {
return acknowledge(account, messageId, to.asBareJid(), to.getResourceOrThrow()) > 0;
} else {
return acknowledge(
account, messageId, to.asBareJid().toEscapedString(), to.getResource())
> 0;
return acknowledge(account, messageId, to.asBareJid()) > 0;
}
}
@ -192,7 +195,7 @@ public abstract class MessageDao {
+ " stanzaId=:stanzaId AND (stanzaIdVerified=1 OR latestVersion IS NULL)) OR"
+ " (stanzaId IS NULL AND messageId=:messageId AND latestVersion IS NULL))")
abstract MessageIdentifier get(
long chatId, Jid fromBare, String occupantId, String stanzaId, String messageId);
long chatId, BareJid fromBare, String occupantId, String stanzaId, String messageId);
public MessageIdentifier getOrCreateVersion(
ChatIdentifier chat,
@ -257,13 +260,13 @@ public abstract class MessageDao {
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND"
+ " (occupantId=:occupantId OR occupantId IS NULL) AND messageId=:messageId")
abstract MessageIdentifier getByOccupantIdAndMessageId(
long chatId, Jid fromBare, String occupantId, String messageId);
long chatId, BareJid fromBare, String occupantId, String messageId);
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND"
+ " messageId=:messageId")
abstract MessageIdentifier getByMessageId(long chatId, Jid fromBare, String messageId);
abstract MessageIdentifier getByMessageId(long chatId, BareJid fromBare, String messageId);
@Query(
"SELECT id FROM message_version WHERE messageEntityId=:messageEntityId ORDER BY (CASE"
@ -393,7 +396,7 @@ public abstract class MessageDao {
@Query(
"DELETE FROM message_reaction WHERE messageEntityId=:messageEntityId AND"
+ " reactionBy=:fromBare")
protected abstract void deleteReactionsByFromBare(long messageEntityId, Jid fromBare);
protected abstract void deleteReactionsByFromBare(long messageEntityId, BareJid fromBare);
@Transaction
@Query(
@ -436,7 +439,7 @@ public abstract class MessageDao {
@Query(
"SELECT id FROM message WHERE chatId=:chatId AND fromBare=:fromBare AND"
+ " messageId=:messageId")
protected abstract Long getMessageByMessageId(long chatId, Jid fromBare, String messageId);
protected abstract Long getMessageByMessageId(long chatId, BareJid fromBare, String messageId);
@Query("SELECT id FROM message WHERE chatId=:chatId AND stanzaId=:stanzaId")
protected abstract Long getMessageByStanzaId(long chatId, String stanzaId);

View file

@ -3,9 +3,9 @@ package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.NickEntity;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.Jid;
@Dao
public abstract class NickDao {

View file

@ -6,12 +6,13 @@ import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.PresenceEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType;
import java.util.Arrays;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Dao
public abstract class PresenceDao {
@ -20,29 +21,29 @@ public abstract class PresenceDao {
public abstract void deletePresences(long account);
@Query("DELETE FROM presence WHERE accountId=:account AND address=:address")
abstract void deletePresences(long account, Jid address);
abstract void deletePresences(long account, BareJid address);
@Query(
"DELETE FROM presence WHERE accountId=:account AND address=:address AND"
+ " resource=:resource")
abstract void deletePresence(long account, Jid address, String resource);
abstract void deletePresence(long account, BareJid address, Resourcepart resource);
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract void insert(PresenceEntity entity);
public void set(
@NonNull final Account account,
@NonNull final Jid address,
@Nullable final String resource,
@NonNull final BareJid address,
@NonNull final Resourcepart resource,
@Nullable final PresenceType type,
@Nullable final PresenceShow show,
@Nullable final String status) {
if (resource == null
if (resource.equals(Resourcepart.EMPTY)
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
deletePresences(account.id, address);
}
if (type == PresenceType.UNAVAILABLE) {
if (resource != null) {
if (!resource.equals(Resourcepart.EMPTY)) {
deletePresence(account.id, address, resource);
}
// unavailable presence only delete previous nothing left to do

View file

@ -7,12 +7,12 @@ import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.database.entity.RosterItemGroupEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.roster.Item;
import java.util.Collection;
import org.jxmpp.jid.Jid;
@Dao
public abstract class RosterDao {

View file

@ -5,9 +5,10 @@ import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.Connection;
import im.conversations.android.database.model.Proxy;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "account",
@ -21,8 +22,8 @@ public class AccountEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Jid address;
public String resource;
@NonNull public BareJid address;
public Resourcepart resource;
public byte[] randomSeed;
public boolean enabled;

View file

@ -6,10 +6,10 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.AvatarThumbnail;
import im.conversations.android.xmpp.model.avatar.Info;
import org.jxmpp.jid.BareJid;
@Entity(
tableName = "avatar",
@ -31,13 +31,13 @@ public class AvatarEntity {
@NonNull public Long accountId;
@NonNull public Jid address;
@NonNull public BareJid address;
@Embedded(prefix = "thumb_")
@NonNull
public AvatarThumbnail thumbnail;
public static AvatarEntity of(final Account account, final Jid address, final Info info) {
public static AvatarEntity of(final Account account, final BareJid address, final Info info) {
final var entity = new AvatarEntity();
entity.accountId = account.id;
entity.address = address;

View file

@ -5,8 +5,8 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
@Entity(
tableName = "axolotl_device_list",
@ -28,7 +28,7 @@ public class AxolotlDeviceListEntity {
@NonNull public Long accountId;
@NonNull public Jid address;
@NonNull public BareJid address;
@NonNull public Instant receivedAt;
@ -36,7 +36,7 @@ public class AxolotlDeviceListEntity {
public boolean isParsingIssue;
public static AxolotlDeviceListEntity of(long accountId, final Jid address) {
public static AxolotlDeviceListEntity of(long accountId, final BareJid address) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = accountId;
entity.address = address;
@ -46,7 +46,7 @@ public class AxolotlDeviceListEntity {
}
public static AxolotlDeviceListEntity of(
final long accountId, final Jid address, final String errorCondition) {
final long accountId, final BareJid address, final String errorCondition) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = accountId;
entity.address = address;
@ -55,7 +55,7 @@ public class AxolotlDeviceListEntity {
return entity;
}
public static AxolotlDeviceListEntity ofParsingIssue(final long account, Jid address) {
public static AxolotlDeviceListEntity ofParsingIssue(final long account, BareJid address) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = account;
entity.address = address;

View file

@ -5,8 +5,8 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.IdentityKey;
@Entity(
@ -29,14 +29,14 @@ public class AxolotlIdentityEntity {
@NonNull public Long accountId;
@NonNull public Jid address;
@NonNull public BareJid address;
@NonNull public Integer deviceId;
@NonNull public IdentityKey identityKey;
public static AxolotlIdentityEntity of(
Account account, Jid address, int deviceId, IdentityKey identityKey) {
Account account, BareJid address, int deviceId, IdentityKey identityKey) {
final var entity = new AxolotlIdentityEntity();
entity.accountId = account.id;
entity.address = address;

View file

@ -5,8 +5,8 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.state.SessionRecord;
@Entity(
@ -29,14 +29,14 @@ public class AxolotlSessionEntity {
@NonNull public Long accountId;
@NonNull public Jid address;
@NonNull public BareJid address;
@NonNull public Integer deviceId;
@NonNull public SessionRecord sessionRecord;
public static AxolotlSessionEntity of(
Account account, Jid address, int deviceId, SessionRecord record) {
Account account, BareJid address, int deviceId, SessionRecord record) {
final var entity = new AxolotlSessionEntity();
entity.accountId = account.id;
entity.address = address;

View file

@ -5,7 +5,7 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "blocked",

View file

@ -5,9 +5,10 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.bookmark.Conference;
import java.util.Map;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
@Entity(
tableName = "bookmark",
@ -41,7 +42,7 @@ public class BookmarkEntity {
public static BookmarkEntity of(
final long accountId, final Map.Entry<String, Conference> entry) {
final var address = jidOrNull(entry.getKey());
final var address = JidCreate.fromOrNull(entry.getKey());
final var conference = entry.getValue();
if (address == null) {
return null;
@ -53,12 +54,4 @@ public class BookmarkEntity {
entity.name = conference.getConferenceName();
return entity;
}
public static Jid jidOrNull(final String address) {
try {
return address == null ? null : Jid.ofEscaped(address);
} catch (final IllegalArgumentException e) {
return null;
}
}
}

View file

@ -6,8 +6,8 @@ import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.disco.items.Item;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "disco_item",
@ -57,7 +57,7 @@ public class DiscoItemEntity {
entity.accountId = accountId;
entity.address = item.getJid();
entity.node = Strings.nullToEmpty(item.getNode());
entity.parentAddress = parent.toEscapedString();
entity.parentAddress = parent.toString();
entity.parentNode = Strings.nullToEmpty(parentNode);
return entity;
}

View file

@ -6,10 +6,11 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.transformer.Transformation;
import java.time.Instant;
import java.util.Objects;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "message",
@ -47,10 +48,10 @@ public class MessageEntity {
public boolean outgoing;
public Jid toBare;
public String toResource;
public Jid fromBare;
public String fromResource;
public BareJid toBare;
public Resourcepart toResource;
public BareJid fromBare;
public Resourcepart fromResource;
public String occupantId;

View file

@ -5,9 +5,10 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.transformer.Transformation;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "message_reaction",
@ -27,8 +28,8 @@ public class MessageReactionEntity {
public String stanzaId;
public String messageId;
public Jid reactionBy;
public String reactionByResource;
public BareJid reactionBy;
public Resourcepart reactionByResource;
public String occupantId;
public Instant receivedAt;

View file

@ -6,9 +6,10 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.MessageState;
import im.conversations.android.database.model.StateType;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "message_state",
@ -26,9 +27,9 @@ public class MessageStateEntity {
@NonNull public Long messageVersionId;
@NonNull public Jid fromBare;
@NonNull public BareJid fromBare;
@Nullable public String fromResource;
@Nullable public Resourcepart fromResource;
@NonNull public StateType type;

View file

@ -6,11 +6,12 @@ import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.Modification;
import im.conversations.android.transformer.Transformation;
import im.conversations.android.xmpp.model.stanza.Message;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "message_version",
@ -31,8 +32,8 @@ public class MessageVersionEntity {
public String messageId;
public String stanzaId;
public Modification modification;
public Jid modifiedBy;
public String modifiedByResource;
public BareJid modifiedBy;
public Resourcepart modifiedByResource;
public String occupantId;
public Instant receivedAt;

View file

@ -5,7 +5,7 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "nick",

View file

@ -6,11 +6,12 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "presence",
@ -39,9 +40,9 @@ public class PresenceEntity {
@NonNull public Long accountId;
@NonNull public Jid address;
@NonNull public BareJid address;
@NonNull public String resource;
@NonNull public Resourcepart resource;
@Nullable public PresenceType type;
@ -66,15 +67,15 @@ public class PresenceEntity {
public static PresenceEntity of(
long account,
Jid address,
@Nullable String resource,
@NonNull BareJid address,
@NonNull Resourcepart resource,
PresenceType type,
PresenceShow show,
String status) {
final var entity = new PresenceEntity();
entity.accountId = account;
entity.address = address;
entity.resource = Strings.nullToEmpty(resource);
entity.resource = resource;
entity.type = type;
entity.show = show;
entity.status = status;

View file

@ -5,8 +5,8 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.roster.Item;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "roster",

View file

@ -6,20 +6,19 @@ import com.google.common.base.Preconditions;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
import com.google.common.primitives.Ints;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.IDs;
import java.io.IOException;
import java.util.UUID;
import org.jxmpp.jid.BareJid;
public class Account {
public final long id;
@NonNull public final Jid address;
@NonNull public final BareJid address;
@NonNull public final byte[] randomSeed;
public Account(final long id, @NonNull final Jid address, @NonNull byte[] randomSeed) {
public Account(final long id, @NonNull final BareJid address, @NonNull byte[] randomSeed) {
Preconditions.checkNotNull(address, "Account can not be instantiated without an address");
Preconditions.checkArgument(address.isBareJid(), "Account address must be bare");
Preconditions.checkArgument(
randomSeed.length == 32, "RandomSeed must have exactly 32 bytes");
this.id = id;
@ -43,7 +42,7 @@ public class Account {
}
public boolean isOnion() {
final String domain = address.getDomain().toEscapedString();
final String domain = address.getDomain().toString();
return domain.endsWith(".onion");
}

View file

@ -1,6 +1,6 @@
package im.conversations.android.database.model;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.Jid;
public class ChatIdentifier {

View file

@ -1,10 +1,10 @@
package im.conversations.android.database.model;
import androidx.room.Relation;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.MessageContentEntity;
import java.time.Instant;
import java.util.List;
import org.jxmpp.jid.Jid;
public class MessageEmbedded {

View file

@ -1,17 +1,17 @@
package im.conversations.android.database.model;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.BareJid;
public class MessageIdentifier {
public final long id;
public final String stanzaId;
public final String messageId;
public final Jid fromBare;
public final BareJid fromBare;
public final Long version;
public MessageIdentifier(
long id, String stanzaId, String messageId, Jid fromBare, Long version) {
long id, String stanzaId, String messageId, BareJid fromBare, Long version) {
this.id = id;
this.stanzaId = stanzaId;
this.messageId = messageId;

View file

@ -1,6 +1,6 @@
package im.conversations.android.database.model;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.Jid;
public class MessageReaction {

View file

@ -1,18 +1,19 @@
package im.conversations.android.database.model;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.transformer.Transformation;
import im.conversations.android.xmpp.model.error.Condition;
import im.conversations.android.xmpp.model.error.Error;
import im.conversations.android.xmpp.model.error.Text;
import im.conversations.android.xmpp.model.stanza.Message;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
public class MessageState {
public final Jid fromBare;
public final BareJid fromBare;
public final String fromResource;
public final Resourcepart fromResource;
public final StateType type;
@ -21,8 +22,8 @@ public class MessageState {
public final String errorText;
public MessageState(
Jid fromBare,
String fromResource,
BareJid fromBare,
Resourcepart fromResource,
StateType type,
String errorCondition,
String errorText) {

View file

@ -4,7 +4,6 @@ import androidx.room.Relation;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.MessageContentEntity;
import im.conversations.android.database.entity.MessageEntity;
import im.conversations.android.database.entity.MessageReactionEntity;
@ -14,6 +13,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jxmpp.jid.Jid;
public class MessageWithContentReactions {

View file

@ -6,7 +6,6 @@ import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.IDs;
import im.conversations.android.database.CredentialStore;
import im.conversations.android.database.entity.AccountEntity;
@ -14,6 +13,7 @@ import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.ConnectionPool;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.RegistrationManager;
import org.jxmpp.jid.BareJid;
public class AccountRepository extends AbstractRepository {
@ -22,9 +22,7 @@ public class AccountRepository extends AbstractRepository {
}
private Account createAccount(
@NonNull final Jid address, final String password, final boolean loginAndBind) {
Preconditions.checkArgument(
address.isBareJid(), "Account should be specified without resource");
@NonNull final BareJid address, final String password, final boolean loginAndBind) {
Preconditions.checkArgument(password != null, "Missing password");
final byte[] randomSeed = IDs.seed();
final var entity = new AccountEntity();
@ -44,12 +42,12 @@ public class AccountRepository extends AbstractRepository {
}
public ListenableFuture<Account> createAccountAsync(
final @NonNull Jid address, final String password, final boolean loginAndBind) {
final @NonNull BareJid address, final String password, final boolean loginAndBind) {
return Futures.submit(() -> createAccount(address, password, loginAndBind), IO_EXECUTOR);
}
public ListenableFuture<Account> createAccountAsync(
final @NonNull Jid address, final String password) {
final @NonNull BareJid address, final String password) {
return createAccountAsync(address, password, true);
}

View file

@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.DeliveryReceipt;
import im.conversations.android.xmpp.model.DeliveryReceiptRequest;
import im.conversations.android.xmpp.model.Extension;
@ -25,6 +24,9 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
public class Transformation {
@ -83,20 +85,20 @@ public class Transformation {
return this.extensions.size() > 0;
}
public Jid fromBare() {
public BareJid fromBare() {
return from == null ? null : from.asBareJid();
}
public String fromResource() {
return from == null ? null : from.getResource();
public Resourcepart fromResource() {
return from == null ? null : from.getResourceOrNull();
}
public Jid toBare() {
public BareJid toBare() {
return to == null ? null : to.asBareJid();
}
public String toResource() {
return to == null ? null : to.getResource();
public Resourcepart toResource() {
return to == null ? null : to.getResourceOrNull();
}
public Instant sentAt() {

View file

@ -1,13 +1,13 @@
package im.conversations.android.transformer;
import android.content.Context;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.model.occupant.OccupantId;
import im.conversations.android.xmpp.model.stanza.Message;
import java.time.Instant;
import org.jxmpp.jid.Jid;
public class TransformationFactory extends XmppConnection.Delegate {

View file

@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import eu.siacs.conversations.xmpp.InvalidJid;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.ChatIdentifier;
@ -132,7 +131,7 @@ public class Transformer {
final var reply = transformation.getExtension(Reply.class);
if (Objects.nonNull(reply)
&& Objects.nonNull(reply.getId())
&& InvalidJid.isValid(reply.getTo())) {
&& Objects.nonNull(reply.getTo())) {
database.messageDao()
.setInReplyTo(
chat, messageIdentifier, messageType, reply.getTo(), reply.getId());

View file

@ -1,7 +1,6 @@
package im.conversations.android.xml;
import androidx.annotation.NonNull;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@ -9,15 +8,14 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.ExtensionFactory;
import im.conversations.android.xmpp.model.Extension;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
public class Element {
private final String name;
@ -154,7 +152,7 @@ public class Element {
}
public Element setAttribute(final String name, final Jid value) {
return this.setAttribute(name, value == null ? null : value.toEscapedString());
return this.setAttribute(name, value == null ? null : value.toString());
}
public void removeAttribute(final String name) {
@ -187,16 +185,12 @@ public class Element {
return Optional.fromNullable(Ints.tryParse(value));
}
public Jid getAttributeAsJid(String name) {
public Jid getAttributeAsJid(final String name) {
final String jid = this.getAttribute(name);
if (Strings.isNullOrEmpty(jid)) {
return null;
}
try {
return Jid.ofEscaped(jid);
} catch (final IllegalArgumentException e) {
return InvalidJid.of(jid, this instanceof Message);
}
return JidCreate.fromOrThrowUnchecked(jid);
}
public Hashtable<String, String> getAttributes() {

View file

@ -1,10 +1,10 @@
package im.conversations.android.xml;
import eu.siacs.conversations.xmpp.Jid;
import java.util.Hashtable;
import java.util.Map.Entry;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jxmpp.jid.Jid;
public class Tag {
public static final int NO = -1;
@ -52,7 +52,7 @@ public class Tag {
public Tag setAttribute(final String attrName, final Jid attrValue) {
if (attrValue != null) {
this.attributes.put(attrName, attrValue.toEscapedString());
this.attributes.put(attrName, attrValue.toString());
}
return this;
}

View file

@ -12,9 +12,7 @@ import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.Conversations;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.model.Account;
@ -26,6 +24,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -66,7 +66,7 @@ public class ConnectionPool {
return setupXmppConnection(context, account);
}
public synchronized ListenableFuture<XmppConnection> get(final Jid address) {
public synchronized ListenableFuture<XmppConnection> get(final BareJid address) {
final var configured =
Iterables.tryFind(this.connections, c -> address.equals(c.getAccount().address));
if (configured.isPresent()) {
@ -78,8 +78,7 @@ public class ConnectionPool {
if (account == null) {
throw new IllegalStateException(
String.format(
"No enabled account with address %s",
address.toEscapedString()));
"No enabled account with address %s", address.toString()));
}
return reconfigure(account);
},
@ -231,9 +230,8 @@ public class ConnectionPool {
final String androidId = PhoneHelper.getAndroidId(context);
for (final XmppConnection xmppConnection : this.connections) {
final Account account = xmppConnection.getAccount();
final boolean pushWasMeantForThisAccount =
CryptoHelper.getFingerprint(account.address, androidId)
.equals(pushedAccountHash);
// TODO fix me if we bring back FCM push support
final boolean pushWasMeantForThisAccount = false;
if (processAccountState(xmppConnection, pushWasMeantForThisAccount, pingCandidates)) {
pingNow++;
}

View file

@ -1,6 +1,6 @@
package im.conversations.android.xmpp;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.Jid;
public abstract class Entity {

View file

@ -30,9 +30,6 @@ import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.utils.SSLSockets;
import eu.siacs.conversations.utils.SocksSocketFactory;
import eu.siacs.conversations.xml.LocalizedContent;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.bind.Bind2;
import im.conversations.android.Conversations;
import im.conversations.android.IDs;
@ -91,6 +88,7 @@ import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -107,6 +105,9 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.HttpUrl;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParserException;
@ -293,7 +294,7 @@ public class XmppConnection implements Runnable {
final int port;
final boolean directTls;
if (connection == null || account.isOnion()) {
destination = account.address.getDomain().toEscapedString();
destination = account.address.getDomain().toString();
port = 5222;
directTls = false;
} else {
@ -328,7 +329,7 @@ public class XmppConnection implements Runnable {
throw new IOException("Could not start stream", e);
}
} else {
final String domain = account.address.getDomain().toEscapedString();
final String domain = account.address.getDomain().toString();
final List<Resolver.Result> results;
if (connection != null) {
results = Resolver.fromHardCoded(connection.hostname, connection.port);
@ -497,7 +498,7 @@ public class XmppConnection implements Runnable {
final boolean quickStart;
if (socket instanceof SSLSocket) {
final SSLSocket sslSocket = (SSLSocket) socket;
SSLSockets.log(account.address, sslSocket);
logTlsCipher(sslSocket);
quickStart = establishStream(SSLSockets.version(sslSocket));
} else {
quickStart = establishStream(SSLSockets.Version.NONE);
@ -524,7 +525,7 @@ public class XmppConnection implements Runnable {
} else {
keyManager = new KeyManager[] {new MyKeyManager(context, credential)};
}
final String domain = account.address.getDomain().toEscapedString();
final String domain = account.address.getDomain().toString();
// TODO we used to use two different trust managers; interactive and non interactive (to
// trigger SSL cert prompts)
// we need a better solution for this using live data or similar
@ -719,8 +720,8 @@ public class XmppConnection implements Runnable {
authorizationJid =
Strings.isNullOrEmpty(authorizationIdentifier)
? null
: Jid.ofEscaped(authorizationIdentifier);
} catch (final IllegalArgumentException e) {
: JidCreate.from(authorizationIdentifier);
} catch (final XmppStringprepException e) {
Log.d(
Config.LOGTAG,
account.address
@ -993,7 +994,9 @@ public class XmppConnection implements Runnable {
private void changeStatusToOnline() {
Log.d(
Config.LOGTAG,
account.address + ": online with resource " + connectionAddress.getResource());
account.address
+ ": online with resource "
+ connectionAddress.getResourceOrNull());
changeStatus(ConnectionState.ONLINE);
}
@ -1058,7 +1061,7 @@ public class XmppConnection implements Runnable {
final S stanza = tagReader.readElement(currentTag, clazz);
if (stanzasReceived == Integer.MAX_VALUE) {
resetStreamId();
throw new IOException("time to restart the session. cant handle >2 billion pcks");
throw new IOException("time to restart the session. cant handle >2 billion stanzas");
}
if (inSmacksSession) {
++stanzasReceived;
@ -1071,25 +1074,12 @@ public class XmppConnection implements Runnable {
+ "). Not in smacks session.");
}
lastPacketReceived = SystemClock.elapsedRealtime();
if (InvalidJid.invalid(stanza.getTo()) || InvalidJid.invalid(stanza.getFrom())) {
Log.e(
Config.LOGTAG,
"encountered invalid stanza from "
+ stanza.getFrom()
+ " to "
+ stanza.getTo());
}
// TODO validate to and from
return stanza;
}
private void processIq(final Tag currentTag) throws IOException {
final Iq packet = processStanza(currentTag, Iq.class);
if (InvalidJid.invalid(packet.getTo()) || InvalidJid.invalid(packet.getFrom())) {
Log.e(
Config.LOGTAG,
"encountered invalid IQ from " + packet.getFrom() + " to " + packet.getTo());
return;
}
final Consumer<Iq> callback;
synchronized (this.packetCallbacks) {
final Pair<Iq, Consumer<Iq>> packetCallbackDuple = packetCallbacks.get(packet.getId());
@ -1130,29 +1120,11 @@ public class XmppConnection implements Runnable {
private void processMessage(final Tag currentTag) throws IOException {
final var message = processStanza(currentTag, Message.class);
if (InvalidJid.invalid(message.getTo()) || InvalidJid.invalid(message.getFrom())) {
Log.e(
Config.LOGTAG,
"encountered invalid Message from "
+ message.getFrom()
+ " to "
+ message.getTo());
return;
}
this.messagePacketConsumer.accept(message);
}
private void processPresence(final Tag currentTag) throws IOException {
final var presence = processStanza(currentTag, Presence.class);
if (InvalidJid.invalid(presence.getTo()) || InvalidJid.invalid(presence.getFrom())) {
Log.e(
Config.LOGTAG,
"encountered invalid Presence from "
+ presence.getFrom()
+ " to "
+ presence.getTo());
return;
}
this.presencePacketConsumer.accept(presence);
}
@ -1181,7 +1153,7 @@ public class XmppConnection implements Runnable {
this.encryptionEnabled = true;
final Tag tag = tagReader.readTag();
if (tag != null && tag.isStart("stream", Namespace.STREAMS)) {
SSLSockets.log(account.address, sslSocket);
logTlsCipher(sslSocket);
processStream();
} else {
throw new StateChangingException(ConnectionState.STREAM_OPENING_ERROR);
@ -1189,6 +1161,14 @@ public class XmppConnection implements Runnable {
sslSocket.close();
}
private void logTlsCipher(final SSLSocket sslSocket) {
final var session = sslSocket.getSession();
LOGGER.info(
"TLS session protocol {} cipher {}",
session.getProtocol(),
session.getCipherSuite());
}
private SSLSocket upgradeSocketToTls(final Socket socket) throws IOException {
final SSLSocketFactory sslSocketFactory;
try {
@ -1202,13 +1182,12 @@ public class XmppConnection implements Runnable {
sslSocketFactory.createSocket(
socket, address.getHostAddress(), socket.getPort(), true);
SSLSockets.setSecurity(sslSocket);
SSLSockets.setHostname(
sslSocket, IDN.toASCII(account.address.getDomain().toEscapedString()));
SSLSockets.setHostname(sslSocket, IDN.toASCII(account.address.getDomain().toString()));
SSLSockets.setApplicationProtocol(sslSocket, "xmpp-client");
final XmppDomainVerifier xmppDomainVerifier = new XmppDomainVerifier();
try {
if (!xmppDomainVerifier.verify(
account.address.getDomain().toEscapedString(),
account.address.getDomain().toString(),
this.verifiedHostname,
sslSocket.getSession())) {
Log.d(
@ -1536,8 +1515,8 @@ public class XmppConnection implements Runnable {
}
final Jid assignedJid;
try {
assignedJid = Jid.ofEscaped(jid);
} catch (final IllegalArgumentException e) {
assignedJid = JidCreate.from(jid);
} catch (final XmppStringprepException e) {
Log.d(
Config.LOGTAG,
account.address
@ -1675,7 +1654,7 @@ public class XmppConnection implements Runnable {
final var discoManager = getManager(DiscoManager.class);
final var nodeHash = this.streamFeatures.getCapabilities();
final var domainDiscoItem = Entity.discoItem(account.address.getDomain());
final var domainDiscoItem = Entity.discoItem(account.address.asDomainBareJid());
if (nodeHash != null) {
discoFutures.add(
discoManager.infoOrCache(domainDiscoItem, nodeHash.node, nodeHash.hash));
@ -1819,12 +1798,13 @@ public class XmppConnection implements Runnable {
private void sendStartStream(final boolean from, final boolean flush) throws IOException {
final Tag stream = Tag.start("stream:stream");
stream.setAttribute("to", account.address.getDomain());
stream.setAttribute("to", account.address.asDomainBareJid());
if (from) {
stream.setAttribute("from", account.address);
}
stream.setAttribute("version", "1.0");
stream.setAttribute("xml:lang", LocalizedContent.STREAM_LANGUAGE);
// TODO use 'en' when privacy mode is enabled
stream.setAttribute("xml:lang", Locale.getDefault().getLanguage());
stream.setAttribute("xmlns", "jabber:client");
stream.setAttribute("xmlns:stream", Namespace.STREAMS);
tagWriter.writeTag(stream, flush);

View file

@ -1,20 +1,18 @@
package im.conversations.android.xmpp.axolotl;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.Jid;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.SignalProtocolAddress;
public class AxolotlAddress extends SignalProtocolAddress {
private final Jid jid;
private final BareJid jid;
public AxolotlAddress(final Jid jid, int deviceId) {
super(jid.toEscapedString(), deviceId);
Preconditions.checkArgument(jid.isBareJid(), "AxolotlAddresses must use bare JIDs");
public AxolotlAddress(final BareJid jid, int deviceId) {
super(jid.toString(), deviceId);
this.jid = jid;
}
public Jid getJid() {
public BareJid getJid() {
return this.jid;
}

View file

@ -9,7 +9,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.avatar.Data;
import im.conversations.android.xmpp.model.avatar.Info;
@ -21,6 +20,8 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,7 +35,7 @@ public class AvatarManager extends AbstractManager {
super(context, connection);
}
public void handleItems(final Jid from, final Items items) {
public void handleItems(final BareJid from, final Items items) {
final var itemsMap = items.getItemMap(Metadata.class);
final var firstEntry = Iterables.getFirst(itemsMap.entrySet(), null);
if (firstEntry == null) {
@ -142,7 +143,7 @@ public class AvatarManager extends AbstractManager {
new File(
accountCacheDirectory,
Hashing.sha256()
.hashString(address.toEscapedString(), StandardCharsets.UTF_8)
.hashString(address.toString(), StandardCharsets.UTF_8)
.toString());
if (userCacheDirectory.mkdirs()) {
LOGGER.debug("Created directory {}", userCacheDirectory.getAbsolutePath());

View file

@ -7,7 +7,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.AxolotlDatabaseStore;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.IqErrorException;
@ -22,6 +21,8 @@ import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
@ -47,7 +48,7 @@ public class AxolotlManager extends AbstractManager {
this.signalProtocolStore = new AxolotlDatabaseStore(context, connection.getAccount());
}
public void handleItems(final Jid from, final Items items) {
public void handleItems(final BareJid from, final Items items) {
final var deviceList = items.getFirstItem(DeviceList.class);
if (from == null || deviceList == null) {
return;
@ -57,7 +58,7 @@ public class AxolotlManager extends AbstractManager {
getDatabase().axolotlDao().setDeviceList(getAccount(), from, deviceIds);
}
public ListenableFuture<Set<Integer>> fetchDeviceIds(final Jid address) {
public ListenableFuture<Set<Integer>> fetchDeviceIds(final BareJid address) {
final var deviceIdsFuture =
Futures.transform(
getManager(PubSubManager.class)

View file

@ -7,8 +7,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.BookmarkEntity;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.NodeConfiguration;
import im.conversations.android.xmpp.XmppConnection;
@ -18,6 +16,8 @@ import im.conversations.android.xmpp.model.pubsub.event.Retract;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -53,7 +53,7 @@ public class BookmarkManager extends AbstractManager {
private void deleteItems(Collection<Retract> retractions) {
final Collection<Jid> addresses =
Collections2.transform(retractions, r -> BookmarkEntity.jidOrNull(r.getId()));
Collections2.transform(retractions, r -> JidCreate.fromOrNull(r.getId()));
getDatabase()
.bookmarkDao()
.delete(getAccount().id, Collections2.filter(addresses, Objects::nonNull));
@ -75,7 +75,7 @@ public class BookmarkManager extends AbstractManager {
}
public ListenableFuture<Void> publishBookmark(final Jid address) {
final var itemId = address.toEscapedString();
final var itemId = address.toString();
final var conference = new Conference();
return Futures.transform(
getManager(PepManager.class)
@ -85,7 +85,7 @@ public class BookmarkManager extends AbstractManager {
}
public ListenableFuture<Void> retractBookmark(final Jid address) {
final var itemId = address.toEscapedString();
final var itemId = address.toString();
return Futures.transform(
getManager(PepManager.class).retract(itemId, Namespace.BOOKMARKS2),
result -> null,

View file

@ -1,9 +1,9 @@
package im.conversations.android.xmpp.manager;
import android.content.Context;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.state.ChatStateNotification;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -13,7 +13,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities;
@ -32,6 +31,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -249,7 +249,7 @@ public class DiscoManager extends AbstractManager {
}
public boolean hasServerFeature(final String feature) {
return hasFeature(getAccount().address.getDomain(), feature);
return hasFeature(getAccount().address.asDomainBareJid(), feature);
}
public ServiceDescription getServiceDescription() {

View file

@ -2,10 +2,10 @@ package im.conversations.android.xmpp.manager;
import android.content.Context;
import com.google.common.base.Strings;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.nick.Nick;
import im.conversations.android.xmpp.model.pubsub.Items;
import org.jxmpp.jid.BareJid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -17,7 +17,7 @@ public class NickManager extends AbstractManager {
super(context, connection);
}
public void handleItems(final Jid from, Items items) {
public void handleItems(final BareJid from, Items items) {
final var item = items.getFirstItem(Nick.class);
final var nick = item == null ? null : item.getContent();
if (from == null || Strings.isNullOrEmpty(nick)) {

View file

@ -2,12 +2,12 @@ package im.conversations.android.xmpp.manager;
import android.content.Context;
import com.google.common.util.concurrent.ListenableFuture;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.NodeConfiguration;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.Extension;
import im.conversations.android.xmpp.model.stanza.Iq;
import java.util.Map;
import org.jxmpp.jid.Jid;
public class PepManager extends AbstractManager {

View file

@ -6,7 +6,6 @@ import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.ExtensionFactory;
import im.conversations.android.xmpp.IqErrorException;
@ -29,6 +28,7 @@ import im.conversations.android.xmpp.model.pubsub.owner.PubSubOwner;
import im.conversations.android.xmpp.model.stanza.Iq;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Map;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -147,6 +147,7 @@ public class PubSubManager extends AbstractManager {
private void handleItems(final Message message) {
final var from = message.getFrom();
final var bareFrom = from == null ? null : from.asBareJid();
final var event = message.getExtension(Event.class);
final Items items = event.getItems();
final var node = items.getNode();
@ -155,15 +156,15 @@ public class PubSubManager extends AbstractManager {
return;
}
if (Namespace.AVATAR_METADATA.equals(node)) {
getManager(AvatarManager.class).handleItems(from, items);
getManager(AvatarManager.class).handleItems(bareFrom, items);
return;
}
if (Namespace.NICK.equals(node)) {
getManager(NickManager.class).handleItems(from, items);
getManager(NickManager.class).handleItems(bareFrom, items);
return;
}
if (Namespace.AXOLOTL_DEVICE_LIST.equals(node)) {
getManager(AxolotlManager.class).handleItems(from, items);
getManager(AxolotlManager.class).handleItems(bareFrom, items);
}
}

View file

@ -2,7 +2,6 @@ package im.conversations.android.xmpp.manager;
import android.content.Context;
import com.google.common.base.Strings;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.DeliveryReceiptRequest;
import im.conversations.android.xmpp.model.markers.Markable;
@ -10,6 +9,7 @@ import im.conversations.android.xmpp.model.receipts.Received;
import im.conversations.android.xmpp.model.receipts.Request;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Collection;
import org.jxmpp.jid.Jid;
public class ReceiptManager extends AbstractManager {

View file

@ -34,9 +34,9 @@ public class RegistrationManager extends AbstractManager {
public ListenableFuture<Void> setPassword(final String password) {
final var account = getAccount();
final var iq = new Iq(Iq.Type.SET);
iq.setTo(account.address.getDomain());
iq.setTo(account.address.asDomainBareJid());
final var register = iq.addExtension(new Register());
register.addUsername(account.address.getEscapedLocal());
register.addUsername(account.address.getLocalpartOrThrow());
register.addPassword(password);
return Futures.transform(
connection.sendIqPacket(iq), r -> null, MoreExecutors.directExecutor());
@ -44,7 +44,7 @@ public class RegistrationManager extends AbstractManager {
public ListenableFuture<Void> unregister() {
final var iq = new Iq(Iq.Type.SET);
iq.setTo(getAccount().address.getDomain());
iq.setTo(getAccount().address.asDomainBareJid());
final var register = iq.addExtension(new Register());
register.addExtension(new Remove());
return Futures.transform(
@ -53,7 +53,7 @@ public class RegistrationManager extends AbstractManager {
public ListenableFuture<Registration> getRegistration() {
final var iq = new Iq(Iq.Type.GET);
iq.setTo(getAccount().address.getDomain());
iq.setTo(getAccount().address.asDomainBareJid());
iq.addExtension(new Register());
return Futures.transform(
connection.sendIqPacketUnbound(iq),
@ -98,7 +98,7 @@ public class RegistrationManager extends AbstractManager {
public ListenableFuture<Void> sendPreAuthentication(final String token) {
final var iq = new Iq(Iq.Type.GET);
iq.setTo(getAccount().address.getDomain());
iq.setTo(getAccount().address.asDomainBareJid());
final var preAuthentication = iq.addExtension(new PreAuth());
preAuthentication.setToken(token);
return Futures.transform(

View file

@ -1,11 +1,11 @@
package im.conversations.android.xmpp.manager;
import android.content.Context;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.stanza.Message;
import im.conversations.android.xmpp.model.unique.StanzaId;
import org.jxmpp.jid.Jid;
public class StanzaIdManager extends AbstractManager {

View file

@ -1,8 +1,8 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
import org.jxmpp.jid.Jid;
@XmlElement
public class Item extends Extension {

View file

@ -1,9 +1,9 @@
package im.conversations.android.xmpp.model.disco.items;
import androidx.annotation.Nullable;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
import org.jxmpp.jid.Jid;
@XmlElement
public class Item extends Extension {

View file

@ -2,6 +2,7 @@ package im.conversations.android.xmpp.model.register;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
import org.jxmpp.jid.parts.Localpart;
@XmlElement(name = "query")
public class Register extends Extension {
@ -10,8 +11,8 @@ public class Register extends Extension {
super(Register.class);
}
public void addUsername(final String username) {
this.addExtension(new Username()).setContent(username);
public void addUsername(final Localpart username) {
this.addExtension(new Username()).setContent(username.toString());
}
public void addPassword(final String password) {

View file

@ -1,9 +1,9 @@
package im.conversations.android.xmpp.model.reply;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.model.Extension;
import org.jxmpp.jid.Jid;
@XmlElement(namespace = Namespace.REPLY)
public class Reply extends Extension {

View file

@ -1,7 +1,6 @@
package im.conversations.android.xmpp.model.roster;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xml.Element;
import im.conversations.android.xmpp.model.Extension;
@ -10,6 +9,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import org.jxmpp.jid.Jid;
@XmlElement
public class Item extends Extension {

View file

@ -1,9 +1,9 @@
package im.conversations.android.xmpp.model.stanza;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.Extension;
import im.conversations.android.xmpp.model.StreamElement;
import im.conversations.android.xmpp.model.error.Error;
import org.jxmpp.jid.Jid;
public abstract class Stanza extends StreamElement {

View file

@ -1,8 +1,8 @@
package im.conversations.android.xmpp.model.unique;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
import org.jxmpp.jid.Jid;
@XmlElement
public class StanzaId extends Extension {

View file

@ -1,7 +1,6 @@
package im.conversations.android.xmpp.processor;
import android.content.Context;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.XmppConnection;
@ -12,6 +11,7 @@ import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.manager.RosterManager;
import java.util.function.Consumer;
import org.jxmpp.jid.Jid;
public class BindProcessor extends XmppConnection.Delegate implements Consumer<Jid> {
@ -35,7 +35,8 @@ public class BindProcessor extends XmppConnection.Delegate implements Consumer<J
}
if (discoManager.hasServerFeature(Namespace.COMMANDS)) {
discoManager.items(Entity.discoItem(account.address.getDomain()), Namespace.COMMANDS);
discoManager.items(
Entity.discoItem(account.address.asDomainBareJid()), Namespace.COMMANDS);
}
getManager(BookmarkManager.class).fetch();

View file

@ -1,9 +1,9 @@
package im.conversations.android.xmpp.processor;
import android.content.Context;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.XmppConnection;
import java.util.function.BiFunction;
import org.jxmpp.jid.Jid;
public class MessageAcknowledgeProcessor extends XmppConnection.Delegate
implements BiFunction<Jid, String, Boolean> {

View file

@ -19,7 +19,7 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
public void accept(final Presence presencePacket) {
final var from = presencePacket.getFrom();
final var address = from == null ? null : from.asBareJid();
final var resource = from == null ? null : from.getResource();
final var resource = from == null ? null : from.getResourceOrEmpty();
final var typeAttribute = presencePacket.getAttribute("type");
final PresenceType type;
try {

View file

@ -51,7 +51,7 @@ public class DigestMd5 extends SaslMechanism {
final String digestUri = "xmpp/" + account.address.getDomain();
final String nonceCount = "00000001";
final String x =
account.address.getEscapedLocal()
account.address.getLocalpartOrNull().toString()
+ ":"
+ account.address.getDomain()
+ ":"
@ -60,7 +60,7 @@ public class DigestMd5 extends SaslMechanism {
final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
final String cNonce = CryptoHelper.random(100);
final byte[] a1 =
CryptoHelper.concatenateByteArrays(
concatenate(
y,
(":" + nonce + ":" + cNonce)
.getBytes(Charset.defaultCharset()));
@ -76,7 +76,7 @@ public class DigestMd5 extends SaslMechanism {
md.digest(kd.getBytes(Charset.defaultCharset())));
final String saslString =
"username=\""
+ account.address.getEscapedLocal()
+ account.address.getLocalpartOrThrow().toString()
+ "\",realm=\""
+ account.address.getDomain()
+ "\",nonce=\""

View file

@ -25,6 +25,6 @@ public class External extends SaslMechanism {
@Override
public String getClientFirstMessage(final SSLSocket sslSocket) {
return Base64.encodeToString(account.address.toEscapedString().getBytes(), Base64.NO_WRAP);
return Base64.encodeToString(account.address.toString().getBytes(), Base64.NO_WRAP);
}
}

View file

@ -49,9 +49,10 @@ public abstract class HashedToken extends SaslMechanism implements ChannelBindin
final byte[] cbData = getChannelBindingData(sslSocket);
final byte[] initiatorHashedToken =
hashing.hashBytes(Bytes.concat(INITIATOR, cbData)).asBytes();
final String username = account.address.getLocalpartOrThrow().toString();
final byte[] firstMessage =
Bytes.concat(
account.address.getEscapedLocal().getBytes(StandardCharsets.UTF_8),
username.getBytes(StandardCharsets.UTF_8),
new byte[] {0x00},
initiatorHashedToken);
return Base64.encodeToString(firstMessage, Base64.NO_WRAP);

View file

@ -33,6 +33,7 @@ public class Plain extends SaslMechanism {
@Override
public String getClientFirstMessage(final SSLSocket sslSocket) {
return getMessage(
account.address.getEscapedLocal(), Strings.nullToEmpty(credential.password));
account.address.getLocalpartOrThrow().toString(),
Strings.nullToEmpty(credential.password));
}
}

View file

@ -233,4 +233,11 @@ public abstract class SaslMechanism {
public static boolean pin(final SaslMechanism saslMechanism) {
return !hashedToken(saslMechanism);
}
protected static byte[] concatenate(byte[] a, byte[] b) {
byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
}

View file

@ -7,11 +7,12 @@ import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.hash.HashFunction;
import eu.siacs.conversations.utils.CryptoHelper;
import im.conversations.android.IDs;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Credential;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.text.Normalizer;
import java.util.concurrent.ExecutionException;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLSocket;
@ -36,6 +37,8 @@ abstract class ScramMechanism extends SaslMechanism {
}
};
private static final byte[] ONE = new byte[] {0, 0, 0, 1};
private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
private static final Cache<CacheKey, KeyPair> CACHE =
@ -67,7 +70,7 @@ abstract class ScramMechanism extends SaslMechanism {
.convert(channelBinding.toString()));
}
// This nonce should be different for each authentication attempt.
this.clientNonce = CryptoHelper.random(100);
this.clientNonce = IDs.huge();
clientFirstMessageBare = "";
}
@ -107,7 +110,7 @@ abstract class ScramMechanism extends SaslMechanism {
*/
private byte[] hi(final byte[] key, final byte[] salt, final int iterations)
throws InvalidKeyException {
byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
byte[] u = hmac(key, concatenate(salt, ONE));
byte[] out = u.clone();
for (int i = 1; i < iterations; i++) {
u = hmac(key, u);
@ -123,8 +126,7 @@ abstract class ScramMechanism extends SaslMechanism {
if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
clientFirstMessageBare =
"n="
+ CryptoHelper.saslEscape(
CryptoHelper.saslPrep(account.address.getEscapedLocal()))
+ escape(prep(account.address.getLocalpartOrThrow().toString()))
+ ",r="
+ this.clientNonce;
state = State.AUTH_TEXT_SENT;
@ -216,7 +218,7 @@ abstract class ScramMechanism extends SaslMechanism {
try {
keys =
getKeyPair(
CryptoHelper.saslPrep(Strings.nullToEmpty(credential.password)),
prep(Strings.nullToEmpty(credential.password)),
salt,
iterationCount);
} catch (ExecutionException e) {
@ -269,6 +271,29 @@ abstract class ScramMechanism extends SaslMechanism {
}
}
public static String escape(final String s) {
final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case ',':
sb.append("=2C");
break;
case '=':
sb.append("=3D");
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
public static String prep(final String s) {
return Normalizer.normalize(s, Normalizer.Form.NFKC);
}
protected byte[] getChannelBindingData(final SSLSocket sslSocket)
throws AuthenticationException {
if (this.channelBinding == ChannelBinding.NONE) {