From ad75ab3f683391c26c2a99242210747c0bf881c2 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 3 Nov 2019 19:41:16 +0100 Subject: optionally search local muc rooms instead of jabber.network --- .../pixart/messenger/services/AvatarService.java | 15 +- .../services/ChannelDiscoveryService.java | 172 ++++++++++++++++----- .../messenger/services/XmppConnectionService.java | 13 +- 3 files changed, 149 insertions(+), 51 deletions(-) (limited to 'src/main/java/de/pixart/messenger/services') diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java index 469de2599..0ae6f7099 100644 --- a/src/main/java/de/pixart/messenger/services/AvatarService.java +++ b/src/main/java/de/pixart/messenger/services/AvatarService.java @@ -13,14 +13,15 @@ import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import androidx.annotation.ColorInt; -import androidx.annotation.Nullable; -import androidx.core.content.res.ResourcesCompat; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.util.LruCache; +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.core.content.res.ResourcesCompat; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -39,7 +40,7 @@ import de.pixart.messenger.entities.ListItem; import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.RawBlockable; -import de.pixart.messenger.http.services.MuclumbusService; +import de.pixart.messenger.entities.Room; import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded; import de.pixart.messenger.xmpp.XmppConnection; @@ -84,13 +85,13 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return get((ListItem) avatarable, size, cachedOnly); } else if (avatarable instanceof MucOptions.User) { return get((MucOptions.User) avatarable, size, cachedOnly); - } else if (avatarable instanceof MuclumbusService.Room) { - return get((MuclumbusService.Room) avatarable, size, cachedOnly); + } else if (avatarable instanceof Room) { + return get((Room) avatarable, size, cachedOnly); } throw new AssertionError("AvatarService does not know how to generate avatar from " + avatarable.getClass().getName()); } - private Bitmap get(final MuclumbusService.Room result, final int size, boolean cacheOnly) { + private Bitmap get(final Room result, final int size, boolean cacheOnly) { final Jid room = result.getRoom(); Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null; if (conversation != null) { diff --git a/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java b/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java index bd92c886d..81668cb92 100644 --- a/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java +++ b/src/main/java/de/pixart/messenger/services/ChannelDiscoveryService.java @@ -1,39 +1,48 @@ package de.pixart.messenger.services; -import androidx.annotation.NonNull; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import org.jetbrains.annotations.NotNull; - import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import de.pixart.messenger.Config; +import de.pixart.messenger.entities.Account; +import de.pixart.messenger.entities.Room; import de.pixart.messenger.http.HttpConnectionManager; import de.pixart.messenger.http.services.MuclumbusService; -import okhttp3.Interceptor; +import de.pixart.messenger.parser.IqParser; +import de.pixart.messenger.xmpp.OnIqPacketReceived; +import de.pixart.messenger.xmpp.XmppConnection; +import de.pixart.messenger.xmpp.stanzas.IqPacket; import okhttp3.OkHttpClient; -import okhttp3.Request; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; +import rocks.xmpp.addr.Jid; public class ChannelDiscoveryService { private final XmppConnectionService service; + private MuclumbusService muclumbusService; - private final Cache> cache; + private final Cache> cache; ChannelDiscoveryService(XmppConnectionService service) { this.service = service; @@ -42,6 +51,7 @@ public class ChannelDiscoveryService { void initializeMuclumbusService() { final OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (service.useTorToConnect()) { try { builder.proxy(HttpConnectionManager.getProxy()); @@ -49,11 +59,6 @@ public class ChannelDiscoveryService { throw new RuntimeException("Unable to use Tor proxy", e); } } - try { - builder.networkInterceptors().add(new UserAgentInterceptor(service.getIqGenerator().getUserAgent())); - } catch (Exception e) { - e.printStackTrace(); - } Retrofit retrofit = new Retrofit.Builder() .client(builder.build()) .baseUrl(Config.CHANNEL_DISCOVERY) @@ -63,21 +68,28 @@ public class ChannelDiscoveryService { this.muclumbusService = retrofit.create(MuclumbusService.class); } - void discover(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { - final boolean all = query == null || query.trim().isEmpty(); - List result = cache.getIfPresent(all ? "" : query); + void cleanCache() { + cache.invalidateAll(); + } + + void discover(@NonNull final String query, Method method, OnChannelSearchResultsFound onChannelSearchResultsFound) { + List result = cache.getIfPresent(key(method, query)); if (result != null) { onChannelSearchResultsFound.onChannelSearchResultsFound(result); return; } - if (all) { - discoverChannels(onChannelSearchResultsFound); + if (method == Method.LOCAL_SERVER) { + discoverChannelsLocalServers(query, onChannelSearchResultsFound); } else { - discoverChannels(query, onChannelSearchResultsFound); + if (query.isEmpty()) { + discoverChannelsJabberNetwork(onChannelSearchResultsFound); + } else { + discoverChannelsJabberNetwork(query, onChannelSearchResultsFound); + } } } - private void discoverChannels(OnChannelSearchResultsFound listener) { + private void discoverChannelsJabberNetwork(OnChannelSearchResultsFound listener) { Call call = muclumbusService.getRooms(1); try { call.enqueue(new Callback() { @@ -89,7 +101,7 @@ public class ChannelDiscoveryService { logError(response); return; } - cache.put("", body.items); + cache.put(key(Method.JABBER_NETWORK, ""), body.items); listener.onChannelSearchResultsFound(body.items); } @@ -104,9 +116,10 @@ public class ChannelDiscoveryService { } } - private void discoverChannels(final String query, OnChannelSearchResultsFound listener) { + private void discoverChannelsJabberNetwork(final String query, OnChannelSearchResultsFound listener) { MuclumbusService.SearchRequest searchRequest = new MuclumbusService.SearchRequest(query); Call searchResultCall = muclumbusService.search(searchRequest); + searchResultCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { @@ -116,7 +129,7 @@ public class ChannelDiscoveryService { logError(response); return; } - cache.put(query, body.result.items); + cache.put(key(Method.JABBER_NETWORK, query), body.result.items); listener.onChannelSearchResultsFound(body.result.items); } @@ -128,6 +141,102 @@ public class ChannelDiscoveryService { }); } + private void discoverChannelsLocalServers(final String query, final OnChannelSearchResultsFound listener) { + final Map localMucService = getLocalMucServices(); + Log.d(Config.LOGTAG, "checking with " + localMucService.size() + " muc services"); + if (localMucService.size() == 0) { + listener.onChannelSearchResultsFound(Collections.emptyList()); + return; + } + if (!query.isEmpty()) { + final List cached = cache.getIfPresent(key(Method.LOCAL_SERVER, "")); + if (cached != null) { + final List results = copyMatching(cached, query); + cache.put(key(Method.LOCAL_SERVER, query), results); + listener.onChannelSearchResultsFound(results); + } + } + final AtomicInteger queriesInFlight = new AtomicInteger(); + final List rooms = new ArrayList<>(); + for (Map.Entry entry : localMucService.entrySet()) { + IqPacket itemsRequest = service.getIqGenerator().queryDiscoItems(entry.getKey()); + queriesInFlight.incrementAndGet(); + service.sendIqPacket(entry.getValue(), itemsRequest, (account, itemsResponse) -> { + if (itemsResponse.getType() == IqPacket.TYPE.RESULT) { + final List items = IqParser.items(itemsResponse); + for (Jid item : items) { + IqPacket infoRequest = service.getIqGenerator().queryDiscoInfo(item); + queriesInFlight.incrementAndGet(); + service.sendIqPacket(account, infoRequest, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket infoResponse) { + if (infoResponse.getType() == IqPacket.TYPE.RESULT) { + final Room room = IqParser.parseRoom(infoResponse); + if (room != null) { + rooms.add(room); + } + if (queriesInFlight.decrementAndGet() <= 0) { + finishDiscoSearch(rooms, query, listener); + } + } else { + queriesInFlight.decrementAndGet(); + } + } + }); + } + } + if (queriesInFlight.decrementAndGet() <= 0) { + finishDiscoSearch(rooms, query, listener); + } + }); + } + } + + private void finishDiscoSearch(List rooms, String query, OnChannelSearchResultsFound listener) { + Collections.sort(rooms); + cache.put(key(Method.LOCAL_SERVER, ""), rooms); + if (query.isEmpty()) { + listener.onChannelSearchResultsFound(rooms); + } else { + List results = copyMatching(rooms, query); + cache.put(key(Method.LOCAL_SERVER, query), results); + listener.onChannelSearchResultsFound(rooms); + } + } + + private static List copyMatching(List haystack, String needle) { + ArrayList result = new ArrayList<>(); + for (Room room : haystack) { + if (room.contains(needle)) { + result.add(room); + } + } + return result; + } + + private Map getLocalMucServices() { + final HashMap localMucServices = new HashMap<>(); + for (Account account : service.getAccounts()) { + if (account.isEnabled()) { + final XmppConnection xmppConnection = account.getXmppConnection(); + if (xmppConnection == null) { + continue; + } + for (final String mucService : xmppConnection.getMucServers()) { + Jid jid = Jid.of(mucService); + if (!localMucServices.containsKey(jid)) { + localMucServices.put(jid, account); + } + } + } + } + return localMucServices; + } + + private static String key(Method method, String query) { + return String.format("%s\00%s", method, query); + } + private static void logError(final Response response) { final ResponseBody errorBody = response.errorBody(); Log.d(Config.LOGTAG, "code from muclumbus=" + response.code()); @@ -142,24 +251,11 @@ public class ChannelDiscoveryService { } public interface OnChannelSearchResultsFound { - void onChannelSearchResultsFound(List results); + void onChannelSearchResultsFound(List results); } - private class UserAgentInterceptor implements Interceptor { - private final String userAgent; - - UserAgentInterceptor(String userAgent) { - this.userAgent = userAgent; - } - - @NotNull - @Override - public okhttp3.Response intercept(Chain chain) throws IOException { - Request originalRequest = chain.request(); - Request requestWithUserAgent = originalRequest.newBuilder() - .header("User-Agent", userAgent) - .build(); - return chain.proceed(requestWithUserAgent); - } + public enum Method { + JABBER_NETWORK, + LOCAL_SERVER } } \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index e7c587107..5de4c79b9 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -44,6 +44,8 @@ import androidx.annotation.IntegerRes; import androidx.core.app.RemoteInput; import androidx.core.content.ContextCompat; +import com.google.common.base.Strings; + import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionID; @@ -928,8 +930,8 @@ public class XmppConnectionService extends Service { mChannelDiscoveryService.initializeMuclumbusService(); } - public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { - mChannelDiscoveryService.discover(query, onChannelSearchResultsFound); + public void discoverChannels(String query, ChannelDiscoveryService.Method method, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { + mChannelDiscoveryService.discover(Strings.nullToEmpty(query).trim(), method, onChannelSearchResultsFound); } public boolean isDataSaverDisabled() { @@ -2536,6 +2538,7 @@ public class XmppConnectionService extends Service { getNotificationService().updateErrorNotification(); toggleForegroundService(); syncEnabledAccountSetting(); + mChannelDiscoveryService.cleanCache(); return true; } else { return false; @@ -3379,9 +3382,7 @@ public class XmppConnectionService extends Service { } public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) { - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.setTo(conversation.getJid().asBareJid()); - request.query("http://jabber.org/protocol/disco#info"); + IqPacket request = mIqGenerator.queryDiscoInfo(conversation.getJid().asBareJid()); sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { @@ -4831,7 +4832,7 @@ public class XmppConnectionService extends Service { request.setTo(jid); final String node = presence.getNode(); final String ver = presence.getVer(); - final Element query = request.query("http://jabber.org/protocol/disco#info"); + final Element query = request.query(Namespace.DISCO_INFO); if (node != null && ver != null) { query.setAttribute("node", node + "#" + ver); } -- cgit v1.2.3