BlockingManager.java

  1package eu.siacs.conversations.xmpp.manager;
  2
  3import android.util.Log;
  4import androidx.annotation.NonNull;
  5import com.google.common.collect.ImmutableSet;
  6import com.google.common.util.concurrent.FutureCallback;
  7import com.google.common.util.concurrent.Futures;
  8import com.google.common.util.concurrent.MoreExecutors;
  9import eu.siacs.conversations.Config;
 10import eu.siacs.conversations.services.XmppConnectionService;
 11import eu.siacs.conversations.xml.Namespace;
 12import eu.siacs.conversations.xmpp.Jid;
 13import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
 14import eu.siacs.conversations.xmpp.XmppConnection;
 15import im.conversations.android.xmpp.model.blocking.Block;
 16import im.conversations.android.xmpp.model.blocking.Blocklist;
 17import im.conversations.android.xmpp.model.blocking.Item;
 18import im.conversations.android.xmpp.model.blocking.Unblock;
 19import im.conversations.android.xmpp.model.error.Condition;
 20import im.conversations.android.xmpp.model.error.Error;
 21import im.conversations.android.xmpp.model.stanza.Iq;
 22import java.util.Collection;
 23import java.util.HashSet;
 24import java.util.Set;
 25
 26public class BlockingManager extends AbstractManager {
 27
 28    private final XmppConnectionService service;
 29
 30    private final HashSet<Jid> blocklist = new HashSet<>();
 31
 32    public BlockingManager(final XmppConnectionService service, final XmppConnection connection) {
 33        super(service, connection);
 34        // TODO find a way to get rid of XmppConnectionService and use context instead
 35        this.service = service;
 36    }
 37
 38    public void request() {
 39        final var future = this.connection.sendIqPacket(new Iq(Iq.Type.GET, new Blocklist()));
 40        Futures.addCallback(
 41                future,
 42                new FutureCallback<>() {
 43                    @Override
 44                    public void onSuccess(final Iq result) {
 45                        final var blocklist = result.getExtension(Blocklist.class);
 46                        if (blocklist == null) {
 47                            Log.d(
 48                                    Config.LOGTAG,
 49                                    getAccount().getJid().asBareJid()
 50                                            + ": invalid blocklist response");
 51                            return;
 52                        }
 53                        final var addresses = itemsAsAddresses(blocklist.getItems());
 54                        Log.d(
 55                                Config.LOGTAG,
 56                                getAccount().getJid().asBareJid()
 57                                        + ": discovered blocklist with "
 58                                        + addresses.size()
 59                                        + " items");
 60                        setBlocklist(addresses);
 61                        removeBlockedConversations(addresses);
 62                        service.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
 63                    }
 64
 65                    @Override
 66                    public void onFailure(@NonNull final Throwable throwable) {
 67                        Log.w(
 68                                Config.LOGTAG,
 69                                getAccount().getJid().asBareJid()
 70                                        + ": could not retrieve blocklist",
 71                                throwable);
 72                    }
 73                },
 74                MoreExecutors.directExecutor());
 75    }
 76
 77    public void pushBlock(final Iq request) {
 78        if (connection.fromServer(request)) {
 79            final var block = request.getExtension(Block.class);
 80            final var addresses = itemsAsAddresses(block.getItems());
 81            synchronized (this.blocklist) {
 82                this.blocklist.addAll(addresses);
 83            }
 84            this.removeBlockedConversations(addresses);
 85            this.service.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
 86            this.connection.sendResultFor(request);
 87        } else {
 88            this.connection.sendErrorFor(request, Error.Type.AUTH, new Condition.Forbidden());
 89        }
 90    }
 91
 92    public void pushUnblock(final Iq request) {
 93        if (connection.fromServer(request)) {
 94            final var unblock = request.getExtension(Unblock.class);
 95            final var address = itemsAsAddresses(unblock.getItems());
 96            synchronized (this.blocklist) {
 97                this.blocklist.removeAll(address);
 98            }
 99            this.service.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
100            this.connection.sendResultFor(request);
101        } else {
102            this.connection.sendErrorFor(request, Error.Type.AUTH, new Condition.Forbidden());
103        }
104    }
105
106    private void removeBlockedConversations(final Collection<Jid> addresses) {
107        var removed = false;
108        for (final Jid address : addresses) {
109            removed |= service.removeBlockedConversations(getAccount(), address);
110        }
111        if (removed) {
112            service.updateConversationUi();
113        }
114    }
115
116    public ImmutableSet<Jid> getBlocklist() {
117        synchronized (this.blocklist) {
118            return ImmutableSet.copyOf(this.blocklist);
119        }
120    }
121
122    private void setBlocklist(final Collection<Jid> addresses) {
123        synchronized (this.blocklist) {
124            this.blocklist.clear();
125            this.blocklist.addAll(addresses);
126        }
127    }
128
129    public boolean hasFeature() {
130        return getManager(DiscoManager.class).hasServerFeature(Namespace.BLOCKING);
131    }
132
133    private static Set<Jid> itemsAsAddresses(final Collection<Item> items) {
134        final var builder = new ImmutableSet.Builder<Jid>();
135        for (final var item : items) {
136            final var jid = Jid.Invalid.getNullForInvalid(item.getJid());
137            if (jid == null) {
138                continue;
139            }
140            builder.add(jid);
141        }
142        return builder.build();
143    }
144}