From 77fc8d2d9e7d375079a506c621638cae873571c5 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 4 May 2018 12:18:31 +0200 Subject: [PATCH] encode and decode % and # in invite links --- .../siacs/conversations/entities/Account.java | 53 +++++++++---------- .../ui/ConferenceDetailsActivity.java | 3 +- .../ui/ContactDetailsActivity.java | 48 ++++++++--------- .../eu/siacs/conversations/utils/XmppUri.java | 43 +++++++++------ 4 files changed, 78 insertions(+), 69 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 83ff9ae51..54cd46056 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -6,9 +6,6 @@ import android.os.SystemClock; import android.util.Log; import android.util.Pair; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.crypto.PgpDecryptionService; - import org.json.JSONException; import org.json.JSONObject; @@ -19,7 +16,9 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.services.XmppConnectionService; @@ -95,7 +94,7 @@ public class Account extends AbstractEntity { public boolean setShowErrorNotification(boolean newValue) { boolean oldValue = showErrorNotification(); - setKey("show_error",Boolean.toString(newValue)); + setKey("show_error", Boolean.toString(newValue)); return newValue != oldValue; } @@ -109,7 +108,7 @@ public class Account extends AbstractEntity { } public enum State { - DISABLED(false,false), + DISABLED(false, false), OFFLINE(false), CONNECTING(false), ONLINE(false), @@ -117,12 +116,12 @@ public class Account extends AbstractEntity { UNAUTHORIZED, SERVER_NOT_FOUND, REGISTRATION_SUCCESSFUL(false), - REGISTRATION_FAILED(true,false), - REGISTRATION_WEB(true,false), - REGISTRATION_CONFLICT(true,false), - REGISTRATION_NOT_SUPPORTED(true,false), - REGISTRATION_PLEASE_WAIT(true,false), - REGISTRATION_PASSWORD_TOO_WEAK(true,false), + REGISTRATION_FAILED(true, false), + REGISTRATION_WEB(true, false), + REGISTRATION_CONFLICT(true, false), + REGISTRATION_NOT_SUPPORTED(true, false), + REGISTRATION_PLEASE_WAIT(true, false), + REGISTRATION_PASSWORD_TOO_WEAK(true, false), TLS_ERROR, INCOMPATIBLE_SERVER, TOR_NOT_AVAILABLE, @@ -148,7 +147,7 @@ public class Account extends AbstractEntity { } State(final boolean isError) { - this(isError,true); + this(isError, true); } State(final boolean isError, final boolean reconnect) { @@ -157,7 +156,7 @@ public class Account extends AbstractEntity { } State() { - this(true,true); + this(true, true); } public int getReadableId() { @@ -254,9 +253,9 @@ public class Account extends AbstractEntity { } private Account(final String uuid, final Jid jid, - final String password, final int options, final String rosterVersion, final String keys, - final String avatar, String displayName, String hostname, int port, - final Presence.Status status, String statusMessage) { + final String password, final int options, final String rosterVersion, final String keys, + final String avatar, String displayName, String hostname, int port, + final Presence.Status status, String statusMessage) { this.uuid = uuid; this.jid = jid; this.password = password; @@ -265,7 +264,7 @@ public class Account extends AbstractEntity { JSONObject tmp; try { tmp = new JSONObject(keys); - } catch(JSONException e) { + } catch (JSONException e) { tmp = new JSONObject(); } this.keys = tmp; @@ -286,7 +285,7 @@ public class Account extends AbstractEntity { cursor.getString(cursor.getColumnIndex(SERVER)), resource == null || resource.trim().isEmpty() ? null : resource); } catch (final IllegalArgumentException ignored) { - Log.d(Config.LOGTAG,cursor.getString(cursor.getColumnIndex(USERNAME))+"@"+cursor.getString(cursor.getColumnIndex(SERVER))); + Log.d(Config.LOGTAG, cursor.getString(cursor.getColumnIndex(USERNAME)) + "@" + cursor.getString(cursor.getColumnIndex(SERVER))); throw new AssertionError(ignored); } return new Account(cursor.getString(cursor.getColumnIndex(UUID)), @@ -480,7 +479,7 @@ public class Account extends AbstractEntity { values.put(PORT, port); values.put(STATUS, presenceStatus.toShowString()); values.put(STATUS_MESSAGE, presenceStatusMessage); - values.put(RESOURCE,jid.getResource()); + values.put(RESOURCE, jid.getResource()); return values; } @@ -584,7 +583,7 @@ public class Account extends AbstractEntity { } public Bookmark getBookmark(final Jid jid) { - for(final Bookmark bookmark : this.bookmarks) { + for (final Bookmark bookmark : this.bookmarks) { if (bookmark.getJid() != null && jid.asBareJid().equals(bookmark.getJid().asBareJid())) { return bookmark; } @@ -619,9 +618,9 @@ public class Account extends AbstractEntity { public String getShareableUri() { List fingerprints = this.getFingerprints(); - String uri = "xmpp:"+this.getJid().asBareJid().toEscapedString(); + String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString(); if (fingerprints.size() > 0) { - return XmppUri.getFingerprintUri(uri,fingerprints,';'); + return XmppUri.getFingerprintUri(uri, fingerprints, ';'); } else { return uri; } @@ -629,9 +628,9 @@ public class Account extends AbstractEntity { public String getShareableLink() { List fingerprints = this.getFingerprints(); - String uri = "https://conversations.im/i/"+this.getJid().asBareJid().toEscapedString(); + String uri = "https://conversations.im/i/" + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString()); if (fingerprints.size() > 0) { - return XmppUri.getFingerprintUri(uri,fingerprints,'&'); + return XmppUri.getFingerprintUri(uri, fingerprints, '&'); } else { return uri; } @@ -642,10 +641,10 @@ public class Account extends AbstractEntity { if (axolotlService == null) { return fingerprints; } - fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,axolotlService.getOwnFingerprint().substring(2),axolotlService.getOwnDeviceId())); - for(XmppAxolotlSession session : axolotlService.findOwnSessions()) { + fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, axolotlService.getOwnFingerprint().substring(2), axolotlService.getOwnDeviceId())); + for (XmppAxolotlSession session : axolotlService.findOwnSessions()) { if (session.getTrust().isVerified() && session.getTrust().isActive()) { - fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,session.getFingerprint().substring(2).replaceAll("\\s",""),session.getRemoteAddress().getDeviceId())); + fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, session.getFingerprint().substring(2).replaceAll("\\s", ""), session.getRemoteAddress().getDeviceId())); } } return fingerprints; diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index 3866e81c5..222c4f57e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -45,6 +45,7 @@ import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdat import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate; import eu.siacs.conversations.ui.util.MenuDoubleTabUtil; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.utils.XmppUri; import rocks.xmpp.addr.Jid; public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed { @@ -298,7 +299,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers protected String getShareableUri(boolean http) { if (mConversation != null) { if (http) { - return "https://conversations.im/j/" + mConversation.getJid().asBareJid().toEscapedString(); + return "https://conversations.im/j/" + XmppUri.lameUrlEncode(mConversation.getJid().asBareJid().toEscapedString()); } else { return "xmpp:" + mConversation.getJid().asBareJid() + "?join"; } diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index bd53b5317..d73676af5 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -10,7 +10,6 @@ import android.preference.PreferenceManager; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; -import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; @@ -63,14 +62,14 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp @Override public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { + boolean isChecked) { if (isChecked) { if (contact .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { xmppConnectionService.sendPresencePacket(contact - .getAccount(), + .getAccount(), xmppConnectionService.getPresenceGenerator() - .sendPresenceUpdatesTo(contact)); + .sendPresenceUpdatesTo(contact)); } else { contact.setOption(Contact.Options.PREEMPTIVE_GRANT); } @@ -78,7 +77,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); xmppConnectionService.sendPresencePacket(contact.getAccount(), xmppConnectionService.getPresenceGenerator() - .stopPresenceUpdatesTo(contact)); + .stopPresenceUpdatesTo(contact)); } } }; @@ -86,15 +85,15 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp @Override public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { + boolean isChecked) { if (isChecked) { xmppConnectionService.sendPresencePacket(contact.getAccount(), xmppConnectionService.getPresenceGenerator() - .requestPresenceUpdatesFrom(contact)); + .requestPresenceUpdatesFrom(contact)); } else { xmppConnectionService.sendPresencePacket(contact.getAccount(), xmppConnectionService.getPresenceGenerator() - .stopPresenceUpdatesFrom(contact)); + .stopPresenceUpdatesFrom(contact)); } } }; @@ -163,18 +162,17 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp @Override protected String getShareableUri(boolean http) { - final String prefix = http ? "https://conversations.im/i/" : "xmpp:"; - if (contact != null) { - return prefix+contact.getJid().asBareJid().toEscapedString(); + if (http) { + return "https://conversations.im/j/" + XmppUri.lameUrlEncode(contact.getJid().asBareJid().toEscapedString()); } else { - return ""; + return "xmpp:" + contact.getJid().asBareJid().toEscapedString(); } } @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - showInactiveOmemo = savedInstanceState != null && savedInstanceState.getBoolean("show_inactive_omemo",false); + showInactiveOmemo = savedInstanceState != null && savedInstanceState.getBoolean("show_inactive_omemo", false); if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) { try { this.accountJid = Jid.of(getIntent().getExtras().getString(EXTRA_ACCOUNT)); @@ -199,7 +197,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp @Override public void onSaveInstanceState(final Bundle savedInstanceState) { - savedInstanceState.putBoolean("show_inactive_omemo",showInactiveOmemo); + savedInstanceState.putBoolean("show_inactive_omemo", showInactiveOmemo); super.onSaveInstanceState(savedInstanceState); } @@ -235,9 +233,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp break; case R.id.action_delete_contact: builder.setTitle(getString(R.string.action_delete_contact)) - .setMessage(getString(R.string.remove_contact_text, contact.getJid().toString())) - .setPositiveButton(getString(R.string.delete), - removeFromRoster).create().show(); + .setMessage(getString(R.string.remove_contact_text, contact.getJid().toString())) + .setPositiveButton(getString(R.string.delete), + removeFromRoster).create().show(); break; case R.id.action_edit_contact: Uri systemAccount = contact.getSystemAccount(); @@ -317,7 +315,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp StringBuilder builder = new StringBuilder(); binding.statusMessage.setVisibility(View.VISIBLE); int s = statusMessages.size(); - for(int i = 0; i < s; ++i) { + for (int i = 0; i < s; ++i) { if (s > 1) { builder.append("• "); } @@ -384,7 +382,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp } } - binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this,contact.getJid())); + binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this, contact.getJid())); String account; if (Config.DOMAIN_LOCK != null) { account = contact.getAccount().getJid().getLocal(); @@ -429,7 +427,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp } binding.scanButton.setVisibility(hasKeys && isCameraFeatureAvailable() ? View.VISIBLE : View.GONE); if (hasKeys) { - binding.scanButton.setOnClickListener((v)-> ScanActivity.scan(this)); + binding.scanButton.setOnClickListener((v) -> ScanActivity.scan(this)); } if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) { hasKeys = true; @@ -455,8 +453,8 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp } else { binding.tags.setVisibility(View.VISIBLE); binding.tags.removeAllViewsInLayout(); - for(final ListItem.Tag tag : tagList) { - final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,binding.tags,false); + for (final ListItem.Tag tag : tagList) { + final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, binding.tags, false); tv.setText(tag.getName()); tv.setBackgroundColor(tag.getColor()); binding.tags.addView(tv); @@ -487,11 +485,11 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp @Override protected void processFingerprintVerification(XmppUri uri) { if (contact != null && contact.getJid().asBareJid().equals(uri.getJid()) && uri.hasFingerprints()) { - if (xmppConnectionService.verifyFingerprints(contact,uri.getFingerprints())) { - Toast.makeText(this,R.string.verified_fingerprints,Toast.LENGTH_SHORT).show(); + if (xmppConnectionService.verifyFingerprints(contact, uri.getFingerprints())) { + Toast.makeText(this, R.string.verified_fingerprints, Toast.LENGTH_SHORT).show(); } } else { - Toast.makeText(this,R.string.invalid_barcode,Toast.LENGTH_SHORT).show(); + Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show(); } } } diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java index d5431aef5..adaf89542 100644 --- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java +++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.utils; import android.net.Uri; +import android.util.Log; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -8,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import eu.siacs.conversations.Config; import rocks.xmpp.addr.Jid; public class XmppUri { @@ -21,7 +23,6 @@ public class XmppUri { protected boolean safeSource = true; public static final String OMEMO_URI_PARAM = "omemo-sid-"; - public static final String OTR_URI_PARAM = "otr-fingerprint"; public static final String ACTION_JOIN = "join"; public static final String ACTION_MESSAGE = "message"; @@ -60,8 +61,9 @@ public class XmppUri { if (segments.size() >= 2 && segments.get(1).contains("@")) { // sample : https://conversations.im/i/foo@bar.com try { - jid = Jid.of(segments.get(1)).toString(); + jid = Jid.of(lameUrlDecode(segments.get(1))).toString(); } catch (Exception e) { + Log.d(Config.LOGTAG, "parsing failed ", e); jid = null; } } else if (segments.size() >= 3) { @@ -71,7 +73,7 @@ public class XmppUri { if (segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0))) { action = ACTION_JOIN; } - fingerprints = parseFingerprints(uri.getQuery(),'&'); + fingerprints = parseFingerprints(uri.getQuery(), '&'); } else if ("xmpp".equalsIgnoreCase(scheme)) { // sample: xmpp:foo@bar.com @@ -120,21 +122,21 @@ public class XmppUri { } protected List parseFingerprints(String query) { - return parseFingerprints(query,';'); + return parseFingerprints(query, ';'); } protected List parseFingerprints(String query, char seperator) { List fingerprints = new ArrayList<>(); String[] pairs = query == null ? new String[0] : query.split(String.valueOf(seperator)); - for(String pair : pairs) { - String[] parts = pair.split("=",2); + for (String pair : pairs) { + String[] parts = pair.split("=", 2); if (parts.length == 2) { String key = parts[0].toLowerCase(Locale.US); String value = parts[1].toLowerCase(Locale.US); if (key.startsWith(OMEMO_URI_PARAM)) { try { int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length())); - fingerprints.add(new Fingerprint(FingerprintType.OMEMO,value,id)); + fingerprints.add(new Fingerprint(FingerprintType.OMEMO, value, id)); } catch (Exception e) { //ignoring invalid device id } @@ -145,11 +147,11 @@ public class XmppUri { } protected String parseParameter(String key, String query) { - for(String pair : query == null ? new String[0] : query.split(";")) { - final String[] parts = pair.split("=",2); + for (String pair : query == null ? new String[0] : query.split(";")) { + final String[] parts = pair.split("=", 2); if (parts.length == 2 && key.equals(parts[0].toLowerCase(Locale.US))) { try { - return URLDecoder.decode(parts[1],"UTF-8"); + return URLDecoder.decode(parts[1], "UTF-8"); } catch (UnsupportedEncodingException e) { return null; } @@ -159,8 +161,8 @@ public class XmppUri { } private boolean hasAction(String query, String action) { - for(String pair : query == null ? new String[0] : query.split(";")) { - final String[] parts = pair.split("=",2); + for (String pair : query == null ? new String[0] : query.split(";")) { + final String[] parts = pair.split("=", 2); if (parts.length == 1 && parts[0].equals(action)) { return true; } @@ -178,7 +180,7 @@ public class XmppUri { public Jid getJid() { try { - return this.jid == null ? null :Jid.of(this.jid.toLowerCase()); + return this.jid == null ? null : Jid.of(this.jid.toLowerCase()); } catch (IllegalArgumentException e) { return null; } @@ -211,6 +213,7 @@ public class XmppUri { public boolean hasFingerprints() { return fingerprints.size() > 0; } + public enum FingerprintType { OMEMO } @@ -218,7 +221,7 @@ public class XmppUri { public static String getFingerprintUri(String base, List fingerprints, char seperator) { StringBuilder builder = new StringBuilder(base); builder.append('?'); - for(int i = 0; i < fingerprints.size(); ++i) { + for (int i = 0; i < fingerprints.size(); ++i) { XmppUri.FingerprintType type = fingerprints.get(i).type; if (type == XmppUri.FingerprintType.OMEMO) { builder.append(XmppUri.OMEMO_URI_PARAM); @@ -226,7 +229,7 @@ public class XmppUri { } builder.append('='); builder.append(fingerprints.get(i).fingerprint); - if (i != fingerprints.size() -1) { + if (i != fingerprints.size() - 1) { builder.append(seperator); } } @@ -250,7 +253,15 @@ public class XmppUri { @Override public String toString() { - return type.toString()+": "+fingerprint+(deviceId != 0 ? " "+String.valueOf(deviceId) : ""); + return type.toString() + ": " + fingerprint + (deviceId != 0 ? " " + String.valueOf(deviceId) : ""); } } + + public static String lameUrlDecode(String url) { + return url.replace("%23", "#").replace("%25", "%"); + } + + public static String lameUrlEncode(String url) { + return url.replace("%", "%25").replace("#", "%23"); + } }