retrieve blocklist on bind

This commit is contained in:
Daniel Gultsch 2023-01-15 10:11:49 +01:00
parent 6b232f7a5a
commit 20962554a4
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
14 changed files with 198 additions and 31 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "d16845c3eb73e5fdbc9902903b74428a",
"identityHash": "aa5e73a1cf9ba959e118f66b89b9d227",
"entities": [
{
"tableName": "account",
@ -1066,7 +1066,7 @@
},
{
"tableName": "roster",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `subscription` TEXT, `ask` INTEGER NOT NULL, `name` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `subscription` TEXT, `isPendingOut` INTEGER NOT NULL, `name` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
@ -1093,8 +1093,8 @@
"notNull": false
},
{
"fieldPath": "ask",
"columnName": "ask",
"fieldPath": "isPendingOut",
"columnName": "isPendingOut",
"affinity": "INTEGER",
"notNull": true
},
@ -1195,7 +1195,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd16845c3eb73e5fdbc9902903b74428a')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'aa5e73a1cf9ba959e118f66b89b9d227')"
]
}
}

View file

@ -67,6 +67,10 @@ public class Element {
return null;
}
public <E extends Extension> boolean hasExtension(final Class<E> clazz) {
return Iterables.any(this.children, clazz::isInstance);
}
public <E extends Extension> E getExtension(final Class<E> clazz) {
final var extension = Iterables.find(this.children, clazz::isInstance);
if (extension == null) {

View file

@ -6,6 +6,7 @@ import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import im.conversations.android.database.dao.AccountDao;
import im.conversations.android.database.dao.BlockingDao;
import im.conversations.android.database.dao.MessageDao;
import im.conversations.android.database.dao.PresenceDao;
import im.conversations.android.database.dao.RosterDao;
@ -74,4 +75,6 @@ public abstract class ConversationsDatabase extends RoomDatabase {
public abstract MessageDao messageDao();
public abstract RosterDao rosterDao();
public abstract BlockingDao blockingDao();
}

View file

@ -0,0 +1,29 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
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;
@Dao
public abstract class BlockingDao {
@Insert
abstract void insert(Collection<BlockedItemEntity> entities);
@Query("DELETE FROM blocked WHERE accountId=:account")
abstract void clear(final long account);
@Transaction
public void setBlocklist(final Account account, final Collection<Item> blockedItems) {
final var entities =
Collections2.transform(blockedItems, i -> BlockedItemEntity.of(account.id, i));
clear(account.id);
insert(entities);
}
}

View file

@ -1,32 +1,56 @@
package im.conversations.android.database.dao;
import static androidx.room.OnConflictStrategy.REPLACE;
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.RosterItemEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.roster.Item;
import java.util.Collection;
@Dao
public abstract class RosterDao {
@Insert
protected abstract void insert(Collection<RosterItemEntity> rosterItems);
@Insert(onConflict = REPLACE)
protected abstract long insert(RosterItemEntity rosterItem);
@Query("DELETE FROM roster WHERE accountId=:account")
protected abstract void clear(final long account);
@Query("DELETE FROM roster WHERE accountId=:account AND address=:address")
protected abstract void delete(final long account, final Jid address);
@Query("UPDATE account SET rosterVersion=:version WHERE id=:account")
protected abstract void setRosterVersion(final long account, final String version);
@Transaction
public void setRoster(
final Account account,
final String version,
final Collection<RosterItemEntity> rosterItems) {
public void set(
final Account account, final String version, final Collection<Item> rosterItems) {
clear(account.id);
insert(rosterItems);
for (final Item item : rosterItems) {
final long id = insert(RosterItemEntity.of(account.id, item));
// TODO insert groups
}
setRosterVersion(account.id, version);
}
public void update(
final Account account, final String version, final Collection<Item> updates) {
for (final Item item : updates) {
final Item.Subscription subscription = item.getSubscription();
if (subscription == null) {
continue;
}
if (subscription == Item.Subscription.REMOVE) {
delete(account.id, item.getJid());
}
final RosterItemEntity entity = RosterItemEntity.of(account.id, item);
final long id = insert(entity);
}
setRosterVersion(account.id, version);
}
}

View file

@ -5,6 +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.blocking.Item;
@Entity(
tableName = "blocked",
@ -26,5 +28,12 @@ public class BlockedItemEntity {
@NonNull public Long accountId;
@NonNull public String address;
@NonNull public Jid address;
public static BlockedItemEntity of(final long accountId, final Item item) {
final var entity = new BlockedItemEntity();
entity.accountId = accountId;
entity.address = item.getJid();
return entity;
}
}

View file

@ -5,10 +5,8 @@ import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.roster.Item;
import java.util.Collection;
@Entity(
tableName = "roster",
@ -47,9 +45,4 @@ public class RosterItemEntity {
entity.name = item.getItemName();
return entity;
}
public static Collection<RosterItemEntity> of(
final long accountId, final Collection<Item> items) {
return Collections2.transform(items, i -> of(accountId, i));
}
}

View file

@ -7,8 +7,6 @@ import eu.siacs.conversations.xml.Element;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.annotation.XmlPackage;
import im.conversations.android.xmpp.model.Extension;
import im.conversations.android.xmpp.model.roster.Item;
import im.conversations.android.xmpp.model.roster.Query;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
@ -18,8 +16,15 @@ import java.util.Map;
public final class Extensions {
// TODO these two maps can easily be generated by an annotation processor
private static final List<Class<? extends Extension>> ELEMENTS =
Arrays.asList(Query.class, Item.class);
Arrays.asList(
im.conversations.android.xmpp.model.roster.Query.class,
im.conversations.android.xmpp.model.roster.Item.class,
im.conversations.android.xmpp.model.blocking.Item.class,
im.conversations.android.xmpp.model.blocking.Block.class,
im.conversations.android.xmpp.model.blocking.Blocklist.class,
im.conversations.android.xmpp.model.blocking.Unblock.class);
private static final Map<Id, Class<? extends Extension>> EXTENSION_CLASS_MAP;

View file

@ -0,0 +1,13 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Block extends Extension {
public Block() {
super("block", Namespace.BLOCKING);
}
}

View file

@ -0,0 +1,12 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Blocklist extends Extension {
public Blocklist() {
super("blocklist", Namespace.BLOCKING);
}
}

View file

@ -0,0 +1,18 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Item extends Extension {
public Item() {
super("item", Namespace.BLOCKING);
}
public Jid getJid() {
return getAttributeAsJid("jid");
}
}

View file

@ -0,0 +1,13 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Unblock extends Extension {
public Unblock() {
super("unblock", Namespace.BLOCKING);
}
}

View file

@ -7,10 +7,11 @@ import com.google.common.collect.Collections2;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.blocking.Blocklist;
import im.conversations.android.xmpp.model.roster.Item;
import im.conversations.android.xmpp.model.roster.Query;
import java.util.Objects;
import java.util.function.Consumer;
public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid> {
@ -36,6 +37,9 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
fetchRoster();
// TODO check feature
fetchBlocklist();
// TODO fetch bookmarks
// TODO send initial presence
@ -61,7 +65,7 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
if (result.getType() != IqPacket.TYPE.RESULT) {
return;
}
final Query query = result.getExtension(Query.class);
final var query = result.getExtension(Query.class);
if (query == null) {
// No query in result means further modifications are sent via pushes
return;
@ -75,8 +79,30 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
final var validItems =
Collections2.filter(
items,
i -> i != null && Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription()));
final var entities = RosterItemEntity.of(account.id, validItems);
database.rosterDao().setRoster(account, version, entities);
i ->
Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription())
&& Objects.nonNull(i.getJid()));
database.rosterDao().set(account, version, validItems);
}
private void fetchBlocklist() {
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
iqPacket.addChild(new Blocklist());
connection.sendIqPacket(iqPacket, this::handleFetchBlocklistResult);
}
private void handleFetchBlocklistResult(final IqPacket result) {
if (result.getType() != IqPacket.TYPE.RESULT) {
return;
}
final var blocklist = result.getExtension(Blocklist.class);
if (blocklist == null) {
return;
}
final var account = getAccount();
final var items =
blocklist.getExtensions(im.conversations.android.xmpp.model.blocking.Item.class);
final var filteredItems = Collections2.filter(items, i -> Objects.nonNull(i.getJid()));
getDatabase().blockingDao().setBlocklist(account, filteredItems);
}
}

View file

@ -1,14 +1,32 @@
package im.conversations.android.xmpp.processor;
import android.content.Context;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.roster.Query;
import java.util.Arrays;
import java.util.function.Consumer;
public class IqProcessor implements Consumer<IqPacket> {
public class IqProcessor extends AbstractBaseProcessor implements Consumer<IqPacket> {
public IqProcessor(final Context context, final XmppConnection connection) {}
public IqProcessor(final Context context, final XmppConnection connection) {
super(context, connection);
}
@Override
public void accept(final IqPacket packet) {}
public void accept(final IqPacket packet) {
final IqPacket.TYPE type = packet.getType();
Preconditions.checkArgument(
Arrays.asList(IqPacket.TYPE.GET, IqPacket.TYPE.SET).contains(type));
if (type == IqPacket.TYPE.SET
&& connection.fromAccount(packet)
&& packet.hasExtension(Query.class)) {
handleRosterPush(packet.getExtension(Query.class));
}
}
private void handleRosterPush(final Query query) {
final String version = query.getVersion();
}
}