Try all gateway translations options

Stephen Paul Weber created

Send jabber:iq:gateway if we're responding to that and fetch translated
response as the JID.  Otherwise use JID escaping if supported.
Otherwise fall back to the dumb ancient % escaping.

Change summary

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 27 
src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java              | 94 
src/main/java/eu/siacs/conversations/xmpp/OnGatewayResult.java           |  4 
3 files changed, 76 insertions(+), 49 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -145,7 +145,7 @@ import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnBindListener;
 import eu.siacs.conversations.xmpp.OnContactStatusChanged;
-import eu.siacs.conversations.xmpp.OnGatewayPromptResult;
+import eu.siacs.conversations.xmpp.OnGatewayResult;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
 import eu.siacs.conversations.xmpp.OnMessageAcknowledged;
@@ -4656,19 +4656,20 @@ public class XmppConnectionService extends Service {
         }
     }
 
-    public void fetchGatewayPrompt(Account account, final Jid jid, final OnGatewayPromptResult callback) {
-        IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+    public void fetchFromGateway(Account account, final Jid jid, final String input, final OnGatewayResult callback) {
+        IqPacket request = new IqPacket(input == null ? IqPacket.TYPE.GET : IqPacket.TYPE.SET);
         request.setTo(jid);
-        request.query("jabber:iq:gateway");
-        sendIqPacket(account, request, new OnIqPacketReceived() {
-            @Override
-            public void onIqPacketReceived(Account account, IqPacket packet) {
-                if (packet.getType() == IqPacket.TYPE.RESULT) {
-                    callback.onGatewayPromptResult(packet.query().findChildContent("prompt"), null);
-                } else {
-                    Element error = packet.findChild("error");
-                    callback.onGatewayPromptResult(null, error == null ? null : error.findChildContent("text"));
-                }
+        Element query = request.query("jabber:iq:gateway");
+        if (input != null) {
+            Element prompt = query.addChild("prompt");
+            prompt.setContent(input);
+        }
+        sendIqPacket(account, request, (Account acct, IqPacket packet) -> {
+            if (packet.getType() == IqPacket.TYPE.RESULT) {
+                callback.onGatewayResult(packet.query().findChildContent(input == null ? "prompt" : "jid"), null);
+            } else {
+                Element error = packet.findChild("error");
+                callback.onGatewayResult(null, error == null ? null : error.findChildContent("text"));
             }
         });
     }

src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java 🔗

@@ -43,7 +43,7 @@ import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 import eu.siacs.conversations.ui.util.DelayedHintHelper;
 import eu.siacs.conversations.xmpp.Jid;
-import eu.siacs.conversations.xmpp.OnGatewayPromptResult;
+import eu.siacs.conversations.xmpp.OnGatewayResult;
 
 public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
 
@@ -161,7 +161,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
 
                 for (final Contact contact : account.getRoster().getContacts()) {
                     if (contact.showInRoster() && (contact.getPresences().anyIdentity("gateway", null) || contact.getPresences().anySupport("jabber:iq:gateway"))) {
-                        context.xmppConnectionService.fetchGatewayPrompt(account, contact.getJid(), (final String prompt, String errorMessage) -> {
+                        context.xmppConnectionService.fetchFromGateway(account, contact.getJid(), null, (final String prompt, String errorMessage) -> {
                             if (prompt == null) return;
 
                             context.runOnUiThread(() -> {
@@ -216,41 +216,67 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
             return;
         }
         final Jid accountJid = accountJid();
-        final Jid contactJid;
-        try {
-            contactJid = Jid.ofEscaped(binding.jid.getText().toString());
-        } catch (final IllegalArgumentException e) {
-            binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
-            return;
-        }
+        final OnGatewayResult finish = (final String jidString, final String errorMessage) -> {
+            getActivity().runOnUiThread(() -> {
+                if (errorMessage != null) {
+                    binding.jidLayout.setError(errorMessage);
+                    return;
+                }
+                if (jidString == null) {
+                    binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
+                    return;
+                }
 
-        if (!issuedWarning && sanityCheckJid) {
-            if (contactJid.isDomainJid()) {
-                binding.jidLayout.setError(
-                        getActivity().getString(R.string.this_looks_like_a_domain));
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
-                issuedWarning = true;
-                return;
-            }
-            if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
-                binding.jidLayout.setError(
-                        getActivity().getString(R.string.this_looks_like_channel));
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
-                issuedWarning = true;
-                return;
-            }
-        }
+                final Jid contactJid;
+                try {
+                    contactJid = Jid.ofEscaped(jidString);
+                } catch (final IllegalArgumentException e) {
+                    binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
+                    return;
+                }
 
-        if (mListener != null) {
-            try {
-                if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
-                    dialog.dismiss();
+                if (!issuedWarning && sanityCheckJid) {
+                    if (contactJid.isDomainJid()) {
+                        binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
+                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
+                        issuedWarning = true;
+                        return;
+                    }
+                    if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
+                        binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
+                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
+                        issuedWarning = true;
+                        return;
+                    }
                 }
-            } catch (JidError error) {
-                binding.jidLayout.setError(error.toString());
-                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
-                issuedWarning = false;
-            }
+
+                if (mListener != null) {
+                    try {
+                        if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
+                            dialog.dismiss();
+                        }
+                    } catch (JidError error) {
+                        binding.jidLayout.setError(error.toString());
+                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
+                        issuedWarning = false;
+                    }
+                }
+            });
+        };
+
+        Pair<String,Pair<Jid,Presence>> p = gatewayListAdapter.getSelected();
+
+        if (p == null) {
+            finish.onGatewayResult(binding.jid.getText().toString(), null);
+        } else if (p.first != null) { // Gateway already responsed to jabber:iq:gateway once
+            final Account acct = ((XmppActivity) getActivity()).xmppConnectionService.findAccountByJid(accountJid);
+            ((XmppActivity) getActivity()).xmppConnectionService.fetchFromGateway(acct, p.second.first, binding.jid.getText().toString(), finish);
+        } else if (p.second.first.isDomainJid() && p.second.second.getServiceDiscoveryResult().getFeatures().contains("jid\\20escaping")) {
+            finish.onGatewayResult(Jid.ofLocalAndDomain(binding.jid.getText().toString(), p.second.first.getDomain().toString()).toString(), null);
+        } else if (p.second.first.isDomainJid()) {
+            finish.onGatewayResult(Jid.ofLocalAndDomain(binding.jid.getText().toString().replace("@", "%"), p.second.first.getDomain().toString()).toString(), null);
+        } else {
+            finish.onGatewayResult(null, null);
         }
     }
 

src/main/java/eu/siacs/conversations/xmpp/OnGatewayPromptResult.java → src/main/java/eu/siacs/conversations/xmpp/OnGatewayResult.java 🔗

@@ -1,7 +1,7 @@
 package eu.siacs.conversations.xmpp;
 
-public interface OnGatewayPromptResult {
+public interface OnGatewayResult {
    // if prompt is null, there was an error
    // errorText may or may not be set
-   public void onGatewayPromptResult(String prompt, String errorText);
+   public void onGatewayResult(String prompt, String errorText);
 }