diff options
22 files changed, 526 insertions, 475 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java index e0bd0e79..82d7145e 100644 --- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java @@ -85,13 +85,11 @@ public class OtrEngine implements OtrEngineHost { this.account.setKey("otr_p", privateKeySpec.getP().toString(16)); this.account.setKey("otr_q", privateKeySpec.getQ().toString(16)); this.account.setKey("otr_y", publicKeySpec.getY().toString(16)); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { + } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) { e.printStackTrace(); } - } + } @Override public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) { @@ -157,9 +155,9 @@ public class OtrEngine implements OtrEngineHost { MessagePacket packet = new MessagePacket(); packet.setFrom(account.getFullJid()); if (session.getUserID().isEmpty()) { - packet.setTo(session.getAccountID()); + packet.setAttribute("to", session.getAccountID()); } else { - packet.setTo(session.getAccountID() + "/" + session.getUserID()); + packet.setAttribute("to", session.getAccountID() + "/" + session.getUserID()); } packet.setBody(body); packet.addChild("private", "urn:xmpp:carbons:2"); diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java index 9a2b4a11..ae4a54a1 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java @@ -41,7 +41,7 @@ public class PgpEngine { Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message - .getConversation().getAccount().getJid()); + .getConversation().getAccount().getJid().toString()); if (message.getType() == Message.TYPE_TEXT) { InputStream is = new ByteArrayInputStream(message.getBody() .getBytes()); @@ -77,10 +77,7 @@ public class PgpEngine { return; case OpenPgpApi.RESULT_CODE_ERROR: callback.error(R.string.openpgp_error, message); - return; - default: - return; - } + } } }); } else if (message.getType() == Message.TYPE_IMAGE) { @@ -135,15 +132,10 @@ public class PgpEngine { return; case OpenPgpApi.RESULT_CODE_ERROR: callback.error(R.string.openpgp_error, message); - return; - default: - return; } } }); - } catch (FileNotFoundException e) { - callback.error(R.string.error_decrypting_file, message); - } catch (IOException e) { + } catch (final IOException e) { callback.error(R.string.error_decrypting_file, message); } @@ -164,7 +156,7 @@ public class PgpEngine { .getMucOptions().getPgpKeyIds()); } params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message - .getConversation().getAccount().getJid()); + .getConversation().getAccount().getJid().toString()); if (message.getType() == Message.TYPE_TEXT) { params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -237,12 +229,8 @@ public class PgpEngine { } } }); - } catch (FileNotFoundException e) { + } catch (final IOException e) { callback.error(R.string.openpgp_error, message); - return; - } catch (IOException e) { - callback.error(R.string.openpgp_error, message); - return; } } } @@ -254,7 +242,7 @@ public class PgpEngine { if (status == null) { status = ""; } - StringBuilder pgpSig = new StringBuilder(); + final StringBuilder pgpSig = new StringBuilder(); pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----"); pgpSig.append('\n'); pgpSig.append('\n'); @@ -269,7 +257,7 @@ public class PgpEngine { Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid()); + params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid().toString()); InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes()); ByteArrayOutputStream os = new ByteArrayOutputStream(); Intent result = api.executeApi(params, is, os); @@ -296,7 +284,7 @@ public class PgpEngine { Intent params = new Intent(); params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); params.setAction(OpenPgpApi.ACTION_SIGN); - params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid()); + params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid().toString()); InputStream is = new ByteArrayInputStream(status.getBytes()); final OutputStream os = new ByteArrayOutputStream(); api.executeApiAsync(params, is, os, new IOpenPgpCallback() { @@ -338,8 +326,7 @@ public class PgpEngine { return; case OpenPgpApi.RESULT_CODE_ERROR: callback.error(R.string.openpgp_error, account); - return; - } + } } }); } @@ -349,7 +336,7 @@ public class PgpEngine { params.setAction(OpenPgpApi.ACTION_GET_KEY); params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount() - .getJid()); + .getJid().toString()); api.executeApiAsync(params, null, null, new IOpenPgpCallback() { @Override @@ -365,8 +352,7 @@ public class PgpEngine { return; case OpenPgpApi.RESULT_CODE_ERROR: callback.error(R.string.openpgp_error, contact); - return; - } + } } }); } @@ -376,7 +362,7 @@ public class PgpEngine { params.setAction(OpenPgpApi.ACTION_GET_KEY); params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount() - .getJid()); + .getJid().toString()); Intent result = api.executeApi(params, null, null); return (PendingIntent) result .getParcelableExtra(OpenPgpApi.RESULT_INTENT); @@ -386,7 +372,7 @@ public class PgpEngine { Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_GET_KEY); params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId); - params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid()); + params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid().toString()); Intent result = api.executeApi(params, null, null); return (PendingIntent) result .getParcelableExtra(OpenPgpApi.RESULT_INTENT); diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 80a9d62f..e600f37b 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.entities; import java.security.interfaces.DSAPublicKey; import java.util.List; -import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; import net.java.otr4j.crypto.OtrCryptoEngineImpl; @@ -16,6 +15,9 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.OtrEngine; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.content.ContentValues; import android.database.Cursor; import android.os.SystemClock; @@ -50,12 +52,10 @@ public class Account extends AbstractEntity { public static final int STATUS_REGISTRATION_SUCCESSFULL = 9; public static final int STATUS_REGISTRATION_NOT_SUPPORTED = 10; - protected String username; - protected String server; + protected Jid jid; protected String password; protected int options = 0; protected String rosterVersion; - protected String resource = "mobile"; protected int status = -1; protected JSONObject keys = new JSONObject(); protected String avatar; @@ -69,31 +69,36 @@ public class Account extends AbstractEntity { private String otrFingerprint; private Roster roster = null; - private List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>(); - public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>(); - public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>(); + private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); + public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>(); + public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>(); public Account() { this.uuid = "0"; } - public Account(String username, String server, String password) { - this(java.util.UUID.randomUUID().toString(), username, server, + public Account(final Jid jid, final String password) { + this(java.util.UUID.randomUUID().toString(), jid, password, 0, null, "", null); } - public Account(String uuid, String username, String server, - String password, int options, String rosterVersion, String keys, - String avatar) { + public Account(final String uuid, final Jid jid, + final String password, final int options, final String rosterVersion, final String keys, + final String avatar) { this.uuid = uuid; - this.username = username; - this.server = server; + this.jid = jid; + if (jid.getResourcepart().isEmpty()) { + try { + this.setResource("mobile"); + } catch (final InvalidJidException ignored) { + } + } this.password = password; this.options = options; this.rosterVersion = rosterVersion; try { this.keys = new JSONObject(keys); - } catch (JSONException e) { + } catch (final JSONException ignored) { } this.avatar = avatar; @@ -112,30 +117,30 @@ public class Account extends AbstractEntity { } public String getUsername() { - return username; + return jid.getLocalpart(); } - public void setUsername(String username) { - this.username = username; - } + public void setUsername(final String username) throws InvalidJidException { + jid = Jid.fromParts(username, jid.getDomainpart(), jid.getResourcepart()); + } - public String getServer() { - return server; + public Jid getServer() { + return jid.toDomainJid(); } - public void setServer(String server) { - this.server = server; + public void setServer(final String server) throws InvalidJidException { + jid = Jid.fromParts(jid.getLocalpart(), server, jid.getResourcepart()); } public String getPassword() { return password; } - public void setPassword(String password) { + public void setPassword(final String password) { this.password = password; } - public void setStatus(int status) { + public void setStatus(final int status) { this.status = status; } @@ -156,25 +161,19 @@ public class Account extends AbstractEntity { } public boolean hasErrorStatus() { - if (getXmppConnection() == null) { - return false; - } else { - return getStatus() > STATUS_NO_INTERNET - && (getXmppConnection().getAttempt() >= 2); - } + return getXmppConnection() != null && getStatus() > STATUS_NO_INTERNET && (getXmppConnection().getAttempt() >= 2); } - public void setResource(String resource) { - this.resource = resource; + public void setResource(final String resource) throws InvalidJidException { + jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource); } public String getResource() { - return this.resource; + return jid.getResourcepart(); } - public String getJid() { - return username.toLowerCase(Locale.getDefault()) + "@" - + server.toLowerCase(Locale.getDefault()); + public Jid getJid() { + return jid.toBareJid(); } public JSONObject getKeys() { @@ -210,8 +209,8 @@ public class Account extends AbstractEntity { public ContentValues getContentValues() { ContentValues values = new ContentValues(); values.put(UUID, uuid); - values.put(USERNAME, username); - values.put(SERVER, server); + values.put(USERNAME, jid.getLocalpart()); + values.put(SERVER, jid.getDomainpart()); values.put(PASSWORD, password); values.put(OPTIONS, options); values.put(KEYS, this.keys.toString()); @@ -221,9 +220,14 @@ public class Account extends AbstractEntity { } public static Account fromCursor(Cursor cursor) { - return new Account(cursor.getString(cursor.getColumnIndex(UUID)), - cursor.getString(cursor.getColumnIndex(USERNAME)), - cursor.getString(cursor.getColumnIndex(SERVER)), + Jid jid = null; + try { + jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)), + cursor.getString(cursor.getColumnIndex(SERVER)), "mobile"); + } catch (final InvalidJidException ignored) { + } + return new Account(cursor.getString(cursor.getColumnIndex(UUID)), + jid, cursor.getString(cursor.getColumnIndex(PASSWORD)), cursor.getInt(cursor.getColumnIndex(OPTIONS)), cursor.getString(cursor.getColumnIndex(ROSTERVERSION)), @@ -246,8 +250,8 @@ public class Account extends AbstractEntity { this.xmppConnection = connection; } - public String getFullJid() { - return this.getJid() + "/" + this.resource; + public Jid getFullJid() { + return this.getJid(); } public String getOtrFingerprint() { @@ -265,7 +269,7 @@ public class Account extends AbstractEntity { builder.insert(26, " "); builder.insert(35, " "); this.otrFingerprint = builder.toString(); - } catch (OtrCryptoException e) { + } catch (final OtrCryptoException ignored) { } } diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java index dd9e805c..54dcfea1 100644 --- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java +++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java @@ -3,15 +3,17 @@ package eu.siacs.conversations.entities; import java.util.Locale; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Bookmark extends Element implements ListItem { private Account account; private Conversation mJoinedConversation; - public Bookmark(Account account, String jid) { + public Bookmark(final Account account, final Jid jid) { super("conference"); - this.setAttribute("jid", jid); + this.setAttribute("jid", jid.toString()); this.account = account; } @@ -55,10 +57,10 @@ public class Bookmark extends Element implements ListItem { } @Override - public int compareTo(ListItem another) { - return this.getDisplayName().compareToIgnoreCase( - another.getDisplayName()); - } + public int compareTo(final ListItem another) { + return this.getDisplayName().compareToIgnoreCase( + another.getDisplayName()); + } @Override public String getDisplayName() { @@ -68,16 +70,20 @@ public class Bookmark extends Element implements ListItem { } else if (getName() != null) { return getName(); } else { - return this.getJid().split("@")[0]; + return this.getJid().getLocalpart(); } } @Override - public String getJid() { - String jid = this.getAttribute("jid"); + public Jid getJid() { + final String jid = this.getAttribute("jid"); if (jid != null) { - return jid.toLowerCase(Locale.US); - } else { + try { + return Jid.fromString(jid); + } catch (final InvalidJidException e) { + return null; + } + } else { return null; } } @@ -108,7 +114,7 @@ public class Bookmark extends Element implements ListItem { public boolean match(String needle) { return needle == null - || getJid().contains(needle.toLowerCase(Locale.US)) + || getJid().toString().toLowerCase(Locale.US).contains(needle.toLowerCase(Locale.US)) || getDisplayName().toLowerCase(Locale.US).contains( needle.toLowerCase(Locale.US)); } diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index af5172d3..8fa91b02 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -1,16 +1,18 @@ package eu.siacs.conversations.entities; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; +import android.content.ContentValues; +import android.database.Cursor; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.HashSet; +import java.util.Set; + import eu.siacs.conversations.xml.Element; -import android.content.ContentValues; -import android.database.Cursor; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Contact implements ListItem { public static final String TABLENAME = "contacts"; @@ -31,7 +33,7 @@ public class Contact implements ListItem { protected String systemName; protected String serverName; protected String presenceName; - protected String jid; + protected Jid jid; protected int subscription = 0; protected String systemAccount; protected String photoUri; @@ -41,12 +43,10 @@ public class Contact implements ListItem { protected Account account; - protected boolean inRoster = true; - public Lastseen lastseen = new Lastseen(); public Contact(final String account, final String systemName, final String serverName, - final String jid, final int subscription, final String photoUri, + final Jid jid, final int subscription, final String photoUri, final String systemAccount, final String keys, final String avatar, final Lastseen lastseen) { this(account, systemName, serverName, jid, subscription, photoUri, systemAccount, keys, @@ -55,7 +55,7 @@ public class Contact implements ListItem { } public Contact(final String account, final String systemName, final String serverName, - final String jid, final int subscription, final String photoUri, + final Jid jid, final int subscription, final String photoUri, final String systemAccount, final String keys, final String avatar) { this.accountUuid = account; this.systemName = systemName; @@ -72,7 +72,7 @@ public class Contact implements ListItem { this.avatar = avatar; } - public Contact(final String jid) { + public Contact(final Jid jid) { this.jid = jid; } @@ -84,7 +84,7 @@ public class Contact implements ListItem { } else if (this.presenceName != null) { return this.presenceName; } else { - return this.jid.split("@")[0]; + return jid.getLocalpart(); } } @@ -92,13 +92,13 @@ public class Contact implements ListItem { return this.photoUri; } - public String getJid() { - return this.jid.toLowerCase(Locale.getDefault()); + public Jid getJid() { + return jid; } public boolean match(String needle) { return needle == null - || jid.contains(needle.toLowerCase()) + || jid.toString().contains(needle.toLowerCase()) || getDisplayName().toLowerCase() .contains(needle.toLowerCase()); } @@ -108,7 +108,7 @@ public class Contact implements ListItem { values.put(ACCOUNT, accountUuid); values.put(SYSTEMNAME, systemName); values.put(SERVERNAME, serverName); - values.put(JID, jid); + values.put(JID, jid.toString()); values.put(OPTIONS, subscription); values.put(SYSTEMACCOUNT, systemAccount); values.put(PHOTOURI, photoUri); @@ -123,10 +123,17 @@ public class Contact implements ListItem { final Lastseen lastseen = new Lastseen( cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)), cursor.getLong(cursor.getColumnIndex(LAST_TIME))); - return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)), + final Jid jid; + try { + jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID))); + } catch (final InvalidJidException e) { + // TODO: Borked DB... handle this somehow? + return null; + } + return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)), cursor.getString(cursor.getColumnIndex(SYSTEMNAME)), cursor.getString(cursor.getColumnIndex(SERVERNAME)), - cursor.getString(cursor.getColumnIndex(JID)), + jid, cursor.getInt(cursor.getColumnIndex(OPTIONS)), cursor.getString(cursor.getColumnIndex(PHOTOURI)), cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)), @@ -198,7 +205,7 @@ public class Contact implements ListItem { } public Set<String> getOtrFingerprints() { - Set<String> set = new HashSet<String>(); + Set<String> set = new HashSet<>(); try { if (this.keys.has("otr_fingerprints")) { JSONArray fingerprints = this.keys @@ -225,7 +232,7 @@ public class Contact implements ListItem { } fingerprints.put(print); this.keys.put("otr_fingerprints", fingerprints); - } catch (JSONException e) { + } catch (final JSONException ignored) { } } @@ -233,7 +240,7 @@ public class Contact implements ListItem { public void setPgpKeyId(long keyId) { try { this.keys.put("pgp_keyid", keyId); - } catch (JSONException e) { + } catch (final JSONException ignored) { } } @@ -273,21 +280,26 @@ public class Contact implements ListItem { String subscription = item.getAttribute("subscription"); if (subscription != null) { - if (subscription.equals("to")) { - this.resetOption(Contact.Options.FROM); - this.setOption(Contact.Options.TO); - } else if (subscription.equals("from")) { - this.resetOption(Contact.Options.TO); - this.setOption(Contact.Options.FROM); - this.resetOption(Contact.Options.PREEMPTIVE_GRANT); - } else if (subscription.equals("both")) { - this.setOption(Contact.Options.TO); - this.setOption(Contact.Options.FROM); - this.resetOption(Contact.Options.PREEMPTIVE_GRANT); - } else if (subscription.equals("none")) { - this.resetOption(Contact.Options.FROM); - this.resetOption(Contact.Options.TO); - } + switch (subscription) { + case "to": + this.resetOption(Options.FROM); + this.setOption(Options.TO); + break; + case "from": + this.resetOption(Options.TO); + this.setOption(Options.FROM); + this.resetOption(Options.PREEMPTIVE_GRANT); + break; + case "both": + this.setOption(Options.TO); + this.setOption(Options.FROM); + this.resetOption(Options.PREEMPTIVE_GRANT); + break; + case "none": + this.resetOption(Options.FROM); + this.resetOption(Options.TO); + break; + } } // do NOT override asking if pending push request @@ -301,8 +313,8 @@ public class Contact implements ListItem { } public Element asElement() { - Element item = new Element("item"); - item.setAttribute("jid", this.jid); + final Element item = new Element("item"); + item.setAttribute("jid", this.jid.toString()); if (this.serverName != null) { item.setAttribute("name", this.serverName); } @@ -335,18 +347,13 @@ public class Contact implements ListItem { } @Override - public int compareTo(ListItem another) { + public int compareTo(final ListItem another) { return this.getDisplayName().compareToIgnoreCase( another.getDisplayName()); } - public String getServer() { - String[] split = getJid().split("@"); - if (split.length >= 2) { - return split[1]; - } else { - return null; - } + public Jid getServer() { + return getJid().toDomainJid(); } public boolean setAvatar(String filename) { diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 9d4c36db..fdffeccb 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -1,13 +1,8 @@ package eu.siacs.conversations.entities; -import java.security.interfaces.DSAPublicKey; -import java.util.ArrayList; -import java.util.List; - -import org.json.JSONException; -import org.json.JSONObject; - -import eu.siacs.conversations.services.XmppConnectionService; +import android.content.ContentValues; +import android.database.Cursor; +import android.os.SystemClock; import net.java.otr4j.OtrException; import net.java.otr4j.crypto.OtrCryptoEngineImpl; @@ -15,9 +10,17 @@ import net.java.otr4j.crypto.OtrCryptoException; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; -import android.content.ContentValues; -import android.database.Cursor; -import android.os.SystemClock; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.security.interfaces.DSAPublicKey; +import java.util.ArrayList; +import java.util.List; + +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Conversation extends AbstractEntity { public static final String TABLENAME = "conversations"; @@ -45,7 +48,7 @@ public class Conversation extends AbstractEntity { private String name; private String contactUuid; private String accountUuid; - private String contactJid; + private Jid contactJid; private int status; private long created; private int mode; @@ -54,7 +57,7 @@ public class Conversation extends AbstractEntity { private String nextPresence; - protected ArrayList<Message> messages = new ArrayList<Message>(); + protected ArrayList<Message> messages = new ArrayList<>(); protected Account account = null; private transient SessionImpl otrSession; @@ -71,17 +74,17 @@ public class Conversation extends AbstractEntity { private Bookmark bookmark; - public Conversation(String name, Account account, String contactJid, - int mode) { + public Conversation(final String name, final Account account, final Jid contactJid, + final int mode) { this(java.util.UUID.randomUUID().toString(), name, null, account .getUuid(), contactJid, System.currentTimeMillis(), STATUS_AVAILABLE, mode, ""); this.account = account; } - public Conversation(String uuid, String name, String contactUuid, - String accountUuid, String contactJid, long created, int status, - int mode, String attributes) { + public Conversation(final String uuid, final String name, final String contactUuid, + final String accountUuid, final Jid contactJid, final long created, final int status, + final int mode, final String attributes) { this.uuid = uuid; this.name = name; this.contactUuid = contactUuid; @@ -91,10 +94,7 @@ public class Conversation extends AbstractEntity { this.status = status; this.mode = mode; try { - if (attributes == null) { - attributes = new String(); - } - this.attributes = new JSONObject(attributes); + this.attributes = new JSONObject(attributes == null ? "" : attributes); } catch (JSONException e) { this.attributes = new JSONObject(); } @@ -105,10 +105,8 @@ public class Conversation extends AbstractEntity { } public boolean isRead() { - if ((this.messages == null) || (this.messages.size() == 0)) - return true; - return this.messages.get(this.messages.size() - 1).isRead(); - } + return (this.messages == null) || (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead(); + } public void markRead() { if (this.messages == null) { @@ -186,7 +184,7 @@ public class Conversation extends AbstractEntity { this.account = account; } - public String getContactJid() { + public Jid getContactJid() { return this.contactJid; } @@ -204,7 +202,7 @@ public class Conversation extends AbstractEntity { values.put(NAME, name); values.put(CONTACT, contactUuid); values.put(ACCOUNT, accountUuid); - values.put(CONTACTJID, contactJid); + values.put(CONTACTJID, contactJid.toString()); values.put(CREATED, created); values.put(STATUS, status); values.put(MODE, mode); @@ -213,11 +211,18 @@ public class Conversation extends AbstractEntity { } public static Conversation fromCursor(Cursor cursor) { - return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)), + Jid jid; + try { + jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID))); + } catch (final InvalidJidException e) { + // Borked DB.. + jid = null; + } + return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)), cursor.getString(cursor.getColumnIndex(NAME)), cursor.getString(cursor.getColumnIndex(CONTACT)), cursor.getString(cursor.getColumnIndex(ACCOUNT)), - cursor.getString(cursor.getColumnIndex(CONTACTJID)), + jid, cursor.getLong(cursor.getColumnIndex(CREATED)), cursor.getInt(cursor.getColumnIndex(STATUS)), cursor.getInt(cursor.getColumnIndex(MODE)), @@ -241,8 +246,9 @@ public class Conversation extends AbstractEntity { if (this.otrSession != null) { return this.otrSession; } else { - SessionID sessionId = new SessionID(this.getContactJid().split("/", - 2)[0], presence, "xmpp"); + final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(), + presence, + "xmpp"); this.otrSession = new SessionImpl(sessionId, getAccount() .getOtrEngine(service)); try { @@ -317,7 +323,7 @@ public class Conversation extends AbstractEntity { builder.insert(26, " "); builder.insert(35, " "); this.otrFingerprint = builder.toString(); - } catch (OtrCryptoException e) { + } catch (final OtrCryptoException ignored) { } } @@ -335,7 +341,7 @@ public class Conversation extends AbstractEntity { this.mucOptions = null; } - public void setContactJid(String jid) { + public void setContactJid(final Jid jid) { this.contactJid = jid; } diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java index a1872d2f..fa650f1c 100644 --- a/src/main/java/eu/siacs/conversations/entities/ListItem.java +++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java @@ -1,7 +1,9 @@ package eu.siacs.conversations.entities; +import eu.siacs.conversations.xmpp.jid.Jid; + public interface ListItem extends Comparable<ListItem> { public String getDisplayName(); - public String getJid(); + public Jid getJid(); } diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index b33d5f37..ce21addc 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -5,6 +5,9 @@ import java.net.URL; import java.util.Arrays; import eu.siacs.conversations.Config; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.content.ContentValues; import android.database.Cursor; @@ -44,7 +47,7 @@ public class Message extends AbstractEntity { public static String REMOTE_MSG_ID = "remoteMsgId"; protected String conversationUuid; - protected String counterpart; + protected Jid counterpart; protected String trueCounterpart; protected String body; protected String encryptedBody; @@ -74,17 +77,17 @@ public class Message extends AbstractEntity { this.conversation = conversation; } - public Message(Conversation conversation, String counterpart, String body, - int encryption, int status) { + public Message(final Conversation conversation, final Jid counterpart, final String body, + final int encryption, final int status) { this(java.util.UUID.randomUUID().toString(), conversation.getUuid(), counterpart, null, body, System.currentTimeMillis(), encryption, status, TYPE_TEXT, null); this.conversation = conversation; } - public Message(String uuid, String conversationUUid, String counterpart, - String trueCounterpart, String body, long timeSent, int encryption, - int status, int type, String remoteMsgId) { + public Message(final String uuid, final String conversationUUid, final Jid counterpart, + final String trueCounterpart, final String body, final long timeSent, + final int encryption, final int status, final int type, final String remoteMsgId) { this.uuid = uuid; this.conversationUuid = conversationUUid; this.counterpart = counterpart; @@ -102,7 +105,7 @@ public class Message extends AbstractEntity { ContentValues values = new ContentValues(); values.put(UUID, uuid); values.put(CONVERSATION, conversationUuid); - values.put(COUNTERPART, counterpart); + values.put(COUNTERPART, counterpart.toString()); values.put(TRUE_COUNTERPART, trueCounterpart); values.put(BODY, body); values.put(TIME_SENT, timeSent); @@ -121,7 +124,7 @@ public class Message extends AbstractEntity { return this.conversation; } - public String getCounterpart() { + public Jid getCounterpart() { return counterpart; } @@ -163,9 +166,15 @@ public class Message extends AbstractEntity { } public static Message fromCursor(Cursor cursor) { - return new Message(cursor.getString(cursor.getColumnIndex(UUID)), + Jid jid; + try { + jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(COUNTERPART))); + } catch (InvalidJidException e) { + jid = null; + } + return new Message(cursor.getString(cursor.getColumnIndex(UUID)), cursor.getString(cursor.getColumnIndex(CONVERSATION)), - cursor.getString(cursor.getColumnIndex(COUNTERPART)), + jid, cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)), cursor.getString(cursor.getColumnIndex(BODY)), cursor.getLong(cursor.getColumnIndex(TIME_SENT)), @@ -225,11 +234,14 @@ public class Message extends AbstractEntity { public void setPresence(String presence) { if (presence == null) { - this.counterpart = this.counterpart.split("/", 2)[0]; + this.counterpart = this.counterpart.toBareJid(); } else { - this.counterpart = this.counterpart.split("/", 2)[0] + "/" - + presence; - } + try { + this.counterpart = Jid.fromString(this.counterpart.toBareJid() + "/" + presence); + } catch (final InvalidJidException ignored) { + // TODO: Handle this? + } + } } public void setTrueCounterpart(String trueCounterpart) { @@ -237,15 +249,11 @@ public class Message extends AbstractEntity { } public String getPresence() { - String[] counterparts = this.counterpart.split("/", 2); - if (counterparts.length == 2) { - return counterparts[1]; + if (!counterpart.getResourcepart().isEmpty()) { + return counterpart.getResourcepart(); } else { - if (this.counterpart.contains("/")) { - return ""; - } else { - return null; - } + // TODO: Return empty string or null? + return null; } } @@ -264,7 +272,7 @@ public class Message extends AbstractEntity { return message; } - public void setCounterpart(String counterpart) { + public void setCounterpart(final Jid counterpart) { this.counterpart = counterpart; } @@ -359,11 +367,7 @@ public class Message extends AbstractEntity { public boolean wasMergedIntoPrevious() { Message prev = this.prev(); - if (prev == null) { - return false; - } else { - return prev.mergeable(this); - } + return prev != null && prev.mergeable(this); } public boolean trusted() { diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index d7407cd5..f83387fc 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -6,6 +6,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import android.annotation.SuppressLint; @@ -66,15 +68,20 @@ public class MucOptions { public void setRole(String role) { role = role.toLowerCase(); - if (role.equals("moderator")) { - this.role = ROLE_MODERATOR; - } else if (role.equals("participant")) { - this.role = ROLE_PARTICIPANT; - } else if (role.equals("visitor")) { - this.role = ROLE_VISITOR; - } else { - this.role = ROLE_NONE; - } + switch (role) { + case "moderator": + this.role = ROLE_MODERATOR; + break; + case "participant": + this.role = ROLE_PARTICIPANT; + break; + case "visitor": + this.role = ROLE_VISITOR; + break; + default: + this.role = ROLE_NONE; + break; + } } public int getAffiliation() { @@ -109,7 +116,7 @@ public class MucOptions { } private Account account; - private List<User> users = new CopyOnWriteArrayList<User>(); + private List<User> users = new CopyOnWriteArrayList<>(); private Conversation conversation; private boolean isOnline = false; private int error = ERROR_ROOM_NOT_FOUND; @@ -233,13 +240,12 @@ public class MucOptions { } public String getProposedNick() { - String[] mucParts = conversation.getContactJid().split("/", 2); if (conversation.getBookmark() != null && conversation.getBookmark().getNick() != null) { return conversation.getBookmark().getNick(); } else { - if (mucParts.length == 2) { - return mucParts[1]; + if (!conversation.getContactJid().getResourcepart().isEmpty()) { + return conversation.getContactJid().getResourcepart(); } else { return account.getUsername(); } @@ -297,7 +303,7 @@ public class MucOptions { } public long[] getPgpKeyIds() { - List<Long> ids = new ArrayList<Long>(); + List<Long> ids = new ArrayList<>(); for (User user : getUsers()) { if (user.getPgpKeyId() != 0) { ids.add(user.getPgpKeyId()); @@ -328,10 +334,14 @@ public class MucOptions { return true; } - public String getJoinJid() { - return this.conversation.getContactJid().split("/", 2)[0] + "/" - + this.joinnick; - } + public Jid getJoinJid() { + try { + return Jid.fromString(this.conversation.getContactJid().toBareJid().toString() + "/" ++ this.joinnick); + } catch (final InvalidJidException e) { + return null; + } + } public String getTrueCounterpart(String counterpart) { for (User user : this.getUsers()) { diff --git a/src/main/java/eu/siacs/conversations/entities/Roster.java b/src/main/java/eu/siacs/conversations/entities/Roster.java index 3267b15a..27d4deb0 100644 --- a/src/main/java/eu/siacs/conversations/entities/Roster.java +++ b/src/main/java/eu/siacs/conversations/entities/Roster.java @@ -2,12 +2,13 @@ package eu.siacs.conversations.entities; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; +import eu.siacs.conversations.xmpp.jid.Jid; + public class Roster { Account account; - ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<String, Contact>(); + final ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<>(); private String version = null; public Roster(Account account) { @@ -27,14 +28,14 @@ public class Roster { } } - public Contact getContact(String jid) { - String cleanJid = jid.split("/", 2)[0].toLowerCase(Locale.getDefault()); - if (contacts.containsKey(cleanJid)) { - return contacts.get(cleanJid); + public Contact getContact(final Jid jid) { + final Jid bareJid = jid.toBareJid(); + if (contacts.containsKey(bareJid.toString())) { + return contacts.get(bareJid.toString()); } else { - Contact contact = new Contact(cleanJid); + Contact contact = new Contact(bareJid); contact.setAccount(account); - contacts.put(cleanJid, contact); + contacts.put(bareJid.toString(), contact); return contact; } } @@ -60,13 +61,13 @@ public class Roster { } public List<Contact> getContacts() { - return new ArrayList<Contact>(this.contacts.values()); + return new ArrayList<>(this.contacts.values()); } - public void initContact(Contact contact) { + public void initContact(final Contact contact) { contact.setAccount(account); contact.setOption(Contact.Options.IN_ROSTER); - contacts.put(contact.getJid(), contact); + contacts.put(contact.getJid().toBareJid().toString(), contact); } public void setVersion(String version) { diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index d44bf0ca..2f6df523 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -6,6 +6,7 @@ import java.util.List; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.IqPacket; @@ -18,7 +19,7 @@ public class IqGenerator extends AbstractGenerator { public IqPacket discoResponse(IqPacket request) { IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT); packet.setId(request.getId()); - packet.setTo(request.getFrom()); + packet.setAttribute("to", request.getFrom()); Element query = packet.addChild("query", "http://jabber.org/protocol/disco#info"); query.setAttribute("node", request.query().getAttribute("node")); @@ -86,8 +87,8 @@ public class IqGenerator extends AbstractGenerator { return packet; } - public IqPacket retrieveAvatarMetaData(String to) { - IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); + public IqPacket retrieveAvatarMetaData(final Jid to) { + final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); if (to != null) { packet.setTo(to); } diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index dd833e56..d53346b0 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -12,6 +12,7 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageGenerator extends AbstractGenerator { @@ -34,7 +35,7 @@ public class MessageGenerator extends AbstractGenerator { packet.setTo(message.getCounterpart()); packet.setType(MessagePacket.TYPE_CHAT); } else { - packet.setTo(message.getCounterpart().split("/", 2)[0]); + packet.setTo(message.getCounterpart().toBareJid()); packet.setType(MessagePacket.TYPE_GROUPCHAT); } packet.setFrom(account.getFullJid()); @@ -113,13 +114,13 @@ public class MessageGenerator extends AbstractGenerator { private MessagePacket generateError(MessagePacket origin) { MessagePacket packet = new MessagePacket(); packet.setId(origin.getId()); - packet.setTo(origin.getFrom()); + packet.setAttribute("to", origin.getFrom()); packet.setBody(origin.getBody()); packet.setType(MessagePacket.TYPE_ERROR); return packet; } - public MessagePacket confirm(Account account, String to, String id) { + public MessagePacket confirm(final Account account, final Jid to, final String id) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_NORMAL); packet.setTo(to); @@ -134,7 +135,7 @@ public class MessageGenerator extends AbstractGenerator { String subject) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_GROUPCHAT); - packet.setTo(conversation.getContactJid().split("/", 2)[0]); + packet.setTo(conversation.getContactJid().toBareJid()); Element subjectChild = new Element("subject"); subjectChild.setContent(subject); packet.addChild(subjectChild); @@ -142,19 +143,19 @@ public class MessageGenerator extends AbstractGenerator { return packet; } - public MessagePacket directInvite(Conversation conversation, String contact) { + public MessagePacket directInvite(final Conversation conversation, final Jid contact) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_NORMAL); packet.setTo(contact); packet.setFrom(conversation.getAccount().getFullJid()); Element x = packet.addChild("x", "jabber:x:conference"); - x.setAttribute("jid", conversation.getContactJid().split("/", 2)[0]); + x.setAttribute("jid", conversation.getContactJid().toBareJid().toString()); return packet; } public MessagePacket invite(Conversation conversation, String contact) { MessagePacket packet = new MessagePacket(); - packet.setTo(conversation.getContactJid().split("/", 2)[0]); + packet.setTo(conversation.getContactJid().toBareJid()); packet.setFrom(conversation.getAccount().getFullJid()); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); @@ -169,7 +170,7 @@ public class MessageGenerator extends AbstractGenerator { MessagePacket originalMessage, String namespace) { MessagePacket receivedPacket = new MessagePacket(); receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(originalMessage.getFrom()); + receivedPacket.setAttribute("to", originalMessage.getFrom()); receivedPacket.setFrom(account.getFullJid()); Element received = receivedPacket.addChild("received", namespace); received.setAttribute("id", originalMessage.getId()); diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index d896dd00..6036c802 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -15,8 +15,8 @@ public class PresenceGenerator extends AbstractGenerator { private PresencePacket subscription(String type, Contact contact) { PresencePacket packet = new PresencePacket(); packet.setAttribute("type", type); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); + packet.setTo(contact.getJid()); + packet.setFrom(contact.getAccount().getJid()); return packet; } @@ -38,7 +38,7 @@ public class PresenceGenerator extends AbstractGenerator { public PresencePacket sendPresence(Account account) { PresencePacket packet = new PresencePacket(); - packet.setAttribute("from", account.getFullJid()); + packet.setFrom(account.getFullJid()); String sig = account.getPgpSignature(); if (sig != null) { packet.addChild("status").setContent("online"); diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 5541c1c6..9e413052 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -11,6 +11,8 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public abstract class AbstractParser { @@ -22,7 +24,7 @@ public abstract class AbstractParser { protected long getTimestamp(Element packet) { long now = System.currentTimeMillis(); - ArrayList<String> stamps = new ArrayList<String>(); + ArrayList<String> stamps = new ArrayList<>(); for (Element child : packet.getChildren()) { if (child.getName().equals("delay")) { stamps.add(child.getAttribute("stamp").replace("Z", "+0000")); @@ -58,21 +60,21 @@ public abstract class AbstractParser { } } - protected void updateLastseen(Element packet, Account account, - boolean presenceOverwrite) { - String[] fromParts = packet.getAttribute("from").split("/", 2); - String from = fromParts[0]; - String presence = null; - if (fromParts.length >= 2) { - presence = fromParts[1]; - } else { - presence = ""; - } + protected void updateLastseen(final Element packet, final Account account, + final boolean presenceOverwrite) { + Jid from; + try { + from = Jid.fromString(packet.getAttribute("from")).toBareJid(); + } catch (final InvalidJidException e) { + // TODO: Handle this? + from = null; + } + String presence = from == null || from.getResourcepart().isEmpty() ? "" : from.getResourcepart(); Contact contact = account.getRoster().getContact(from); long timestamp = getTimestamp(packet); if (timestamp >= contact.lastseen.time) { contact.lastseen.time = timestamp; - if ((presence != null) && (presenceOverwrite)) { + if (!presence.isEmpty() && presenceOverwrite) { contact.lastseen.presence = presence; } } diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index df6754f2..1c66da26 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -5,6 +5,8 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class IqParser extends AbstractParser implements OnIqPacketReceived { @@ -20,8 +22,14 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { } for (Element item : query.getChildren()) { if (item.getName().equals("item")) { - String jid = item.getAttribute("jid"); - String name = item.getAttribute("name"); + Jid jid; + try { + jid = Jid.fromString(item.getAttribute("jid")); + } catch (final InvalidJidException e) { + // TODO: Handle this? + jid = null; + } + String name = item.getAttribute("name"); String subscription = item.getAttribute("subscription"); Contact contact = account.getRoster().getContact(jid); if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { @@ -59,8 +67,13 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.hasChild("query", "jabber:iq:roster")) { - String from = packet.getFrom(); - if ((from == null) || (from.equals(account.getJid()))) { + Jid from = null; + try { + from = Jid.fromString(packet.getFrom()); + } catch (final InvalidJidException e) { + // TODO: Handle this? + } + if ((from == null) || (from.equals(account.getJid()))) { Element query = packet.findChild("query"); this.rosterItems(account, query); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index ef045546..310c8f42 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -9,6 +9,8 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Roster; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteCantOpenDatabaseException; @@ -147,7 +149,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public CopyOnWriteArrayList<Conversation> getConversations(int status) { - CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>(); + CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = { Integer.toString(status) }; Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME @@ -165,7 +167,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public ArrayList<Message> getMessages(Conversation conversation, int limit, long timestamp) { - ArrayList<Message> list = new ArrayList<Message>(); + ArrayList<Message> list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; if (timestamp == -1) { @@ -192,9 +194,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { return list; } - public Conversation findConversation(Account account, String contactJid) { + public Conversation findConversation(final Account account, final Jid contactJid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { account.getUuid(), contactJid + "%" }; + String[] selectionArgs = { account.getUuid(), contactJid.toBareJid().toString() + "%" }; Cursor cursor = db.query(Conversation.TABLENAME, null, Conversation.ACCOUNT + "=? AND " + Conversation.CONTACTJID + " like ?", selectionArgs, null, null, null); @@ -212,7 +214,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public List<Account> getAccounts() { - List<Account> list = new ArrayList<Account>(); + List<Account> list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor = db.query(Account.TABLENAME, null, null, null, null, null, null); @@ -276,15 +278,15 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.close(); } - public void writeRoster(Roster roster) { - Account account = roster.getAccount(); - SQLiteDatabase db = this.getWritableDatabase(); + public void writeRoster(final Roster roster) { + final Account account = roster.getAccount(); + final SQLiteDatabase db = this.getWritableDatabase(); for (Contact contact : roster.getContacts()) { if (contact.getOption(Contact.Options.IN_ROSTER)) { db.insert(Contact.TABLENAME, null, contact.getContentValues()); } else { String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?"; - String[] whereArgs = { account.getUuid(), contact.getJid() }; + String[] whereArgs = { account.getUuid(), contact.getJid().toString() }; db.delete(Contact.TABLENAME, where, whereArgs); } } @@ -341,7 +343,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public List<Message> getImageMessages(Conversation conversation) { - ArrayList<Message> list = new ArrayList<Message>(); + ArrayList<Message> list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; String[] selectionArgs = { conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE) }; diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ea6f509f..27268898 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -55,6 +55,8 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnMessageAcknowledged; import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; @@ -91,8 +93,6 @@ public class XmppConnectionService extends Service { public DatabaseBackend databaseBackend; private FileBackend fileBackend = new FileBackend(this); - public long startDate; - private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; public static String ACTION_CLEAR_NOTIFICATION = "clear_notification"; @@ -171,7 +171,6 @@ public class XmppConnectionService extends Service { XmppConnection connection = account.getXmppConnection(); if (mOnAccountUpdate != null) { mOnAccountUpdate.onAccountUpdate(); - ; } if (account.getStatus() == Account.STATUS_ONLINE) { for (Conversation conversation : account.pendingConferenceLeaves) { @@ -182,12 +181,12 @@ public class XmppConnectionService extends Service { } mJingleConnectionManager.cancelInTransmission(); List<Conversation> conversations = getConversations(); - for (int i = 0; i < conversations.size(); ++i) { - if (conversations.get(i).getAccount() == account) { - conversations.get(i).startOtrIfNeeded(); - sendUnsendMessages(conversations.get(i)); - } - } + for (Conversation conversation : conversations) { + if (conversation.getAccount() == account) { + conversation.startOtrIfNeeded(); + sendUnsendMessages(conversation); + } + } if (connection != null && connection.getFeatures().csi()) { if (checkListeners()) { Log.d(Config.LOGTAG, account.getJid() @@ -335,7 +334,7 @@ public class XmppConnectionService extends Service { return find(bookmark.getAccount(), bookmark.getJid()); } - public Conversation find(Account account, String jid) { + public Conversation find(final Account account, final Jid jid) { return find(getConversations(), account, jid); } @@ -415,7 +414,7 @@ public class XmppConnectionService extends Service { if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } return START_STICKY; @@ -536,9 +535,12 @@ public class XmppConnectionService extends Service { public XmppConnection createConnection(Account account) { SharedPreferences sharedPref = getPreferences(); - account.setResource(sharedPref.getString("resource", "mobile") - .toLowerCase(Locale.getDefault())); - XmppConnection connection = new XmppConnection(account, this); + try { + account.setResource(sharedPref.getString("resource", "mobile") + .toLowerCase(Locale.getDefault())); + } catch (final InvalidJidException ignored) { + } + XmppConnection connection = new XmppConnection(account, this); connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); connection.setOnPresencePacketReceivedListener(this.mPresenceParser); @@ -757,7 +759,7 @@ public class XmppConnectionService extends Service { @Override public void onIqPacketReceived(Account account, IqPacket packet) { Element query = packet.query(); - List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>(); + List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); Element storage = query.findChild("storage", "storage:bookmarks"); if (storage != null) { @@ -806,8 +808,14 @@ public class XmppConnectionService extends Service { } for (Bundle phoneContact : phoneContacts) { for (Account account : accounts) { - String jid = phoneContact.getString("jid"); - Contact contact = account.getRoster() + Jid jid; + try { + jid = Jid.fromString(phoneContact.getString("jid")); + } catch (final InvalidJidException e) { + // TODO: Warn if contact import fails here? + break; + } + final Contact contact = account.getRoster() .getContact(jid); String systemAccount = phoneContact .getInt("phoneid") @@ -827,7 +835,7 @@ public class XmppConnectionService extends Service { public List<Conversation> getConversations() { if (this.conversations == null) { - Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>(); + Hashtable<String, Account> accountLookupTable = new Hashtable<>(); for (Account account : this.accounts) { accountLookupTable.put(account.getUuid(), account); } @@ -925,20 +933,20 @@ public class XmppConnectionService extends Service { return null; } - public Conversation find(List<Conversation> haystack, Account account, - String jid) { + public Conversation find(final List<Conversation> haystack, + final Account account, + final Jid jid) { for (Conversation conversation : haystack) { if ((account == null || conversation.getAccount() == account) - && (conversation.getContactJid().split("/", 2)[0] - .equalsIgnoreCase(jid))) { + && (conversation.getContactJid().toBareJid().equals(jid.toBareJid()))) { return conversation; } } return null; } - public Conversation findOrCreateConversation(Account account, String jid, - boolean muc) { + public Conversation findOrCreateConversation(final Account account, final Jid jid, + final boolean muc) { Conversation conversation = find(account, jid); if (conversation != null) { return conversation; @@ -961,7 +969,7 @@ public class XmppConnectionService extends Service { if (contact != null) { conversationName = contact.getDisplayName(); } else { - conversationName = jid.split("@")[0]; + conversationName = jid.getLocalpart(); } if (muc) { conversation = new Conversation(conversationName, account, jid, @@ -1163,13 +1171,12 @@ public class XmppConnectionService extends Service { public void connectMultiModeConversations(Account account) { List<Conversation> conversations = getConversations(); - for (int i = 0; i < conversations.size(); i++) { - Conversation conversation = conversations.get(i); - if ((conversation.getMode() == Conversation.MODE_MULTI) - && (conversation.getAccount() == account)) { - joinMuc(conversation); - } - } + for (Conversation conversation : conversations) { + if ((conversation.getMode() == Conversation.MODE_MULTI) + && (conversation.getAccount() == account)) { + joinMuc(conversation); + } + } } public void joinMuc(Conversation conversation) { @@ -1182,8 +1189,8 @@ public class XmppConnectionService extends Service { String nick = conversation.getMucOptions().getProposedNick(); conversation.getMucOptions().setJoinNick(nick); PresencePacket packet = new PresencePacket(); - String joinJid = conversation.getMucOptions().getJoinJid(); - packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); + final Jid joinJid = conversation.getMucOptions().getJoinJid(); + packet.setTo(conversation.getMucOptions().getJoinJid()); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); if (conversation.getMucOptions().getPassword() != null) { @@ -1260,8 +1267,8 @@ public class XmppConnectionService extends Service { }); options.flagAboutToRename(); PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", options.getJoinJid()); - packet.setAttribute("from", conversation.getAccount().getFullJid()); + packet.setTo(options.getJoinJid()); + packet.setFrom(conversation.getAccount().getFullJid()); String sig = account.getPgpSignature(); if (sig != null) { @@ -1289,8 +1296,8 @@ public class XmppConnectionService extends Service { account.pendingConferenceLeaves.remove(conversation); if (account.getStatus() == Account.STATUS_ONLINE) { PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); - packet.setAttribute("from", conversation.getAccount().getFullJid()); + packet.setTo(conversation.getMucOptions().getJoinJid()); + packet.setFrom(conversation.getAccount().getFullJid()); packet.setAttribute("type", "unavailable"); sendPresencePacket(conversation.getAccount(), packet); conversation.getMucOptions().setOffline(); @@ -1307,20 +1314,19 @@ public class XmppConnectionService extends Service { || (account.getStatus() == Account.STATUS_DISABLED)) { if (!force) { List<Conversation> conversations = getConversations(); - for (int i = 0; i < conversations.size(); i++) { - Conversation conversation = conversations.get(i); - if (conversation.getAccount() == account) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - leaveMuc(conversation); - } else { - if (conversation.endOtrIfNeeded()) { - Log.d(Config.LOGTAG, account.getJid() - + ": ended otr session with " - + conversation.getContactJid()); - } - } - } - } + for (Conversation conversation : conversations) { + if (conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + leaveMuc(conversation); + } else { + if (conversation.endOtrIfNeeded()) { + Log.d(Config.LOGTAG, account.getJid() + + ": ended otr session with " + + conversation.getContactJid()); + } + } + } + } } account.getXmppConnection().disconnect(force); } @@ -1365,24 +1371,23 @@ public class XmppConnectionService extends Service { account.getJid() + " otr session established with " + conversation.getContactJid() + "/" + otrSession.getSessionID().getUserID()); - for (int i = 0; i < messages.size(); ++i) { - Message msg = messages.get(i); - if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING) - && (msg.getEncryption() == Message.ENCRYPTION_OTR)) { - msg.setPresence(otrSession.getSessionID().getUserID()); - if (msg.getType() == Message.TYPE_TEXT) { - MessagePacket outPacket = mMessageGenerator - .generateOtrChat(msg, true); - if (outPacket != null) { - msg.setStatus(Message.STATUS_SEND); - databaseBackend.updateMessage(msg); - sendMessagePacket(account, outPacket); - } - } else if (msg.getType() == Message.TYPE_IMAGE) { - mJingleConnectionManager.createNewConnection(msg); - } - } - } + for (Message msg : messages) { + if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING) + && (msg.getEncryption() == Message.ENCRYPTION_OTR)) { + msg.setPresence(otrSession.getSessionID().getUserID()); + if (msg.getType() == Message.TYPE_TEXT) { + MessagePacket outPacket = mMessageGenerator + .generateOtrChat(msg, true); + if (outPacket != null) { + msg.setStatus(Message.STATUS_SEND); + databaseBackend.updateMessage(msg); + sendMessagePacket(account, outPacket); + } + } else if (msg.getType() == Message.TYPE_IMAGE) { + mJingleConnectionManager.createNewConnection(msg); + } + } + } updateConversationUi(); } @@ -1397,8 +1402,8 @@ public class XmppConnectionService extends Service { packet.setFrom(account.getFullJid()); packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); - packet.setTo(otrSession.getSessionID().getAccountID() + "/" - + otrSession.getSessionID().getUserID()); + packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/" + + otrSession.getSessionID().getUserID()); try { packet.setBody(otrSession .transformSending(CryptoHelper.FILETRANSFER @@ -1596,7 +1601,7 @@ public class XmppConnectionService extends Service { if (account.getStatus() == Account.STATUS_ONLINE) { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); Element item = iq.query("jabber:iq:roster").addChild("item"); - item.setAttribute("jid", contact.getJid()); + item.setAttribute("jid", contact.getJid().toString()); item.setAttribute("subscription", "remove"); account.getXmppConnection().sendIqPacket(iq, null); } @@ -1648,8 +1653,8 @@ public class XmppConnectionService extends Service { } } - public boolean markMessage(Account account, String recipient, String uuid, - int status) { + public boolean markMessage(final Account account, final Jid recipient, final String uuid, + final int status) { if (uuid == null) { return false; } else { @@ -1730,7 +1735,7 @@ public class XmppConnectionService extends Service { } } - public Account findAccountByJid(String accountJid) { + public Account findAccountByJid(final Jid accountJid) { for (Account account : this.accounts) { if (account.getJid().equals(accountJid)) { return account; @@ -1756,7 +1761,7 @@ public class XmppConnectionService extends Service { Log.d(Config.LOGTAG, conversation.getAccount().getJid() + ": sending read marker for " + conversation.getName()); Account account = conversation.getAccount(); - String to = conversation.getContactJid(); + final Jid to = conversation.getContactJid(); this.sendMessagePacket(conversation.getAccount(), mMessageGenerator.confirm(account, to, id)); } @@ -1810,14 +1815,14 @@ public class XmppConnectionService extends Service { } public List<String> getKnownHosts() { - List<String> hosts = new ArrayList<String>(); + List<String> hosts = new ArrayList<>(); for (Account account : getAccounts()) { - if (!hosts.contains(account.getServer())) { - hosts.add(account.getServer()); + if (!hosts.contains(account.getServer().toString())) { + hosts.add(account.getServer().toString()); } for (Contact contact : account.getRoster().getContacts()) { if (contact.showInRoster()) { - String server = contact.getServer(); + final String server = contact.getServer().toString(); if (server != null && !hosts.contains(server)) { hosts.add(server); } @@ -1828,7 +1833,7 @@ public class XmppConnectionService extends Service { } public List<String> getKnownConferenceHosts() { - ArrayList<String> mucServers = new ArrayList<String>(); + ArrayList<String> mucServers = new ArrayList<>(); for (Account account : accounts) { if (account.getXmppConnection() != null) { String server = account.getXmppConnection().getMucServer(); @@ -1891,7 +1896,7 @@ public class XmppConnectionService extends Service { } public List<Contact> findContacts(String jid) { - ArrayList<Contact> contacts = new ArrayList<Contact>(); + ArrayList<Contact> contacts = new ArrayList<>(); for (Account account : getAccounts()) { if (!account.isOptionSet(Account.OPTION_DISABLED)) { Contact contact = account.getRoster().getContactFromRoster(jid); @@ -1931,7 +1936,7 @@ public class XmppConnectionService extends Service { } public void resendFailedMessages(Message message) { - List<Message> messages = new ArrayList<Message>(); + List<Message> messages = new ArrayList<>(); Message current = message; while(current.getStatus() == Message.STATUS_SEND_FAILED) { messages.add(current); diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index f101e883..8c1a8dea 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -11,6 +11,7 @@ import de.measite.minidns.record.AAAA; import de.measite.minidns.record.Data; import de.measite.minidns.util.NameUtil; import eu.siacs.conversations.Config; +import eu.siacs.conversations.xmpp.jid.Jid; import java.io.IOException; import java.net.InetAddress; @@ -26,7 +27,8 @@ import android.util.Log; public class DNSHelper { protected static Client client = new Client(); - public static Bundle getSRVRecord(String host) throws IOException { + public static Bundle getSRVRecord(final Jid jid) throws IOException { + final String host = jid.getDomainpart(); String dns[] = client.findDNS(); if (dns != null) { @@ -62,9 +64,9 @@ public class DNSHelper { // a random order respecting the weight, and dump that priority by // priority - TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<Integer, ArrayList<SRV>>(); - TreeMap<String, ArrayList<String>> ips4 = new TreeMap<String, ArrayList<String>>(); - TreeMap<String, ArrayList<String>> ips6 = new TreeMap<String, ArrayList<String>>(); + TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>(); + TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>(); + TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>(); for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { @@ -97,7 +99,7 @@ public class DNSHelper { } Random rnd = new Random(); - ArrayList<SRV> result = new ArrayList<SRV>( + ArrayList<SRV> result = new ArrayList<>( priorities.size() * 2 + 1); for (ArrayList<SRV> s : priorities.values()) { @@ -136,7 +138,7 @@ public class DNSHelper { bundle.putString("error", "nosrv"); return bundle; } - ArrayList<Bundle> values = new ArrayList<Bundle>(); + ArrayList<Bundle> values = new ArrayList<>(); for (SRV srv : result) { Bundle namePort = new Bundle(); namePort.putString("name", srv.getName()); diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 2b9d6632..9c160384 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -48,6 +48,8 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.XmlReader; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; @@ -76,10 +78,12 @@ public class XmppConnection implements Runnable { private boolean shouldBind = true; private boolean shouldAuthenticate = true; private Element streamFeatures; - private HashMap<String, List<String>> disco = new HashMap<String, List<String>>(); + private HashMap<String, List<String>> disco = new HashMap<>(); + private String streamId = null; private int smVersion = 3; - private SparseArray<String> messageReceipts = new SparseArray<String>(); + private SparseArray<String> messageReceipts = new SparseArray<>(); + private boolean usingCompression = false; private boolean usingEncryption = false; private int stanzasReceived = 0; @@ -89,7 +93,7 @@ public class XmppConnection implements Runnable { private long lastConnect = 0; private long lastSessionStarted = 0; private int attempt = 0; - private Hashtable<String, PacketReceived> packetCallbacks = new Hashtable<String, PacketReceived>(); + private Hashtable<String, PacketReceived> packetCallbacks = new Hashtable<>(); private OnPresencePacketReceived presenceListener = null; private OnJinglePacketReceived jingleListener = null; private OnIqPacketReceived unregisteredIqListener = null; @@ -102,7 +106,7 @@ public class XmppConnection implements Runnable { public XmppConnection(Account account, XmppConnectionService service) { this.account = account; this.wakeLock = service.getPowerManager().newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, account.getJid()); + PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toString()); tagWriter = new TagWriter(); mXmppConnectionService = service; applicationContext = service.getApplicationContext(); @@ -127,7 +131,7 @@ public class XmppConnection implements Runnable { } protected void connect() { - Log.d(Config.LOGTAG, account.getJid() + ": connecting"); + Log.d(Config.LOGTAG, account.getJid().toString() + ": connecting"); usingCompression = false; usingEncryption = false; lastConnect = SystemClock.elapsedRealtime(); @@ -143,7 +147,7 @@ public class XmppConnection implements Runnable { Bundle result = DNSHelper.getSRVRecord(account.getServer()); ArrayList<Parcelable> values = result.getParcelableArrayList("values"); if ("timeout".equals(result.getString("error"))) { - Log.d(Config.LOGTAG, account.getJid() + ": dns timeout"); + Log.d(Config.LOGTAG, account.getJid().toString() + ": dns timeout"); this.changeStatus(Account.STATUS_OFFLINE); return; } else if (values != null) { @@ -158,12 +162,12 @@ public class XmppConnection implements Runnable { InetSocketAddress addr; if (srvIpServer != null) { addr = new InetSocketAddress(srvIpServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": using values from dns " + srvRecordServer + "[" + srvIpServer + "]:" + srvRecordPort); } else { addr = new InetSocketAddress(srvRecordServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": using values from dns " + srvRecordServer + ":" + srvRecordPort); } @@ -171,10 +175,10 @@ public class XmppConnection implements Runnable { socket.connect(addr, 20000); socketError = false; } catch (UnknownHostException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, account.getJid().toString() + ": " + e.getMessage()); i++; } catch (IOException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, account.getJid().toString() + ": " + e.getMessage()); i++; } } @@ -183,16 +187,16 @@ public class XmppConnection implements Runnable { if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } return; } } else if (result.containsKey("error") && "nosrv".equals(result.getString("error", null))) { - socket = new Socket(account.getServer(), 5222); + socket = new Socket(account.getServer().getDomainpart(), 5222); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": timeout in DNS resolution"); changeStatus(Account.STATUS_OFFLINE); return; @@ -222,51 +226,38 @@ public class XmppConnection implements Runnable { if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } - return; - } catch (IOException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); + } catch (final IOException | XmlPullParserException e) { + Log.d(Config.LOGTAG, account.getJid().toString() + ": " + e.getMessage()); this.changeStatus(Account.STATUS_OFFLINE); if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } - return; - } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); + } catch (NoSuchAlgorithmException e) { + Log.d(Config.LOGTAG, account.getJid().toString() + ": " + e.getMessage()); this.changeStatus(Account.STATUS_OFFLINE); Log.d(Config.LOGTAG, "compression exception " + e.getMessage()); if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { - } - } - return; - } catch (XmlPullParserException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); - this.changeStatus(Account.STATUS_OFFLINE); - if (wakeLock.isHeld()) { - try { - wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } - return; - } + } - } + } @Override public void run() { connect(); } - private void processStream(Tag currentTag) throws XmlPullParserException, + private void processStream(final Tag currentTag) throws XmlPullParserException, IOException, NoSuchAlgorithmException { Tag nextTag = tagReader.readTag(); while ((nextTag != null) && (!nextTag.isEnd("stream"))) { @@ -279,7 +270,7 @@ public class XmppConnection implements Runnable { } else if (nextTag.isStart("compressed")) { switchOverToZLib(nextTag); } else if (nextTag.isStart("success")) { - Log.d(Config.LOGTAG, account.getJid() + ": logged in"); + Log.d(Config.LOGTAG, account.getJid().toString() + ": logged in"); tagReader.readTag(); tagReader.reset(); sendStartStream(); @@ -300,11 +291,11 @@ public class XmppConnection implements Runnable { Element enabled = tagReader.readElement(nextTag); if ("true".equals(enabled.getAttribute("resume"))) { this.streamId = enabled.getAttribute("id"); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": stream managment(" + smVersion + ") enabled (resumable)"); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": stream managment(" + smVersion + ") enabled"); } this.lastSessionStarted = SystemClock.elapsedRealtime(); @@ -318,11 +309,11 @@ public class XmppConnection implements Runnable { try { int serverCount = Integer.parseInt(h); if (serverCount != stanzasSent) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": session resumed with lost packages"); stanzasSent = serverCount; } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": session resumed"); } if (acknowledgedListener != null) { @@ -334,7 +325,7 @@ public class XmppConnection implements Runnable { } } messageReceipts.clear(); - } catch (NumberFormatException e) { + } catch (final NumberFormatException ignored) { } sendInitialPing(); @@ -357,7 +348,7 @@ public class XmppConnection implements Runnable { } } else if (nextTag.isStart("failed")) { tagReader.readElement(nextTag); - Log.d(Config.LOGTAG, account.getJid() + ": resumption failed"); + Log.d(Config.LOGTAG, account.getJid().toString() + ": resumption failed"); streamId = null; if (account.getStatus() != Account.STATUS_ONLINE) { sendBindRequest(); @@ -380,7 +371,7 @@ public class XmppConnection implements Runnable { } private void sendInitialPing() { - Log.d(Config.LOGTAG, account.getJid() + ": sending intial ping"); + Log.d(Config.LOGTAG, account.getJid().toString() + ": sending intial ping"); IqPacket iq = new IqPacket(IqPacket.TYPE_GET); iq.setFrom(account.getFullJid()); iq.addChild("ping", "urn:xmpp:ping"); @@ -388,7 +379,7 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toString() + ": online with resource " + account.getResource()); changeStatus(Account.STATUS_ONLINE); } @@ -507,7 +498,7 @@ public class XmppConnection implements Runnable { tagWriter.writeElement(compress); } - private void switchOverToZLib(Tag currentTag) + private void switchOverToZLib(final Tag currentTag) throws XmlPullParserException, IOException, NoSuchAlgorithmException { tagReader.readTag(); // read tag close @@ -537,7 +528,7 @@ public class XmppConnection implements Runnable { return getPreferences().getBoolean("enable_legacy_ssl", false); } - private void switchOverToTls(Tag currentTag) throws XmlPullParserException, + private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException { tagReader.readTag(); try { @@ -551,24 +542,23 @@ public class XmppConnection implements Runnable { throw new IOException("SSLSocketFactory was null"); } - HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier()); + final HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier()); if (socket == null) { throw new IOException("socket was null"); } - SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, + final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); // Support all protocols except legacy SSL. // The min SDK version prevents us having to worry about SSLv2. In - // future, this may be - // true of SSLv3 as well. + // future, this may be true of SSLv3 as well. final String[] supportProtocols; if (enableLegacySSL()) { supportProtocols = sslSocket.getSupportedProtocols(); } else { - final List<String> supportedProtocols = new LinkedList<String>( + final List<String> supportedProtocols = new LinkedList<>( Arrays.asList(sslSocket.getSupportedProtocols())); supportedProtocols.remove("SSLv3"); supportProtocols = new String[supportedProtocols.size()]; @@ -577,7 +567,7 @@ public class XmppConnection implements Runnable { sslSocket.setEnabledProtocols(supportProtocols); if (verifier != null - && !verifier.verify(account.getServer(), + && !verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) { sslSocket.close(); throw new IOException("host mismatch in TLS connection"); @@ -590,12 +580,10 @@ public class XmppConnection implements Runnable { usingEncryption = true; processStream(tagReader.readTag()); sslSocket.close(); - } catch (NoSuchAlgorithmException e1) { + } catch (final NoSuchAlgorithmException | KeyManagementException e1) { e1.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); } - } + } private void sendSaslAuthPlain() throws IOException { String saslString = CryptoHelper.saslPlain(account.getUsername(), @@ -676,7 +664,7 @@ public class XmppConnection implements Runnable { } private List<String> extractMechanisms(Element stream) { - ArrayList<String> mechanisms = new ArrayList<String>(stream + ArrayList<String> mechanisms = new ArrayList<>(stream .getChildren().size()); for (Element child : stream.getChildren()) { mechanisms.add(child.getContent()); @@ -742,10 +730,14 @@ public class XmppConnection implements Runnable { public void onIqPacketReceived(Account account, IqPacket packet) { Element bind = packet.findChild("bind"); if (bind != null) { - Element jid = bind.findChild("jid"); + final Element jid = bind.findChild("jid"); if (jid != null && jid.getContent() != null) { - account.setResource(jid.getContent().split("/", 2)[1]); - if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) { + try { + account.setResource(Jid.fromString(jid.getContent()).getResourcepart()); + } catch (final InvalidJidException e) { + // TODO: Handle the case where an external JID is technically invalid? + } + if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) { smVersion = 3; EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); @@ -783,24 +775,24 @@ public class XmppConnection implements Runnable { } } - private void sendServiceDiscoveryInfo(final String server) { - IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - iq.setTo(server); + private void sendServiceDiscoveryInfo(final Jid server) { + final IqPacket iq = new IqPacket(IqPacket.TYPE_GET); + iq.setTo(server.toDomainJid()); iq.query("http://jabber.org/protocol/disco#info"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - List<Element> elements = packet.query().getChildren(); - List<String> features = new ArrayList<String>(); - for (int i = 0; i < elements.size(); ++i) { - if (elements.get(i).getName().equals("feature")) { - features.add(elements.get(i).getAttribute("var")); - } - } - disco.put(server, features); - - if (account.getServer().equals(server)) { + final List<Element> elements = packet.query().getChildren(); + final List<String> features = new ArrayList<>(); + for (Element element : elements) { + if (element.getName().equals("feature")) { + features.add(element.getAttribute("var")); + } + } + disco.put(server.toDomainJid().toString(), features); + + if (account.getServer().equals(server.toDomainJid())) { enableAdvancedStreamFeatures(); } } @@ -813,21 +805,25 @@ public class XmppConnection implements Runnable { } } - private void sendServiceDiscoveryItems(final String server) { - IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - iq.setTo(server); + private void sendServiceDiscoveryItems(final Jid server) { + final IqPacket iq = new IqPacket(IqPacket.TYPE_GET); + iq.setTo(server.toDomainJid()); iq.query("http://jabber.org/protocol/disco#items"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { List<Element> elements = packet.query().getChildren(); - for (int i = 0; i < elements.size(); ++i) { - if (elements.get(i).getName().equals("item")) { - String jid = elements.get(i).getAttribute("jid"); - sendServiceDiscoveryInfo(jid); - } - } + for (Element element : elements) { + if (element.getName().equals("item")) { + final String jid = element.getAttribute("jid"); + try { + sendServiceDiscoveryInfo(Jid.fromString(jid).toDomainJid()); + } catch (final InvalidJidException ignored) { + // TODO: Handle the case where an external JID is technically invalid? + } + } + } } }); } @@ -854,9 +850,13 @@ public class XmppConnection implements Runnable { throws XmlPullParserException, IOException { Element streamError = tagReader.readElement(currentTag); if (streamError != null && streamError.hasChild("conflict")) { - String resource = account.getResource().split("\\.")[0]; - account.setResource(resource + "." + nextRandomId()); - Log.d(Config.LOGTAG, + final String resource = account.getResource().split("\\.")[0]; + try { + account.setResource(resource + "." + nextRandomId()); + } catch (final InvalidJidException ignored) { + // Should never reach here. + } + Log.d(Config.LOGTAG, account.getJid() + ": switching resource due to conflict (" + account.getResource() + ")"); } @@ -864,8 +864,8 @@ public class XmppConnection implements Runnable { private void sendStartStream() throws IOException { Tag stream = Tag.start("stream:stream"); - stream.setAttribute("from", account.getJid()); - stream.setAttribute("to", account.getServer()); + stream.setAttribute("from", account.getJid().toString()); + stream.setAttribute("to", account.getServer().toString()); stream.setAttribute("version", "1.0"); stream.setAttribute("xml:lang", "en"); stream.setAttribute("xmlns", "jabber:client"); @@ -1003,7 +1003,7 @@ public class XmppConnection implements Runnable { } public List<String> findDiscoItemsByFeature(String feature) { - List<String> items = new ArrayList<String>(); + final List<String> items = new ArrayList<>(); for (Entry<String, List<String>> cursor : disco.entrySet()) { if (cursor.getValue().contains(feature)) { items.add(cursor.getKey()); @@ -1079,12 +1079,10 @@ public class XmppConnection implements Runnable { this.connection = connection; } - private boolean hasDiscoFeature(String server, String feature) { - if (!connection.disco.containsKey(server)) { - return false; - } - return connection.disco.get(server).contains(feature); - } + private boolean hasDiscoFeature(final Jid server, final String feature) { + return connection.disco.containsKey(server.toDomainJid().toString()) && + connection.disco.get(server.toDomainJid().toString()).contains(feature); + } public boolean carbons() { return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2"); @@ -1095,12 +1093,7 @@ public class XmppConnection implements Runnable { } public boolean csi() { - if (connection.streamFeatures == null) { - return false; - } else { - return connection.streamFeatures.hasChild("csi", - "urn:xmpp:csi:0"); - } + return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0"); } public boolean pubsub() { @@ -1113,11 +1106,7 @@ public class XmppConnection implements Runnable { } public boolean rosterVersioning() { - if (connection.streamFeatures == null) { - return false; - } else { - return connection.streamFeatures.hasChild("ver"); - } + return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver"); } public boolean streamhost() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index 7d8f702f..2bbde7ab 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -19,7 +19,7 @@ public final class Jid { private final String displayjid; public String getLocalpart() { - return IDN.toUnicode(localpart); + return localpart; } public String getDomainpart() { @@ -27,10 +27,10 @@ public final class Jid { } public String getResourcepart() { - return IDN.toUnicode(resourcepart); + return resourcepart; } - public Jid fromString(final String jid) throws InvalidJidException { + public static Jid fromString(final String jid) throws InvalidJidException { return new Jid(jid); } @@ -121,11 +121,20 @@ public final class Jid { this.displayjid = finaljid; } - public Jid getBareJid() { + public Jid toBareJid() { try { return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, ""); } catch (final InvalidJidException e) { - // This should never happen due to the contracts we have in place. + // This should never happen. + return null; + } + } + + public Jid toDomainJid() { + try { + return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart()); + } catch (final InvalidJidException e) { + // This should never happen. return null; } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java index 154fadf6..9f5ac988 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java +++ b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -1,6 +1,8 @@ package eu.siacs.conversations.xmpp.pep; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.util.Base64; public class Avatar { @@ -10,7 +12,7 @@ public class Avatar { public int height; public int width; public long size; - public String owner; + public Jid owner; public byte[] getImageAsBytes() { return Base64.decode(image, Base64.DEFAULT); diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java index eef41c79..ea8f64a9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp.stanzas; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; public class AbstractStanza extends Element { @@ -20,15 +21,15 @@ public class AbstractStanza extends Element { return this.getAttribute("id"); } - public void setTo(String to) { - setAttribute("to", to); + public void setTo(final Jid to) { + setAttribute("to", to.toString()); } - public void setFrom(String from) { - setAttribute("from", from); + public void setFrom(final Jid from) { + setAttribute("from", from.toString()); } - public void setId(String id) { + public void setId(final String id) { setAttribute("id", id); } } |