diff options
Diffstat (limited to 'src/main')
55 files changed, 1476 insertions, 1079 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 225091f9..30622887 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="eu.siacs.conversations"> +<manifest + package="eu.siacs.conversations" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> @@ -112,7 +113,10 @@ <data android:mimeType="image/*" /> </intent-filter> </activity> - <activity android:name="de.duenndns.ssl.MemorizingActivity" /> + <activity + android:name="de.duenndns.ssl.MemorizingActivity" + android:theme="@style/ConversationsTheme" + tools:replace="android:theme"/> <activity android:name=".ui.AboutActivity" android:label="@string/title_activity_about" diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java index e0bd0e79..912eea77 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) { @@ -155,11 +153,11 @@ public class OtrEngine implements OtrEngineHost { public void injectMessage(SessionID session, String body) throws OtrException { MessagePacket packet = new MessagePacket(); - packet.setFrom(account.getFullJid()); + packet.setFrom(account.getJid()); 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..3d7cc671 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().toBareJid().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().toBareJid().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().toBareJid().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().toBareJid().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().toBareJid().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().toBareJid().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().toBareJid().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..8b6a7bd7 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -1,9 +1,8 @@ package eu.siacs.conversations.entities; -import java.security.interfaces.DSAPublicKey; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CopyOnWriteArrayList; +import android.content.ContentValues; +import android.database.Cursor; +import android.os.SystemClock; import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoException; @@ -11,14 +10,17 @@ import net.java.otr4j.crypto.OtrCryptoException; import org.json.JSONException; import org.json.JSONObject; +import java.security.interfaces.DSAPublicKey; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.OtrEngine; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; -import android.content.ContentValues; -import android.database.Cursor; -import android.os.SystemClock; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Account extends AbstractEntity { @@ -49,56 +51,68 @@ public class Account extends AbstractEntity { public static final int STATUS_REGISTRATION_CONFLICT = 8; public static final int STATUS_REGISTRATION_SUCCESSFULL = 9; public static final int STATUS_REGISTRATION_NOT_SUPPORTED = 10; - - protected String username; - protected String server; + public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>(); + public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>(); + 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; - protected boolean online = false; - private OtrEngine otrEngine = null; private XmppConnection xmppConnection = null; private Presences presences = new Presences(); private long mEndGracePeriod = 0L; 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 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.isBareJid()) { + this.setResource("mobile"); + } 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; } + public static Account fromCursor(Cursor cursor) { + 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)), + cursor.getString(cursor.getColumnIndex(KEYS)), + cursor.getString(cursor.getColumnIndex(AVATAR))); + } + public boolean isOptionSet(int option) { return ((options & (1 << option)) != 0); } @@ -112,33 +126,29 @@ 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) { - this.status = status; - } - public int getStatus() { if (isOptionSet(OPTION_DISABLED)) { return STATUS_DISABLED; @@ -147,6 +157,10 @@ public class Account extends AbstractEntity { } } + public void setStatus(final int status) { + this.status = status; + } + public boolean errorStatus() { int s = getStatus(); return (s == STATUS_REGISTRATION_FAILED @@ -156,25 +170,22 @@ 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 String getResource() { + return jid.getResourcepart(); } - public String getResource() { - return this.resource; + public void setResource(final String resource) { + try { + jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource); + } catch (final InvalidJidException ignored) { + } } - public String getJid() { - return username.toLowerCase(Locale.getDefault()) + "@" - + server.toLowerCase(Locale.getDefault()); + public Jid getJid() { + return jid; } public JSONObject getKeys() { @@ -210,8 +221,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()); @@ -220,17 +231,6 @@ public class Account extends AbstractEntity { return values; } - public static Account fromCursor(Cursor cursor) { - return new Account(cursor.getString(cursor.getColumnIndex(UUID)), - cursor.getString(cursor.getColumnIndex(USERNAME)), - cursor.getString(cursor.getColumnIndex(SERVER)), - cursor.getString(cursor.getColumnIndex(PASSWORD)), - cursor.getInt(cursor.getColumnIndex(OPTIONS)), - cursor.getString(cursor.getColumnIndex(ROSTERVERSION)), - cursor.getString(cursor.getColumnIndex(KEYS)), - cursor.getString(cursor.getColumnIndex(AVATAR))); - } - public OtrEngine getOtrEngine(XmppConnectionService context) { if (otrEngine == null) { otrEngine = new OtrEngine(context, this); @@ -246,10 +246,6 @@ public class Account extends AbstractEntity { this.xmppConnection = connection; } - public String getFullJid() { - return this.getJid() + "/" + this.resource; - } - public String getOtrFingerprint() { if (this.otrFingerprint == null) { try { @@ -265,7 +261,7 @@ public class Account extends AbstractEntity { builder.insert(26, " "); builder.insert(35, " "); this.otrFingerprint = builder.toString(); - } catch (OtrCryptoException e) { + } catch (final OtrCryptoException ignored) { } } @@ -324,17 +320,17 @@ public class Account extends AbstractEntity { return this.roster; } - public void setBookmarks(List<Bookmark> bookmarks) { - this.bookmarks = bookmarks; - } - public List<Bookmark> getBookmarks() { return this.bookmarks; } - public boolean hasBookmarkFor(String conferenceJid) { + public void setBookmarks(List<Bookmark> bookmarks) { + this.bookmarks = bookmarks; + } + + public boolean hasBookmarkFor(final Jid conferenceJid) { for (Bookmark bmark : this.bookmarks) { - if (bmark.getJid().equals(conferenceJid)) { + if (bmark.getJid().equals(conferenceJid.toBareJid())) { return true; } } @@ -357,30 +353,30 @@ public class Account extends AbstractEntity { public int getReadableStatusId() { switch (getStatus()) { - case Account.STATUS_DISABLED: - return R.string.account_status_disabled; - case Account.STATUS_ONLINE: - return R.string.account_status_online; - case Account.STATUS_CONNECTING: - return R.string.account_status_connecting; - case Account.STATUS_OFFLINE: - return R.string.account_status_offline; - case Account.STATUS_UNAUTHORIZED: - return R.string.account_status_unauthorized; - case Account.STATUS_SERVER_NOT_FOUND: - return R.string.account_status_not_found; - case Account.STATUS_NO_INTERNET: - return R.string.account_status_no_internet; - case Account.STATUS_REGISTRATION_FAILED: - return R.string.account_status_regis_fail; - case Account.STATUS_REGISTRATION_CONFLICT: - return R.string.account_status_regis_conflict; - case Account.STATUS_REGISTRATION_SUCCESSFULL: - return R.string.account_status_regis_success; - case Account.STATUS_REGISTRATION_NOT_SUPPORTED: - return R.string.account_status_regis_not_sup; - default: - return R.string.account_status_unknown; + case Account.STATUS_DISABLED: + return R.string.account_status_disabled; + case Account.STATUS_ONLINE: + return R.string.account_status_online; + case Account.STATUS_CONNECTING: + return R.string.account_status_connecting; + case Account.STATUS_OFFLINE: + return R.string.account_status_offline; + case Account.STATUS_UNAUTHORIZED: + return R.string.account_status_unauthorized; + case Account.STATUS_SERVER_NOT_FOUND: + return R.string.account_status_not_found; + case Account.STATUS_NO_INTERNET: + return R.string.account_status_no_internet; + case Account.STATUS_REGISTRATION_FAILED: + return R.string.account_status_regis_fail; + case Account.STATUS_REGISTRATION_CONFLICT: + return R.string.account_status_regis_conflict; + case Account.STATUS_REGISTRATION_SUCCESSFULL: + return R.string.account_status_regis_success; + case Account.STATUS_REGISTRATION_NOT_SUPPORTED: + return R.string.account_status_regis_not_sup; + default: + return R.string.account_status_unknown; } } 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..9a827f85 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,33 +72,35 @@ public class Contact implements ListItem { this.avatar = avatar; } - public Contact(final String jid) { + public Contact(final Jid jid) { this.jid = jid; } public String getDisplayName() { if (this.systemName != null) { - return this.systemName; - } else if (this.serverName != null) { - return this.serverName; + return this.systemName; + } else if (this.serverName != null) { + return this.serverName; } else if (this.presenceName != null) { - return this.presenceName; + return this.presenceName; + } else if (jid.hasLocalpart()) { + return jid.getLocalpart(); } else { - return this.jid.split("@")[0]; - } + return jid.getDomainpart(); + } } public String getProfilePhoto() { 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 +110,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 +125,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 +207,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 +234,7 @@ public class Contact implements ListItem { } fingerprints.put(print); this.keys.put("otr_fingerprints", fingerprints); - } catch (JSONException e) { + } catch (final JSONException ignored) { } } @@ -233,7 +242,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 +282,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 +315,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 +349,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..c8dedd7b 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,16 +48,16 @@ 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; private JSONObject attributes = new JSONObject(); - private String nextPresence; + private Jid nextCounterpart; - 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,16 +341,16 @@ public class Conversation extends AbstractEntity { this.mucOptions = null; } - public void setContactJid(String jid) { + public void setContactJid(final Jid jid) { this.contactJid = jid; } - public void setNextPresence(String presence) { - this.nextPresence = presence; + public void setNextCounterpart(Jid jid) { + this.nextCounterpart = jid; } - public String getNextPresence() { - return this.nextPresence; + public Jid getNextCounterpart() { + return this.nextCounterpart; } public int getLatestEncryption() { diff --git a/src/main/java/eu/siacs/conversations/entities/Downloadable.java b/src/main/java/eu/siacs/conversations/entities/Downloadable.java index 70516b20..e4c85336 100644 --- a/src/main/java/eu/siacs/conversations/entities/Downloadable.java +++ b/src/main/java/eu/siacs/conversations/entities/Downloadable.java @@ -2,8 +2,8 @@ package eu.siacs.conversations.entities; public interface Downloadable { - public final String[] VALID_EXTENSIONS = { "webp", "jpeg", "jpg", "png" }; - public final String[] VALID_CRYPTO_EXTENSIONS = { "pgp", "gpg", "otr" }; + public final String[] VALID_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"}; + public final String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"}; public static final int STATUS_UNKNOWN = 0x200; public static final int STATUS_CHECKING = 0x201; 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..c4bc86ff 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -1,12 +1,15 @@ package eu.siacs.conversations.entities; +import android.content.ContentValues; +import android.database.Cursor; + import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import eu.siacs.conversations.Config; -import android.content.ContentValues; -import android.database.Cursor; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Message extends AbstractEntity { @@ -42,9 +45,9 @@ public class Message extends AbstractEntity { public static String STATUS = "status"; public static String TYPE = "type"; public static String REMOTE_MSG_ID = "remoteMsgId"; - + public boolean markable = false; protected String conversationUuid; - protected String counterpart; + protected Jid counterpart; protected String trueCounterpart; protected String body; protected String encryptedBody; @@ -54,11 +57,8 @@ public class Message extends AbstractEntity { protected int type; protected boolean read = true; protected String remoteMsgId = null; - protected Conversation conversation = null; protected Downloadable downloadable = null; - public boolean markable = false; - private Message mNextMessage = null; private Message mPreviousMessage = null; @@ -74,17 +74,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; @@ -97,12 +97,42 @@ public class Message extends AbstractEntity { this.remoteMsgId = remoteMsgId; } + public static Message fromCursor(Cursor cursor) { + 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)), + jid, + cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)), + cursor.getString(cursor.getColumnIndex(BODY)), + cursor.getLong(cursor.getColumnIndex(TIME_SENT)), + cursor.getInt(cursor.getColumnIndex(ENCRYPTION)), + cursor.getInt(cursor.getColumnIndex(STATUS)), + cursor.getInt(cursor.getColumnIndex(TYPE)), + cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID))); + } + + public static Message createStatusMessage(Conversation conversation) { + Message message = new Message(); + message.setType(Message.TYPE_STATUS); + message.setConversation(conversation); + return message; + } + @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); values.put(UUID, uuid); values.put(CONVERSATION, conversationUuid); - values.put(COUNTERPART, counterpart); + if (counterpart == null) { + values.putNull(COUNTERPART); + } else { + values.put(COUNTERPART, counterpart.toString()); + } values.put(TRUE_COUNTERPART, trueCounterpart); values.put(BODY, body); values.put(TIME_SENT, timeSent); @@ -121,10 +151,18 @@ public class Message extends AbstractEntity { return this.conversation; } - public String getCounterpart() { + public void setConversation(Conversation conv) { + this.conversation = conv; + } + + public Jid getCounterpart() { return counterpart; } + public void setCounterpart(final Jid counterpart) { + this.counterpart = counterpart; + } + public Contact getContact() { if (this.conversation.getMode() == Conversation.MODE_SINGLE) { return this.conversation.getContact(); @@ -142,6 +180,10 @@ public class Message extends AbstractEntity { return body; } + public void setBody(String body) { + this.body = body; + } + public long getTimeSent() { return timeSent; } @@ -150,10 +192,18 @@ public class Message extends AbstractEntity { return encryption; } + public void setEncryption(int encryption) { + this.encryption = encryption; + } + public int getStatus() { return status; } + public void setStatus(int status) { + this.status = status; + } + public String getRemoteMsgId() { return this.remoteMsgId; } @@ -162,27 +212,6 @@ public class Message extends AbstractEntity { this.remoteMsgId = id; } - public static Message fromCursor(Cursor cursor) { - return new Message(cursor.getString(cursor.getColumnIndex(UUID)), - cursor.getString(cursor.getColumnIndex(CONVERSATION)), - cursor.getString(cursor.getColumnIndex(COUNTERPART)), - cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)), - cursor.getString(cursor.getColumnIndex(BODY)), - cursor.getLong(cursor.getColumnIndex(TIME_SENT)), - cursor.getInt(cursor.getColumnIndex(ENCRYPTION)), - cursor.getInt(cursor.getColumnIndex(STATUS)), - cursor.getInt(cursor.getColumnIndex(TYPE)), - cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID))); - } - - public void setConversation(Conversation conv) { - this.conversation = conv; - } - - public void setStatus(int status) { - this.status = status; - } - public boolean isRead() { return this.read; } @@ -199,14 +228,6 @@ public class Message extends AbstractEntity { this.timeSent = time; } - public void setEncryption(int encryption) { - this.encryption = encryption; - } - - public void setBody(String body) { - this.body = body; - } - public String getEncryptedBody() { return this.encryptedBody; } @@ -215,57 +236,24 @@ public class Message extends AbstractEntity { this.encryptedBody = body; } - public void setType(int type) { - this.type = type; - } - public int getType() { return this.type; } - public void setPresence(String presence) { - if (presence == null) { - this.counterpart = this.counterpart.split("/", 2)[0]; - } else { - this.counterpart = this.counterpart.split("/", 2)[0] + "/" - + presence; - } + public void setType(int type) { + this.type = type; } public void setTrueCounterpart(String trueCounterpart) { this.trueCounterpart = trueCounterpart; } - public String getPresence() { - String[] counterparts = this.counterpart.split("/", 2); - if (counterparts.length == 2) { - return counterparts[1]; - } else { - if (this.counterpart.contains("/")) { - return ""; - } else { - return null; - } - } - } - - public void setDownloadable(Downloadable downloadable) { - this.downloadable = downloadable; - } - public Downloadable getDownloadable() { return this.downloadable; } - public static Message createStatusMessage(Conversation conversation) { - Message message = new Message(); - message.setType(Message.TYPE_STATUS); - message.setConversation(conversation); - return message; - } - - public void setCounterpart(String counterpart) { - this.counterpart = counterpart; + public void setDownloadable(Downloadable downloadable) { + this.downloadable = downloadable; } public boolean equals(Message message) { @@ -320,13 +308,14 @@ public class Message extends AbstractEntity { && message.getEncryption() != Message.ENCRYPTION_PGP && this.getType() == message.getType() && this.getEncryption() == message.getEncryption() + && this.getCounterpart() != null && this.getCounterpart().equals(message.getCounterpart()) && (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && ((this .getStatus() == message.getStatus() || ((this.getStatus() == Message.STATUS_SEND || this .getStatus() == Message.STATUS_SEND_RECEIVED) && (message .getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND || message - .getStatus() == Message.STATUS_SEND_DISPLAYED)))) + .getStatus() == Message.STATUS_SEND_DISPLAYED)))) && !message.bodyContainsDownloadable() && !this.bodyContainsDownloadable()); } @@ -359,13 +348,9 @@ 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() { Contact contact = this.getContact(); return (status > STATUS_RECEIVED || (contact != null && contact.trusted())); @@ -391,14 +376,14 @@ public class Message extends AbstractEntity { String[] extensionParts = filename.split("\\."); if (extensionParts.length == 2 && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains( - extensionParts[extensionParts.length - 1])) { + extensionParts[extensionParts.length - 1])) { return true; } else if (extensionParts.length == 3 && Arrays - .asList(Downloadable.VALID_CRYPTO_EXTENSIONS) - .contains(extensionParts[extensionParts.length - 1]) + .asList(Downloadable.VALID_CRYPTO_EXTENSIONS) + .contains(extensionParts[extensionParts.length - 1]) && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains( - extensionParts[extensionParts.length - 2])) { + extensionParts[extensionParts.length - 2])) { return true; } else { return false; @@ -410,7 +395,7 @@ public class Message extends AbstractEntity { public ImageParams getImageParams() { ImageParams params = getLegacyImageParams(); - if (params!=null) { + if (params != null) { return params; } params = new ImageParams(); @@ -473,7 +458,7 @@ public class Message extends AbstractEntity { } return params; } - + public ImageParams getLegacyImageParams() { ImageParams params = new ImageParams(); if (body == null) { diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index d7407cd5..e25c6b89 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; @@ -145,9 +152,9 @@ public class MucOptions { } public void processPacket(PresencePacket packet, PgpEngine pgp) { - String[] fromParts = packet.getFrom().split("/", 2); - if (fromParts.length >= 2) { - String name = fromParts[1]; + final Jid from = packet.getFrom(); + if (!from.isBareJid()) { + final String name = from.getResourcepart(); String type = packet.getAttribute("type"); if (type == null) { User user = new User(); @@ -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().isBareJid()) { + 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()) { @@ -366,4 +376,4 @@ public class MucOptions { public Conversation getConversation() { return this.conversation; } -}
\ No newline at end of file +} 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/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index dde01789..d1b76c3f 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -20,7 +20,7 @@ public abstract class AbstractGenerator { "http://jabber.org/protocol/disco#info", "urn:xmpp:avatar:metadata+notify", "urn:xmpp:ping"}; - public final String IDENTITY_NAME = "Conversations 0.8.2"; + public final String IDENTITY_NAME = "Conversations 0.8.3"; public final String IDENTITY_TYPE = "phone"; protected XmppConnectionService mXmppConnectionService; diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index d44bf0ca..5d674748 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.setTo(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..65f776ff 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,10 +35,10 @@ 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()); + packet.setFrom(account.getJid()); packet.setId(message.getUuid()); if (addDelay) { addDelay(packet, message.getTimeSent()); @@ -113,17 +114,17 @@ public class MessageGenerator extends AbstractGenerator { private MessagePacket generateError(MessagePacket origin) { MessagePacket packet = new MessagePacket(); packet.setId(origin.getId()); - packet.setTo(origin.getFrom()); + packet.setTo(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); - packet.setFrom(account.getFullJid()); + packet.setFrom(account.getJid()); Element received = packet.addChild("displayed", "urn:xmpp:chat-markers:0"); received.setAttribute("id", id); @@ -134,28 +135,28 @@ 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); - packet.setFrom(conversation.getAccount().getJid()); + packet.setFrom(conversation.getAccount().getJid().toBareJid()); 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()); + packet.setFrom(conversation.getAccount().getJid()); 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.setFrom(conversation.getAccount().getFullJid()); + packet.setTo(conversation.getContactJid().toBareJid()); + packet.setFrom(conversation.getAccount().getJid()); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); Element invite = new Element("invite"); @@ -169,8 +170,8 @@ public class MessageGenerator extends AbstractGenerator { MessagePacket originalMessage, String namespace) { MessagePacket receivedPacket = new MessagePacket(); receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(originalMessage.getFrom()); - receivedPacket.setFrom(account.getFullJid()); + receivedPacket.setTo(originalMessage.getFrom()); + receivedPacket.setFrom(account.getJid()); Element received = receivedPacket.addChild("received", namespace); received.setAttribute("id", originalMessage.getId()); return receivedPacket; diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index d896dd00..e3642f6b 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().toBareJid()); 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.getJid()); 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..eedfca16 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.isBareJid() ? "" : 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..d5d1f3a0 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,8 @@ 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()))) { + final Jid from = packet.getFrom(); + if ((from == null) || (from.equals(account.getJid().toBareJid()))) { Element query = packet.findChild("query"); this.rosterItems(account, query); } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 472a2e46..e40855d8 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -7,11 +7,12 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnMessagePacketReceived; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; @@ -22,9 +23,9 @@ public class MessageParser extends AbstractParser implements } private Message parseChat(MessagePacket packet, Account account) { - String[] fromParts = packet.getFrom().split("/", 2); + final Jid jid = packet.getFrom(); Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, fromParts[0], false); + .findOrCreateConversation(account, jid.toBareJid(), false); updateLastseen(packet, account, true); String pgpBody = getPgpBody(packet); Message finishedMessage; @@ -39,31 +40,31 @@ public class MessageParser extends AbstractParser implements finishedMessage.setRemoteMsgId(packet.getId()); finishedMessage.markable = isMarkable(packet); if (conversation.getMode() == Conversation.MODE_MULTI - && fromParts.length >= 2) { + && !jid.isBareJid()) { finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setPresence(fromParts[1]); finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(fromParts[1])); + .getTrueCounterpart(jid.getResourcepart())); if (conversation.hasDuplicateMessage(finishedMessage)) { return null; } } + finishedMessage.setCounterpart(jid); finishedMessage.setTime(getTimestamp(packet)); return finishedMessage; } private Message parseOtrChat(MessagePacket packet, Account account) { - boolean properlyAddressed = (packet.getTo().split("/", 2).length == 2) + boolean properlyAddressed = (!packet.getTo().isBareJid()) || (account.countPresences() == 1); - String[] fromParts = packet.getFrom().split("/", 2); + final Jid from = packet.getFrom(); Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, fromParts[0], false); + .findOrCreateConversation(account, from.toBareJid(), false); String presence; - if (fromParts.length >= 2) { - presence = fromParts[1]; + if (from.isBareJid()) { + presence = ""; } else { - presence = ""; + presence = from.getResourcepart(); } updateLastseen(packet, account, true); String body = packet.getBody(); @@ -128,24 +129,23 @@ public class MessageParser extends AbstractParser implements private Message parseGroupchat(MessagePacket packet, Account account) { int status; - String[] fromParts = packet.getFrom().split("/", 2); + final Jid from = packet.getFrom(); if (mXmppConnectionService.find(account.pendingConferenceLeaves, - account, fromParts[0]) != null) { + account, from.toBareJid()) != null) { return null; } Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, fromParts[0], true); + .findOrCreateConversation(account, from.toBareJid(), true); if (packet.hasChild("subject")) { conversation.getMucOptions().setSubject( packet.findChild("subject").getContent()); mXmppConnectionService.updateConversationUi(); return null; } - if ((fromParts.length == 1)) { + if (from.isBareJid()) { return null; } - String counterPart = fromParts[1]; - if (counterPart.equals(conversation.getMucOptions().getActualNick())) { + if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { if (mXmppConnectionService.markMessage(conversation, packet.getId(), Message.STATUS_SEND)) { return null; @@ -158,17 +158,17 @@ public class MessageParser extends AbstractParser implements String pgpBody = getPgpBody(packet); Message finishedMessage; if (pgpBody == null) { - finishedMessage = new Message(conversation, counterPart, + finishedMessage = new Message(conversation, from, packet.getBody(), Message.ENCRYPTION_NONE, status); } else { - finishedMessage = new Message(conversation, counterPart, pgpBody, + finishedMessage = new Message(conversation, from, pgpBody, Message.ENCRYPTION_PGP, status); } finishedMessage.setRemoteMsgId(packet.getId()); finishedMessage.markable = isMarkable(packet); if (status == Message.STATUS_RECEIVED) { finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(counterPart)); + .getTrueCounterpart(from.getResourcepart())); } if (packet.hasChild("delay") && conversation.hasDuplicateMessage(finishedMessage)) { @@ -178,9 +178,9 @@ public class MessageParser extends AbstractParser implements return finishedMessage; } - private Message parseCarbonMessage(MessagePacket packet, Account account) { + private Message parseCarbonMessage(final MessagePacket packet, final Account account) { int status; - String fullJid; + final Jid fullJid; Element forwarded; if (packet.hasChild("received", "urn:xmpp:carbons:2")) { forwarded = packet.findChild("received", "urn:xmpp:carbons:2") @@ -206,11 +206,11 @@ public class MessageParser extends AbstractParser implements parseNonMessage(message, account); } else if (status == Message.STATUS_SEND && message.hasChild("displayed", "urn:xmpp:chat-markers:0")) { - String to = message.getAttribute("to"); + final Jid to = message.getTo(); if (to != null) { - Conversation conversation = mXmppConnectionService.find( + final Conversation conversation = mXmppConnectionService.find( mXmppConnectionService.getConversations(), account, - to.split("/")[0]); + to.toBareJid()); if (conversation != null) { mXmppConnectionService.markRead(conversation, false); } @@ -219,21 +219,20 @@ public class MessageParser extends AbstractParser implements return null; } if (status == Message.STATUS_RECEIVED) { - fullJid = message.getAttribute("from"); + fullJid = message.getFrom(); if (fullJid == null) { return null; } else { updateLastseen(message, account, true); } } else { - fullJid = message.getAttribute("to"); + fullJid = message.getTo(); if (fullJid == null) { return null; } } - String[] parts = fullJid.split("/", 2); Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, parts[0], false); + .findOrCreateConversation(account, fullJid.toBareJid(), false); String pgpBody = getPgpBody(message); Message finishedMessage; if (pgpBody != null) { @@ -248,11 +247,11 @@ public class MessageParser extends AbstractParser implements finishedMessage.setRemoteMsgId(message.getAttribute("id")); finishedMessage.markable = isMarkable(message); if (conversation.getMode() == Conversation.MODE_MULTI - && parts.length >= 2) { + && !fullJid.isBareJid()) { finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setPresence(parts[1]); + finishedMessage.setCounterpart(fullJid); finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(parts[1])); + .getTrueCounterpart(fullJid.getResourcepart())); if (conversation.hasDuplicateMessage(finishedMessage)) { return null; } @@ -260,39 +259,39 @@ public class MessageParser extends AbstractParser implements return finishedMessage; } - private void parseError(MessagePacket packet, Account account) { - String[] fromParts = packet.getFrom().split("/", 2); - mXmppConnectionService.markMessage(account, fromParts[0], + private void parseError(final MessagePacket packet, final Account account) { + final Jid from = packet.getFrom(); + mXmppConnectionService.markMessage(account, from.toBareJid(), packet.getId(), Message.STATUS_SEND_FAILED); } private void parseNonMessage(Element packet, Account account) { - String from = packet.getAttribute("from"); + final Jid from = packet.getFrom(); if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); - parseEvent(event, packet.getAttribute("from"), account); + parseEvent(event, from, account); } else if (from != null && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { String id = packet .findChild("displayed", "urn:xmpp:chat-markers:0") .getAttribute("id"); updateLastseen(packet, account, true); - mXmppConnectionService.markMessage(account, from.split("/", 2)[0], + mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_DISPLAYED); } else if (from != null && packet.hasChild("received", "urn:xmpp:chat-markers:0")) { String id = packet.findChild("received", "urn:xmpp:chat-markers:0") .getAttribute("id"); updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.split("/", 2)[0], + mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_RECEIVED); } else if (from != null && packet.hasChild("received", "urn:xmpp:receipts")) { String id = packet.findChild("received", "urn:xmpp:receipts") .getAttribute("id"); updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.split("/", 2)[0], + mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_RECEIVED); } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { Element x = packet.findChild("x", @@ -300,7 +299,7 @@ public class MessageParser extends AbstractParser implements if (x.hasChild("invite")) { Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, - packet.getAttribute("from"), true); + packet.getFrom(), true); if (!conversation.getMucOptions().online()) { if (x.hasChild("password")) { Element password = x.findChild("password"); @@ -315,8 +314,13 @@ public class MessageParser extends AbstractParser implements } } else if (packet.hasChild("x", "jabber:x:conference")) { Element x = packet.findChild("x", "jabber:x:conference"); - String jid = x.getAttribute("jid"); - String password = x.getAttribute("password"); + Jid jid; + try { + jid = Jid.fromString(x.getAttribute("jid")); + } catch (InvalidJidException e) { + jid = null; + } + String password = x.getAttribute("password"); if (jid != null) { Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, jid, true); @@ -333,7 +337,7 @@ public class MessageParser extends AbstractParser implements } } - private void parseEvent(Element event, String from, Account account) { + private void parseEvent(final Element event, final Jid from, final Account account) { Element items = event.findChild("items"); String node = items.getAttribute("node"); if (node != null) { @@ -343,7 +347,7 @@ public class MessageParser extends AbstractParser implements avatar.owner = from; if (mXmppConnectionService.getFileBackend().isAvatarCached( avatar)) { - if (account.getJid().equals(from)) { + if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { mXmppConnectionService.databaseBackend .updateAccount(account); @@ -474,7 +478,7 @@ public class MessageParser extends AbstractParser implements if (message.getStatus() == Message.STATUS_RECEIVED && conversation.getOtrSession() != null && !conversation.getOtrSession().getSessionID().getUserID() - .equals(message.getPresence())) { + .equals(message.getCounterpart().getResourcepart())) { conversation.endOtrIfNeeded(); } diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 4e90cda8..635f2932 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -9,6 +9,7 @@ import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceParser extends AbstractParser implements @@ -21,8 +22,9 @@ public class PresenceParser extends AbstractParser implements public void parseConferencePresence(PresencePacket packet, Account account) { PgpEngine mPgpEngine = mXmppConnectionService.getPgpEngine(); if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { - Conversation muc = mXmppConnectionService.find(account, packet - .getAttribute("from").split("/", 2)[0]); + final Conversation muc = packet.getFrom() == null ? null : mXmppConnectionService.find( + account, + packet.getFrom().toBareJid()); if (muc != null) { boolean before = muc.getMucOptions().online(); muc.getMucOptions().processPacket(packet, mPgpEngine); @@ -32,8 +34,8 @@ public class PresenceParser extends AbstractParser implements mXmppConnectionService.getAvatarService().clear(muc); } } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { - Conversation muc = mXmppConnectionService.find(account, packet - .getAttribute("from").split("/", 2)[0]); + final Conversation muc = mXmppConnectionService.find(account, + packet.getFrom().toBareJid()); if (muc != null) { boolean before = muc.getMucOptions().online(); muc.getMucOptions().processPacket(packet, mPgpEngine); @@ -51,15 +53,15 @@ public class PresenceParser extends AbstractParser implements if (packet.getFrom() == null) { return; } - String[] fromParts = packet.getFrom().split("/", 2); + final Jid from = packet.getFrom(); String type = packet.getAttribute("type"); - if (fromParts[0].equals(account.getJid())) { - if (fromParts.length == 2) { + if (from.toBareJid().equals(account.getJid().toBareJid())) { + if (!from.isBareJid()) { if (type == null) { - account.updatePresence(fromParts[1], + account.updatePresence(from.getResourcepart(), Presences.parseShow(packet.findChild("show"))); } else if (type.equals("unavailable")) { - account.removePresence(fromParts[1]); + account.removePresence(from.getResourcepart()); account.deactivateGracePeriod(); } } @@ -67,8 +69,8 @@ public class PresenceParser extends AbstractParser implements Contact contact = account.getRoster().getContact(packet.getFrom()); if (type == null) { String presence; - if (fromParts.length >= 2) { - presence = fromParts[1]; + if (!from.isBareJid()) { + presence = from.getResourcepart(); } else { presence = ""; } @@ -95,10 +97,10 @@ public class PresenceParser extends AbstractParser implements mXmppConnectionService.onContactStatusChanged .onContactStatusChanged(contact, online); } else if (type.equals("unavailable")) { - if (fromParts.length != 2) { + if (from.isBareJid()) { contact.clearPresences(); } else { - contact.removePresence(fromParts[1]); + contact.removePresence(from.getResourcepart()); } mXmppConnectionService.onContactStatusChanged .onContactStatusChanged(contact, false); 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/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 778fd4dd..83bdacf8 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -22,13 +22,14 @@ public class AvatarService { private static final int FG_COLOR = 0xFFFAFAFA; private static final int TRANSPARENT = 0x00000000; + private static final int PLACEHOLDER_COLOR = 0xFF202020; private static final String PREFIX_CONTACT = "contact"; private static final String PREFIX_CONVERSATION = "conversation"; private static final String PREFIX_ACCOUNT = "account"; private static final String PREFIX_GENERIC = "generic"; - private ArrayList<Integer> sizes = new ArrayList<Integer>(); + final private ArrayList<Integer> sizes = new ArrayList<>(); protected XmppConnectionService mXmppConnectionService = null; @@ -36,7 +37,7 @@ public class AvatarService { this.mXmppConnectionService = service; } - public Bitmap get(Contact contact, int size) { + public Bitmap get(final Contact contact, final int size) { final String KEY = key(contact, size); Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY); if (avatar != null) { @@ -49,7 +50,7 @@ public class AvatarService { avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size); } if (avatar == null) { - avatar = get(contact.getDisplayName(), size); + avatar = get(contact.getDisplayName(), size); } this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); return avatar; @@ -68,7 +69,7 @@ public class AvatarService { this.sizes.add(size); } } - return PREFIX_CONTACT + "_" + contact.getAccount().getJid() + "_" + return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_" + contact.getJid() + "_" + String.valueOf(size); } @@ -140,7 +141,7 @@ public class AvatarService { drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1); drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size); drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1); - drawTile(canvas, "\u2026", 0xFF202020, size / 2 + 1, size / 2 + 1, + drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1, size, size); } this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap); @@ -173,7 +174,7 @@ public class AvatarService { avatar = mXmppConnectionService.getFileBackend().getAvatar( account.getAvatar(), size); if (avatar == null) { - avatar = get(account.getJid(), size); + avatar = get(account.getJid().toBareJid().toString(), size); } mXmppConnectionService.getBitmapCache().put(KEY, avatar); return avatar; @@ -196,7 +197,7 @@ public class AvatarService { + String.valueOf(size); } - public Bitmap get(String name, int size) { + public Bitmap get(final String name, final int size) { final String KEY = key(name, size); Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY); if (bitmap != null) { @@ -204,8 +205,15 @@ public class AvatarService { } bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - String letter = name.substring(0, 1); - int color = this.getColorForName(name); + String letter; + int color; + if (name.length() > 0) { + letter = name.substring(0, 1); + color = this.getColorForName(name); + } else { + letter = "X"; + color = PLACEHOLDER_COLOR; + } drawTile(canvas, letter, color, 0, 0, size, size); mXmppConnectionService.getBitmapCache().put(KEY, bitmap); return bitmap; @@ -255,21 +263,21 @@ public class AvatarService { .cropCenter(uri, bottom - top, right - left); if (bitmap != null) { drawTile(canvas, bitmap, left, top, right, bottom); - } else { - String letter = user.getName().substring(0, 1); - int color = this.getColorForName(user.getName()); - drawTile(canvas, letter, color, left, top, right, bottom); + return; } - } else { - String letter = user.getName().substring(0, 1); - int color = this.getColorForName(user.getName()); - drawTile(canvas, letter, color, left, top, right, bottom); } + } + String name = user.getName(); + String letter; + int color; + if (name.length() > 0) { + letter = name.substring(0, 1); + color = this.getColorForName(name); } else { - String letter = user.getName().substring(0, 1); - int color = this.getColorForName(user.getName()); - drawTile(canvas, letter, color, left, top, right, bottom); + letter = "X"; + color = PLACEHOLDER_COLOR; } + drawTile(canvas, letter, color, left, top, right, bottom); } private void drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ea6f509f..787cacd0 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -19,6 +19,7 @@ import de.duenndns.ssl.MemorizingTrustManager; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; +import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionStatus; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -55,6 +56,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 +94,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 +172,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,19 +182,19 @@ 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() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive"); connection.sendInactive(); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active"); connection.sendActive(); } @@ -214,7 +214,7 @@ public class XmppConnectionService extends Service { && (account.getStatus() != Account.STATUS_NO_INTERNET)) { if (connection != null) { int next = connection.getTimeToNextAttempt(); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": error connecting account. try again in " + next + "s for the " + (connection.getAttempt() + 1) + " time"); @@ -309,7 +309,7 @@ public class XmppConnectionService extends Service { message = new Message(conversation, "", conversation.getNextEncryption(forceEncryption())); } - message.setPresence(conversation.getNextPresence()); + message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_IMAGE); message.setStatus(Message.STATUS_OFFERED); new Thread(new Runnable() { @@ -335,7 +335,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 +415,7 @@ public class XmppConnectionService extends Service { if (wakeLock.isHeld()) { try { wakeLock.release(); - } catch (RuntimeException re) { + } catch (final RuntimeException ignored) { } } return START_STICKY; @@ -536,9 +536,9 @@ 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); + account.setResource(sharedPref.getString("resource", "mobile") + .toLowerCase(Locale.getDefault())); + XmppConnection connection = new XmppConnection(account, this); connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); connection.setOnPresencePacketReceivedListener(this.mPresenceParser); @@ -560,11 +560,10 @@ public class XmppConnectionService extends Service { if (account.getStatus() == Account.STATUS_ONLINE && account.getXmppConnection() != null) { if (message.getType() == Message.TYPE_IMAGE) { - if (message.getPresence() != null) { + if (message.getCounterpart() != null) { if (message.getEncryption() == Message.ENCRYPTION_OTR) { - if (!conv.hasValidOtrSession() - && (message.getPresence() != null)) { - conv.startOtrSession(this, message.getPresence(), + if (!conv.hasValidOtrSession()) { + conv.startOtrSession(this, message.getCounterpart().getResourcepart(), true); message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() @@ -583,19 +582,24 @@ public class XmppConnectionService extends Service { } } else { if (message.getEncryption() == Message.ENCRYPTION_OTR) { - if (!conv.hasValidOtrSession() - && (message.getPresence() != null)) { - conv.startOtrSession(this, message.getPresence(), true); + if (!conv.hasValidOtrSession()&& (message.getCounterpart() != null)) { + conv.startOtrSession(this, message.getCounterpart().getResourcepart(), true); message.setStatus(Message.STATUS_WAITING); - } else if (conv.hasValidOtrSession() - && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { - message.setPresence(conv.getOtrSession().getSessionID() - .getUserID()); - packet = mMessageGenerator.generateOtrChat(message); - send = true; - - } else if (message.getPresence() == null) { - conv.startOtrIfNeeded(); + } else if (conv.hasValidOtrSession()) { + SessionID id = conv.getOtrSession().getSessionID(); + try { + message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID())); + } catch (final InvalidJidException e) { + message.setCounterpart(null); + } + if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { + packet = mMessageGenerator.generateOtrChat(message); + send = true; + } else { + message.setStatus(Message.STATUS_WAITING); + conv.startOtrIfNeeded(); + } + } else { message.setStatus(Message.STATUS_WAITING); } } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { @@ -628,11 +632,15 @@ public class XmppConnectionService extends Service { message.setEncryption(Message.ENCRYPTION_DECRYPTED); } else if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (conv.hasValidOtrSession()) { - message.setPresence(conv.getOtrSession().getSessionID() - .getUserID()); + SessionID id = conv.getOtrSession().getSessionID(); + try { + message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID())); + } catch (final InvalidJidException e) { + message.setCounterpart(null); + } } else if (!conv.hasValidOtrSession() - && message.getPresence() != null) { - conv.startOtrSession(this, message.getPresence(), false); + && message.getCounterpart() != null) { + conv.startOtrSession(this, message.getCounterpart().getResourcepart(), false); } } } @@ -667,10 +675,10 @@ public class XmppConnectionService extends Service { Presences presences = message.getConversation().getContact() .getPresences(); if (!message.getConversation().hasValidOtrSession()) { - if ((message.getPresence() != null) - && (presences.has(message.getPresence()))) { + if ((message.getCounterpart() != null) + && (presences.has(message.getCounterpart().getResourcepart()))) { message.getConversation().startOtrSession(this, - message.getPresence(), true); + message.getCounterpart().getResourcepart(), true); } else { if (presences.size() == 1) { String presence = presences.asStringArray()[0]; @@ -697,16 +705,20 @@ public class XmppConnectionService extends Service { packet = mMessageGenerator.generatePgpChat(message, true); } } else if (message.getType() == Message.TYPE_IMAGE) { - Presences presences = message.getConversation().getContact() - .getPresences(); - if ((message.getPresence() != null) - && (presences.has(message.getPresence()))) { + Contact contact = message.getConversation().getContact(); + Presences presences = contact.getPresences(); + if ((message.getCounterpart() != null) + && (presences.has(message.getCounterpart().getResourcepart()))) { markMessage(message, Message.STATUS_OFFERED); mJingleConnectionManager.createNewConnection(message); } else { if (presences.size() == 1) { String presence = presences.asStringArray()[0]; - message.setPresence(presence); + try { + message.setCounterpart(Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), presence)); + } catch (InvalidJidException e) { + return; + } markMessage(message, Message.STATUS_OFFERED); mJingleConnectionManager.createNewConnection(message); } @@ -726,10 +738,10 @@ public class XmppConnectionService extends Service { public void fetchRosterFromServer(Account account) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); if (!"".equals(account.getRosterVersion())) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster version " + account.getRosterVersion()); } else { - Log.d(Config.LOGTAG, account.getJid() + ": fetching roster"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster"); } iqPacket.query("jabber:iq:roster").setAttribute("ver", account.getRosterVersion()); @@ -757,7 +769,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 +818,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 +845,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 +943,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 +979,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 +1181,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 +1199,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 +1277,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().getJid()); String sig = account.getPgpSignature(); if (sig != null) { @@ -1289,13 +1306,13 @@ 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().getJid()); packet.setAttribute("type", "unavailable"); sendPresencePacket(conversation.getAccount(), packet); conversation.getMucOptions().setOffline(); conversation.deregisterWithBookmark(); - Log.d(Config.LOGTAG, conversation.getAccount().getJid() + Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": leaving muc " + conversation.getContactJid()); } else { account.pendingConferenceLeaves.add(conversation); @@ -1307,20 +1324,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().toBareJid() + + ": ended otr session with " + + conversation.getContactJid()); + } + } + } + } } account.getXmppConnection().disconnect(force); } @@ -1362,27 +1378,31 @@ public class XmppConnectionService extends Service { List<Message> messages = conversation.getMessages(); Session otrSession = conversation.getOtrSession(); Log.d(Config.LOGTAG, - account.getJid() + " otr session established with " + account.getJid().toBareJid() + " 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)) { + SessionID id = otrSession.getSessionID(); + try { + msg.setCounterpart(Jid.fromString(id.getAccountID()+"/"+id.getUserID())); + } catch (InvalidJidException e) { + break; } - } - } + 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(); } @@ -1394,10 +1414,10 @@ public class XmppConnectionService extends Service { if (otrSession != null) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_CHAT); - packet.setFrom(account.getFullJid()); + packet.setFrom(account.getJid()); packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); - packet.setTo(otrSession.getSessionID().getAccountID() + "/" + packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/" + otrSession.getSessionID().getUserID()); try { packet.setBody(otrSession @@ -1504,13 +1524,13 @@ public class XmppConnectionService extends Service { @Override public void onIqPacketReceived(Account account, IqPacket result) { - final String ERROR = account.getJid() + final String ERROR = account.getJid().toBareJid() + ": fetching avatar for " + avatar.owner + " failed "; if (result.getType() == IqPacket.TYPE_RESULT) { avatar.image = mIqParser.avatarData(result); if (avatar.image != null) { if (getFileBackend().save(avatar)) { - if (account.getJid().equals(avatar.owner)) { + if (account.getJid().toBareJid().equals(avatar.owner)) { if (account.setAvatar(avatar.getFilename())) { databaseBackend.updateAccount(account); } @@ -1528,7 +1548,7 @@ public class XmppConnectionService extends Service { if (callback != null) { callback.success(avatar); } - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": succesfully fetched avatar for " + avatar.owner); return; @@ -1568,7 +1588,7 @@ public class XmppConnectionService extends Service { if (items != null) { Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { - avatar.owner = account.getJid(); + avatar.owner = account.getJid().toBareJid(); if (fileBackend.isAvatarCached(avatar)) { if (account.setAvatar(avatar.getFilename())) { databaseBackend.updateAccount(account); @@ -1596,7 +1616,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 +1668,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,9 +1750,9 @@ 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)) { + if (account.getJid().toBareJid().equals(accountJid)) { return account; } } @@ -1753,10 +1773,10 @@ public class XmppConnectionService extends Service { String id = conversation.getLatestMarkableMessageId(); conversation.markRead(); if (confirmMessages() && id != null && calledByUi) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid() + Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": 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 +1830,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 +1848,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 +1911,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 +1951,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/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java index f14da352..57152b26 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java @@ -25,7 +25,7 @@ import eu.siacs.conversations.ui.adapter.ListItemAdapter; public class ChooseContactActivity extends XmppActivity { private ListView mListView; - private ArrayList<ListItem> contacts = new ArrayList<ListItem>(); + private ArrayList<ListItem> contacts = new ArrayList<>(); private ArrayAdapter<ListItem> mContactsAdapter; private EditText mSearchEditText; @@ -96,10 +96,10 @@ public class ChooseContactActivity extends XmppActivity { Intent request = getIntent(); Intent data = new Intent(); ListItem mListItem = contacts.get(position); - data.putExtra("contact", mListItem.getJid()); + data.putExtra("contact", mListItem.getJid().toString()); String account = request.getStringExtra("account"); if (account == null && mListItem instanceof Contact) { - account = ((Contact) mListItem).getAccount().getJid(); + account = ((Contact) mListItem).getAccount().getJid().toBareJid().toString(); } data.putExtra("account", account); data.putExtra("conversation", diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index cc9fca26..85c35ecd 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -53,7 +53,7 @@ public class ConferenceDetailsActivity extends XmppActivity { } }; - private List<User> users = new ArrayList<MucOptions.User>(); + private List<User> users = new ArrayList<>(); private OnConversationUpdate onConvChanged = new OnConversationUpdate() { @Override @@ -142,7 +142,7 @@ public class ConferenceDetailsActivity extends XmppActivity { @Override protected String getShareableUri() { if (conversation!=null) { - return "xmpp:"+conversation.getContactJid().split("/")[0]+"?join"; + return "xmpp:"+conversation.getContactJid().toBareJid().toString()+"?join"; } else { return ""; } @@ -207,11 +207,11 @@ public class ConferenceDetailsActivity extends XmppActivity { private void populateView() { mAccountJid.setText(getString(R.string.using_account, conversation - .getAccount().getJid())); + .getAccount().getJid().toBareJid())); mYourPhoto.setImageBitmap(avatarService().get( conversation.getAccount(), getPixel(48))); setTitle(conversation.getName()); - mFullJid.setText(conversation.getContactJid().split("/", 2)[0]); + mFullJid.setText(conversation.getContactJid().toBareJid().toString()); mYourNick.setText(conversation.getMucOptions().getActualNick()); mRoleAffiliaton = (TextView) findViewById(R.id.muc_role); if (conversation.getMucOptions().online()) { diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 7ac30e39..7106fcdb 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -35,14 +35,16 @@ import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class ContactDetailsActivity extends XmppActivity { public static final String ACTION_VIEW_CONTACT = "view_contact"; private Contact contact; - private String accountJid; - private String contactJid; + private Jid accountJid; + private Jid contactJid; private TextView contactJidTv; private TextView accountJidTv; @@ -68,7 +70,7 @@ public class ContactDetailsActivity extends XmppActivity { public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(Contacts.CONTENT_ITEM_TYPE); - intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid()); + intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString()); intent.putExtra(Intents.Insert.IM_PROTOCOL, CommonDataKinds.Im.PROTOCOL_JABBER); intent.putExtra("finishActivityOnSaveCompleted", true); @@ -174,9 +176,15 @@ public class ContactDetailsActivity extends XmppActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) { - this.accountJid = getIntent().getExtras().getString("account"); - this.contactJid = getIntent().getExtras().getString("contact"); - } + try { + this.accountJid = Jid.fromString(getIntent().getExtras().getString("account")); + } catch (final InvalidJidException ignored) { + } + try { + this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact")); + } catch (final InvalidJidException ignored) { + } + } setContentView(R.layout.activity_contact_details); contactJidTv = (TextView) findViewById(R.id.details_contactjid); @@ -318,10 +326,10 @@ public class ContactDetailsActivity extends XmppActivity { contactJidTv.setText(contact.getJid() + " (" + contact.getPresences().size() + ")"); } else { - contactJidTv.setText(contact.getJid()); + contactJidTv.setText(contact.getJid().toString()); } accountJidTv.setText(getString(R.string.using_account, contact - .getAccount().getJid())); + .getAccount().getJid().toBareJid())); prepareContactBadge(badge, contact); if (contact.getSystemAccount() == null) { badge.setOnClickListener(onBadgeClick); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 2a4ddd2b..c6ec63df 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -31,7 +31,6 @@ import android.widget.Toast; import java.util.ArrayList; import java.util.List; -import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; @@ -70,7 +69,7 @@ public class ConversationActivity extends XmppActivity implements private View mContentView; - private List<Conversation> conversationList = new ArrayList<Conversation>(); + private List<Conversation> conversationList = new ArrayList<>(); private Conversation selectedConversation = null; private ListView listView; private ConversationFragment mConversationFragment; @@ -107,7 +106,7 @@ public class ConversationActivity extends XmppActivity implements protected String getShareableUri() { Conversation conversation = getSelectedConversation(); if (conversation != null) { - return "xmpp:" + conversation.getAccount().getJid(); + return "xmpp:" + conversation.getAccount().getJid().toBareJid(); } else { return ""; } @@ -161,8 +160,10 @@ public class ConversationActivity extends XmppActivity implements this.listAdapter = new ConversationAdapter(this, conversationList); listView.setAdapter(this.listAdapter); - getActionBar().setDisplayHomeAsUpEnabled(false); - getActionBar().setHomeButtonEnabled(false); + if (getActionBar() != null) { + getActionBar().setDisplayHomeAsUpEnabled(false); + getActionBar().setHomeButtonEnabled(false); + } listView.setOnItemClickListener(new OnItemClickListener() { @@ -229,8 +230,7 @@ public class ConversationActivity extends XmppActivity implements .useSubjectToIdentifyConference()) { ab.setTitle(getSelectedConversation().getName()); } else { - ab.setTitle(getSelectedConversation().getContactJid() - .split("/")[0]); + ab.setTitle(getSelectedConversation().getContactJid().toBareJid().toString()); } } invalidateOptionsMenu(); @@ -601,7 +601,7 @@ public class ConversationActivity extends XmppActivity implements } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { + public boolean onKeyDown(final int keyCode, final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (!isConversationsOverviewVisable()) { showConversationsOverview(); @@ -612,7 +612,7 @@ public class ConversationActivity extends XmppActivity implements } @Override - protected void onNewIntent(Intent intent) { + protected void onNewIntent(final Intent intent) { if (xmppConnectionServiceBound) { if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) { handleViewConversationIntent(intent); @@ -646,7 +646,7 @@ public class ConversationActivity extends XmppActivity implements } @Override - public void onSaveInstanceState(Bundle savedInstanceState) { + public void onSaveInstanceState(final Bundle savedInstanceState) { Conversation conversation = getSelectedConversation(); if (conversation != null) { savedInstanceState.putString(STATE_OPEN_CONVERSATION, @@ -684,7 +684,9 @@ public class ConversationActivity extends XmppActivity implements } this.mConversationFragment.reInit(getSelectedConversation()); mOpenConverstaion = null; - } else if (getSelectedConversation() == null) { + } else if (getSelectedConversation() != null) { + this.mConversationFragment.updateMessages(); + } else { showConversationsOverview(); mPendingImageUri = null; setSelectedConversation(conversationList.get(0)); @@ -713,11 +715,11 @@ public class ConversationActivity extends XmppActivity implements } private void selectConversationByUuid(String uuid) { - for (int i = 0; i < conversationList.size(); ++i) { - if (conversationList.get(i).getUuid().equals(uuid)) { - setSelectedConversation(conversationList.get(i)); - } - } + for (Conversation aConversationList : conversationList) { + if (aConversationList.getUuid().equals(uuid)) { + setSelectedConversation(aConversationList); + } + } } public void registerListener() { @@ -831,7 +833,7 @@ public class ConversationActivity extends XmppActivity implements try { this.startIntentSenderForResult(pi.getIntentSender(), requestCode, null, 0, 0, 0); - } catch (SendIntentException e1) { + } catch (final SendIntentException ignored) { } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 8754b953..d2ba6f51 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -54,6 +54,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.jid.Jid; public class ConversationFragment extends Fragment { @@ -92,11 +93,9 @@ public class ConversationFragment extends Fragment { } }; protected ListView messagesView; - protected LayoutInflater inflater; - protected List<Message> messageList = new ArrayList<Message>(); + protected List<Message> messageList = new ArrayList<>(); protected MessageAdapter messageListAdapter; protected Contact contact; - protected String queuedPqpMessage = null; private EditMessage mEditMessage; private ImageButton mSendButton; private RelativeLayout snackbar; @@ -147,7 +146,7 @@ public class ConversationFragment extends Fragment { } } }; - private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<Message>(); + private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>(); private boolean mDecryptJobRunning = false; private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() { @@ -191,7 +190,7 @@ public class ConversationFragment extends Fragment { } if (mEditMessage.getText().length() < 1) { if (this.conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setNextPresence(null); + conversation.setNextCounterpart(null); updateChatMsgHint(); } return; @@ -200,10 +199,10 @@ public class ConversationFragment extends Fragment { .toString(), conversation.getNextEncryption(activity .forceEncryption())); if (conversation.getMode() == Conversation.MODE_MULTI) { - if (conversation.getNextPresence() != null) { - message.setPresence(conversation.getNextPresence()); + if (conversation.getNextCounterpart() != null) { + message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_PRIVATE); - conversation.setNextPresence(null); + conversation.setNextCounterpart(null); } } if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) { @@ -217,10 +216,10 @@ public class ConversationFragment extends Fragment { public void updateChatMsgHint() { if (conversation.getMode() == Conversation.MODE_MULTI - && conversation.getNextPresence() != null) { + && conversation.getNextCounterpart() != null) { this.mEditMessage.setHint(getString( R.string.send_private_message_to, - conversation.getNextPresence())); + conversation.getNextCounterpart().getResourcepart())); } else { switch (conversation.getNextEncryption(activity.forceEncryption())) { case Message.ENCRYPTION_NONE: @@ -280,11 +279,12 @@ public class ConversationFragment extends Fragment { public void onContactPictureClicked(Message message) { if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getConversation().getMode() == Conversation.MODE_MULTI) { - if (message.getPresence() != null) { - highlightInConference(message.getPresence()); - } else { - highlightInConference(message - .getCounterpart()); + if (message.getCounterpart() != null) { + if (!message.getCounterpart().isBareJid()) { + highlightInConference(message.getCounterpart().getResourcepart()); + } else { + highlightInConference(message.getCounterpart().toString()); + } } } else { Contact contact = message.getConversation() @@ -299,7 +299,7 @@ public class ConversationFragment extends Fragment { } else { Account account = message.getConversation().getAccount(); Intent intent = new Intent(activity, EditAccountActivity.class); - intent.putExtra("jid", account.getJid()); + intent.putExtra("jid", account.getJid().toBareJid().toString()); startActivity(intent); } } @@ -311,9 +311,7 @@ public class ConversationFragment extends Fragment { public void onContactPictureLongClicked(Message message) { if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getConversation().getMode() == Conversation.MODE_MULTI) { - if (message.getPresence() != null) { - privateMessageWith(message.getPresence()); - } else { + if (message.getCounterpart() != null) { privateMessageWith(message.getCounterpart()); } } @@ -430,9 +428,9 @@ public class ConversationFragment extends Fragment { .createNewConnection(message); } - protected void privateMessageWith(String counterpart) { + protected void privateMessageWith(final Jid counterpart) { this.mEditMessage.setText(""); - this.conversation.setNextPresence(counterpart); + this.conversation.setNextCounterpart(counterpart); updateChatMsgHint(); } @@ -466,7 +464,7 @@ public class ConversationFragment extends Fragment { this.activity = (ConversationActivity) getActivity(); this.conversation = conversation; if (this.conversation.getMode() == Conversation.MODE_MULTI) { - this.conversation.setNextPresence(null); + this.conversation.setNextCounterpart(null); } this.mEditMessage.setText(""); this.mEditMessage.append(this.conversation.getNextMessage()); @@ -836,7 +834,7 @@ public class ConversationFragment extends Fragment { @Override public void onPresenceSelected() { - message.setPresence(conversation.getNextPresence()); + message.setCounterpart(conversation.getNextCounterpart()); xmppService.sendMessage(message); messageSent(); } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index c1da35f5..2344ee4b 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -1,15 +1,10 @@ package eu.siacs.conversations.ui; -import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Point; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -27,26 +22,15 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.integration.android.IntentIntegrator; -import com.google.zxing.integration.android.IntentResult; -import com.google.zxing.qrcode.QRCodeWriter; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; - -import java.util.Hashtable; - -import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.xmpp.XmppConnection.Features; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; public class EditAccountActivity extends XmppActivity { @@ -68,7 +52,7 @@ public class EditAccountActivity extends XmppActivity { private RelativeLayout mOtrFingerprintBox; private ImageButton mOtrFingerprintToClipboardButton; - private String jidToEdit; + private Jid jidToEdit; private Account mAccount; private boolean mFetchingAvatar = false; @@ -89,15 +73,14 @@ public class EditAccountActivity extends XmppActivity { return; } boolean registerNewAccount = mRegisterNew.isChecked(); - String[] jidParts = mAccountJid.getText().toString().split("@"); - String username = jidParts[0]; - String server; - if (jidParts.length >= 2) { - server = jidParts[1]; - } else { - server = ""; - } - String password = mPassword.getText().toString(); + final Jid jid; + try { + jid = Jid.fromString(mAccountJid.getText().toString()); + } catch (final InvalidJidException e) { + // TODO: Handle this error? + return; + } + String password = mPassword.getText().toString(); String passwordConfirm = mPasswordConfirm.getText().toString(); if (registerNewAccount) { if (!password.equals(passwordConfirm)) { @@ -109,19 +92,25 @@ public class EditAccountActivity extends XmppActivity { } if (mAccount != null) { mAccount.setPassword(password); - mAccount.setUsername(username); - mAccount.setServer(server); + try { + mAccount.setUsername(jid.hasLocalpart() ? jid.getLocalpart() : ""); + mAccount.setServer(jid.getDomainpart()); + } catch (final InvalidJidException ignored) { + } mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); xmppConnectionService.updateAccount(mAccount); } else { - if (xmppConnectionService.findAccountByJid(mAccountJid - .getText().toString()) != null) { - mAccountJid - .setError(getString(R.string.account_already_exists)); - mAccountJid.requestFocus(); - return; - } - mAccount = new Account(username, server, password); + try { + if (xmppConnectionService.findAccountByJid(Jid.fromString(mAccountJid.getText().toString())) != null) { + mAccountJid + .setError(getString(R.string.account_already_exists)); + mAccountJid.requestFocus(); + return; + } + } catch (InvalidJidException e) { + return; + } + mAccount = new Account(jid.toBareJid(), password); mAccount.setOption(Account.OPTION_USETLS, true); mAccount.setOption(Account.OPTION_USECOMPRESSION, true); mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); @@ -191,8 +180,7 @@ public class EditAccountActivity extends XmppActivity { finishInitialSetup(avatar); } }; - private KnownHostsAdapter mKnownHostsAdapter; - private TextWatcher mTextWatcher = new TextWatcher() { + private TextWatcher mTextWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, @@ -217,7 +205,7 @@ public class EditAccountActivity extends XmppActivity { if (mAccount!=null) { Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); - intent.putExtra("account", mAccount.getJid()); + intent.putExtra("account", mAccount.getJid().toBareJid().toString()); startActivity(intent); } } @@ -235,7 +223,7 @@ public class EditAccountActivity extends XmppActivity { } else { intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); - intent.putExtra("account", mAccount.getJid()); + intent.putExtra("account", mAccount.getJid().toBareJid().toString()); intent.putExtra("setup", true); } startActivity(intent); @@ -244,18 +232,6 @@ public class EditAccountActivity extends XmppActivity { }); } - protected boolean inputDataDiffersFromAccount() { - if (mAccount == null) { - return true; - } else { - return (!mAccount.getJid().equals(mAccountJid.getText().toString())) - || (!mAccount.getPassword().equals( - mPassword.getText().toString()) || mAccount - .isOptionSet(Account.OPTION_REGISTER) != mRegisterNew - .isChecked()); - } - } - protected void updateSaveButton() { if (mAccount != null && mAccount.getStatus() == Account.STATUS_CONNECTING) { @@ -288,7 +264,7 @@ public class EditAccountActivity extends XmppActivity { } protected boolean accountInfoEdited() { - return (!this.mAccount.getJid().equals( + return (!this.mAccount.getJid().toBareJid().equals( this.mAccountJid.getText().toString())) || (!this.mAccount.getPassword().equals( this.mPassword.getText().toString())); @@ -297,7 +273,7 @@ public class EditAccountActivity extends XmppActivity { @Override protected String getShareableUri() { if (mAccount!=null) { - return "xmpp:"+mAccount.getJid(); + return "xmpp:"+mAccount.getJid().toBareJid(); } else { return ""; } @@ -358,8 +334,12 @@ public class EditAccountActivity extends XmppActivity { protected void onStart() { super.onStart(); if (getIntent() != null) { - this.jidToEdit = getIntent().getStringExtra("jid"); - if (this.jidToEdit != null) { + try { + this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid")); + } catch (final InvalidJidException | NullPointerException ignored) { + this.jidToEdit = null; + } + if (this.jidToEdit != null) { this.mRegisterNew.setVisibility(View.GONE); getActionBar().setTitle(getString(R.string.account_details)); } else { @@ -379,9 +359,9 @@ public class EditAccountActivity extends XmppActivity { @Override protected void onBackendConnected() { - this.mKnownHostsAdapter = new KnownHostsAdapter(this, - android.R.layout.simple_list_item_1, - xmppConnectionService.getKnownHosts()); + KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this, + android.R.layout.simple_list_item_1, + xmppConnectionService.getKnownHosts()); this.xmppConnectionService .setOnAccountListChangedListener(this.mOnAccountUpdateListener); if (this.jidToEdit != null) { @@ -393,12 +373,12 @@ public class EditAccountActivity extends XmppActivity { this.mCancelButton.setEnabled(false); this.mCancelButton.setTextColor(getSecondaryTextColor()); } - this.mAccountJid.setAdapter(this.mKnownHostsAdapter); + this.mAccountJid.setAdapter(mKnownHostsAdapter); updateSaveButton(); } private void updateAccountInformation() { - this.mAccountJid.setText(this.mAccount.getJid()); + this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); this.mPassword.setText(this.mAccount.getPassword()); if (this.jidToEdit != null) { this.mAvatar.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java index 77f8b68a..011ca0a9 100644 --- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -81,7 +81,7 @@ public class ManageAccountActivity extends XmppActivity { } else { menu.findItem(R.id.mgmt_account_enable).setVisible(false); } - menu.setHeaderTitle(this.selectedAccount.getJid()); + menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString()); } @Override @@ -166,7 +166,7 @@ public class ManageAccountActivity extends XmppActivity { private void publishAvatar(Account account) { Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); - intent.putExtra("account", account.getJid()); + intent.putExtra("account", account.getJid().toString()); startActivity(intent); } diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index 6aa40c41..10ee0cd5 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -14,6 +14,8 @@ import android.widget.TextView; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.utils.PhoneHelper; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; public class PublishProfilePictureActivity extends XmppActivity { @@ -148,8 +150,13 @@ public class PublishProfilePictureActivity extends XmppActivity { @Override protected void onBackendConnected() { if (getIntent() != null) { - String jid = getIntent().getStringExtra("account"); - if (jid != null) { + Jid jid; + try { + jid = Jid.fromString(getIntent().getStringExtra("account")); + } catch (InvalidJidException e) { + jid = null; + } + if (jid != null) { this.account = xmppConnectionService.findAccountByJid(jid); if (this.account.getXmppConnection() != null) { this.support = this.account.getXmppConnection() @@ -180,7 +187,7 @@ public class PublishProfilePictureActivity extends XmppActivity { } else { loadImageIntoPreview(avatarUri); } - this.accountTextView.setText(this.account.getJid()); + this.accountTextView.setText(this.account.getJid().toBareJid().toString()); } } diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index fc6308fc..aba60175 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -5,6 +5,8 @@ import java.util.Arrays; import java.util.Locale; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; + import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Build; @@ -62,8 +64,8 @@ public class SettingsActivity extends XmppActivity implements .toLowerCase(Locale.US); if (xmppConnectionServiceBound) { for (Account account : xmppConnectionService.getAccounts()) { - account.setResource(resource); - if (!account.isOptionSet(Account.OPTION_DISABLED)) { + account.setResource(resource); + if (!account.isOptionSet(Account.OPTION_DISABLED)) { xmppConnectionService.reconnectAccount(account, false); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java index 9fbc3db1..609dc280 100644 --- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java @@ -9,6 +9,9 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.adapter.ConversationAdapter; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; @@ -150,13 +153,23 @@ public class ShareWithActivity extends XmppActivity { } private void share() { - Account account = xmppConnectionService.findAccountByJid(share.account); - if (account == null) { + Account account; + try { + account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account)); + } catch (final InvalidJidException e) { + account = null; + } + if (account == null) { return; } - Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, share.contact, false); - share(conversation); + final Conversation conversation; + try { + conversation = xmppConnectionService + .findOrCreateConversation(account, Jid.fromString(share.contact), false); + } catch (final InvalidJidException e) { + return; + } + share(conversation); } private void share(final Conversation conversation) { diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index ed6b2a85..2b357d0d 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -63,6 +63,8 @@ import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter; import eu.siacs.conversations.utils.Validator; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class StartConversationActivity extends XmppActivity { @@ -71,7 +73,7 @@ public class StartConversationActivity extends XmppActivity { private ViewPager mViewPager; private MyListFragment mContactsListFragment = new MyListFragment(); - private List<ListItem> contacts = new ArrayList<ListItem>(); + private List<ListItem> contacts = new ArrayList<>(); private ArrayAdapter<ListItem> mContactsAdapter; private MyListFragment mConferenceListFragment = new MyListFragment(); @@ -359,17 +361,26 @@ public class StartConversationActivity extends XmppActivity { return; } if (Validator.isValidJid(jid.getText().toString())) { - String accountJid = (String) spinner - .getSelectedItem(); - String contactJid = jid.getText().toString(); - Account account = xmppConnectionService + final Jid accountJid; + try { + accountJid = Jid.fromString((String) spinner + .getSelectedItem()); + } catch (final InvalidJidException e) { + return; + } + final Jid contactJid; + try { + contactJid = Jid.fromString(jid.getText().toString()); + } catch (final InvalidJidException e) { + return; + } + Account account = xmppConnectionService .findAccountByJid(accountJid); if (account == null) { dialog.dismiss(); return; } - Contact contact = account.getRoster().getContact( - contactJid); + Contact contact = account.getRoster().getContact(contactJid); if (contact.showInRoster()) { jid.setError(getString(R.string.contact_already_exists)); } else { @@ -416,10 +427,19 @@ public class StartConversationActivity extends XmppActivity { return; } if (Validator.isValidJid(jid.getText().toString())) { - String accountJid = (String) spinner - .getSelectedItem(); - String conferenceJid = jid.getText().toString(); - Account account = xmppConnectionService + final Jid accountJid; + try { + accountJid = Jid.fromString((String) spinner.getSelectedItem()); + } catch (final InvalidJidException e) { + return; + } + final Jid conferenceJid; + try { + conferenceJid = Jid.fromString(jid.getText().toString()); + } catch (final InvalidJidException e) { + return; // TODO: Do some error handling... + } + Account account = xmppConnectionService .findAccountByJid(accountJid); if (account == null) { dialog.dismiss(); @@ -471,7 +491,7 @@ public class StartConversationActivity extends XmppActivity { } private void populateAccountSpinner(Spinner spinner) { - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, + ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, mActivatedAccounts); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); @@ -554,7 +574,7 @@ public class StartConversationActivity extends XmppActivity { this.mActivatedAccounts.clear(); for (Account account : xmppConnectionService.getAccounts()) { if (account.getStatus() != Account.STATUS_DISABLED) { - this.mActivatedAccounts.add(account.getJid()); + this.mActivatedAccounts.add(account.getJid().toBareJid().toString()); } } this.mKnownHosts = xmppConnectionService.getKnownHosts(); @@ -779,7 +799,7 @@ public class StartConversationActivity extends XmppActivity { // sample: imto://xmpp/jid@foo.com try { jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1]; - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException ignored) { } } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 052385f6..e8a858c0 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -65,6 +65,8 @@ import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder; import eu.siacs.conversations.utils.ExceptionHelper; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public abstract class XmppActivity extends Activity { @@ -275,14 +277,14 @@ public abstract class XmppActivity extends Activity { public void switchToContactDetails(Contact contact) { Intent intent = new Intent(this, ContactDetailsActivity.class); intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("account", contact.getAccount().getJid()); - intent.putExtra("contact", contact.getJid()); + intent.putExtra("account", contact.getAccount().getJid().toBareJid().toString()); + intent.putExtra("contact", contact.getJid().toString()); startActivity(intent); } public void switchToAccount(Account account) { Intent intent = new Intent(this, EditAccountActivity.class); - intent.putExtra("jid", account.getJid()); + intent.putExtra("jid", account.getJid().toBareJid().toString()); startActivity(intent); } @@ -303,7 +305,7 @@ public abstract class XmppActivity extends Activity { try { startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0); - } catch (SendIntentException e) { + } catch (final SendIntentException ignored) { } } @@ -347,9 +349,9 @@ public abstract class XmppActivity extends Activity { } protected void showAddToRosterDialog(final Conversation conversation) { - String jid = conversation.getContactJid(); + final Jid jid = conversation.getContactJid(); AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(jid); + builder.setTitle(jid.toString()); builder.setMessage(getString(R.string.not_in_roster)); builder.setNegativeButton(getString(R.string.cancel), null); builder.setPositiveButton(getString(R.string.add_contact), @@ -357,7 +359,7 @@ public abstract class XmppActivity extends Activity { @Override public void onClick(DialogInterface dialog, int which) { - String jid = conversation.getContactJid(); + final Jid jid = conversation.getContactJid(); Account account = conversation.getAccount(); Contact contact = account.getRoster().getContact(jid); xmppConnectionService.createContact(contact); @@ -369,7 +371,7 @@ public abstract class XmppActivity extends Activity { private void showAskForPresenceDialog(final Contact contact) { AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(contact.getJid()); + builder.setTitle(contact.getJid().toString()); builder.setMessage(R.string.request_presence_updates); builder.setNegativeButton(R.string.cancel, null); builder.setPositiveButton(R.string.request_now, @@ -391,14 +393,14 @@ public abstract class XmppActivity extends Activity { private void warnMutalPresenceSubscription(final Conversation conversation, final OnPresenceSelected listener) { AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(conversation.getContact().getJid()); + builder.setTitle(conversation.getContact().getJid().toString()); builder.setMessage(R.string.without_mutual_presence_updates); builder.setNegativeButton(R.string.cancel, null); builder.setPositiveButton(R.string.ignore, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - conversation.setNextPresence(null); + conversation.setNextCounterpart(null); if (listener != null) { listener.onPresenceSelected(); } @@ -449,7 +451,7 @@ public abstract class XmppActivity extends Activity { public void selectPresence(final Conversation conversation, final OnPresenceSelected listener) { - Contact contact = conversation.getContact(); + final Contact contact = conversation.getContact(); if (!contact.showInRoster()) { showAddToRosterDialog(conversation); } else { @@ -463,12 +465,16 @@ public abstract class XmppActivity extends Activity { || !contact.getOption(Contact.Options.FROM)) { warnMutalPresenceSubscription(conversation, listener); } else { - conversation.setNextPresence(null); + conversation.setNextCounterpart(null); listener.onPresenceSelected(); } } else if (presences.size() == 1) { String presence = presences.asStringArray()[0]; - conversation.setNextPresence(presence); + try { + conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),presence)); + } catch (InvalidJidException e) { + conversation.setNextCounterpart(null); + } listener.onPresenceSelected(); } else { final StringBuilder presence = new StringBuilder(); @@ -499,7 +505,11 @@ public abstract class XmppActivity extends Activity { @Override public void onClick(DialogInterface dialog, int which) { - conversation.setNextPresence(presence.toString()); + try { + conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),presence.toString())); + } catch (InvalidJidException e) { + conversation.setNextCounterpart(null); + } listener.onPresenceSelected(); } }); @@ -567,11 +577,10 @@ public abstract class XmppActivity extends Activity { nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() { @Override public NdefMessage createNdefMessage(NfcEvent nfcEvent) { - NdefMessage msg = new NdefMessage(new NdefRecord[]{ - NdefRecord.createUri(getShareableUri()), - NdefRecord.createApplicationRecord("eu.siacs.conversations") - }); - return msg; + return new NdefMessage(new NdefRecord[]{ + NdefRecord.createUri(getShareableUri()), + NdefRecord.createApplicationRecord("eu.siacs.conversations") + }); } }, this); } @@ -620,7 +629,7 @@ public abstract class XmppActivity extends Activity { protected Bitmap createQrCodeBitmap(String input, int size) { try { final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter(); - final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>(); + final Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints); final int width = result.getWidth(); @@ -649,7 +658,7 @@ public abstract class XmppActivity extends Activity { private Message message = null; public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<ImageView>(imageView); + imageViewReference = new WeakReference<>(imageView); } @Override @@ -665,7 +674,7 @@ public abstract class XmppActivity extends Activity { @Override protected void onPostExecute(Bitmap bitmap) { - if (imageViewReference != null && bitmap != null) { + if (bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); @@ -695,9 +704,8 @@ public abstract class XmppActivity extends Activity { imageView.setImageDrawable(asyncDrawable); try { task.execute(message); - } catch (RejectedExecutionException e) { - return; - } + } catch (final RejectedExecutionException ignored) { + } } } } @@ -734,7 +742,7 @@ public abstract class XmppActivity extends Activity { public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>( + bitmapWorkerTaskReference = new WeakReference<>( bitmapWorkerTask); } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java index e13b3204..d0ba3374 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -31,7 +31,7 @@ public class AccountAdapter extends ArrayAdapter<Account> { view = inflater.inflate(R.layout.account_row, parent, false); } TextView jid = (TextView) view.findViewById(R.id.account_jid); - jid.setText(account.getJid()); + jid.setText(account.getJid().toBareJid().toString()); TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); imageView.setImageBitmap(activity.avatarService().get(account, diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index b5c20dc5..b3df8d72 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -58,7 +58,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { || activity.useSubjectToIdentifyConference()) { convName.setText(conversation.getName()); } else { - convName.setText(conversation.getContactJid().split("/")[0]); + convName.setText(conversation.getContactJid().toBareJid().toString()); } TextView mLastMessage = (TextView) view .findViewById(R.id.conversation_lastmsg); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java index efc6b4d9..d78dbd6a 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java @@ -34,7 +34,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { TextView jid = (TextView) view.findViewById(R.id.contact_jid); ImageView picture = (ImageView) view.findViewById(R.id.contact_photo); - jid.setText(item.getJid()); + jid.setText(item.getJid().toString()); name.setText(item.getDisplayName()); picture.setImageBitmap(activity.avatarService().get(item, activity.getPixel(48))); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index f2227308..3ebb9390 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -29,6 +29,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message.ImageParams; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.jid.Jid; public class MessageAdapter extends ArrayAdapter<Message> { @@ -135,11 +136,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (contact != null) { info = contact.getDisplayName(); } else { - if (message.getPresence() != null) { - info = message.getPresence(); - } else { - info = message.getCounterpart(); - } + info = getDisplayedMucCounterpart(message.getCounterpart()); } } break; @@ -227,14 +224,13 @@ public class MessageAdapter extends ArrayAdapter<Message> { privateMarker = activity .getString(R.string.private_message); } else { - String to; - if (message.getPresence() != null) { - to = message.getPresence(); + final String to; + if (message.getCounterpart() != null) { + to = message.getCounterpart().getResourcepart(); } else { - to = message.getCounterpart(); + to = ""; } - privateMarker = activity.getString( - R.string.private_message_to, to); + privateMarker = activity.getString(R.string.private_message_to, to); } SpannableString span = new SpannableString(privateMarker + " " + message.getBody()); @@ -305,6 +301,16 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.image.setOnLongClickListener(openContextMenu); } + private String getDisplayedMucCounterpart(final Jid counterpart) { + if (counterpart==null) { + return ""; + } else if (!counterpart.isBareJid()) { + return counterpart.getResourcepart(); + } else { + return counterpart.toString(); + } + } + @Override public View getView(int position, View view, ViewGroup parent) { final Message item = getItem(position); @@ -413,17 +419,14 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (contact != null) { viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48))); } else if (item.getConversation().getMode() == Conversation.MODE_MULTI) { - String name = item.getPresence(); - if (name == null) { - name = item.getCounterpart(); - } - viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(name, activity.getPixel(48))); + viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(getDisplayedMucCounterpart(item.getCounterpart()), + activity.getPixel(48))); } } else if (type == SENT) { viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(item.getConversation().getAccount(), activity.getPixel(48))); } - if (viewHolder.contact_picture != null) { + if (viewHolder != null && viewHolder.contact_picture != null) { viewHolder.contact_picture .setOnClickListener(new OnClickListener() { @@ -488,14 +491,16 @@ public class MessageAdapter extends ArrayAdapter<Message> { } else { displayInfoMessage(viewHolder, R.string.install_openkeychain); - viewHolder.message_box - .setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - activity.showInstallPgpDialog(); - } - }); + if (viewHolder != null) { + viewHolder.message_box + .setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + activity.showInstallPgpDialog(); + } + }); + } } } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { displayDecryptionFailed(viewHolder); 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/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java index b5fc88bd..6f3152ca 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java @@ -13,6 +13,9 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -87,12 +90,15 @@ public class ExceptionHelper { public void onClick(DialogInterface dialog, int which) { Log.d(Config.LOGTAG, "using account=" - + finalAccount.getJid() + + finalAccount.getJid().toBareJid() + " to send in stack trace"); - Conversation conversation = service - .findOrCreateConversation(finalAccount, - "bugs@siacs.eu", false); - Message message = new Message(conversation, report + Conversation conversation = null; + try { + conversation = service.findOrCreateConversation(finalAccount, + Jid.fromString("bugs@siacs.eu"), false); + } catch (final InvalidJidException ignored) { + } + Message message = new Message(conversation, report .toString(), Message.ENCRYPTION_NONE); service.sendMessage(message); } @@ -103,15 +109,12 @@ public class ExceptionHelper { @Override public void onClick(DialogInterface dialog, int which) { preferences.edit().putBoolean("never_send", true) - .commit(); + .apply(); } }); builder.create().show(); - } catch (FileNotFoundException e) { - return; - } catch (IOException e) { - return; - } + } catch (final IOException ignored) { + } } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 5141c83c..1def052e 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -110,7 +110,7 @@ public class UIHelper { List<Account> accounts) { NotificationManager mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); - List<Account> accountsWproblems = new ArrayList<Account>(); + List<Account> accountsWproblems = new ArrayList<>(); for (Account account : accounts) { if (account.hasErrorStatus()) { accountsWproblems.add(account); @@ -124,7 +124,7 @@ public class UIHelper { } else if (accountsWproblems.size() == 1) { mBuilder.setContentTitle(context .getString(R.string.problem_connecting_to_account)); - mBuilder.setContentText(accountsWproblems.get(0).getJid()); + mBuilder.setContentText(accountsWproblems.get(0).getJid().toBareJid().toString()); } else { mBuilder.setContentTitle(context .getString(R.string.problem_connecting_to_accounts)); @@ -165,7 +165,7 @@ public class UIHelper { TextView yourprint = (TextView) view .findViewById(R.id.verify_otr_yourprint); - jid.setText(contact.getJid()); + jid.setText(contact.getJid().toString()); fingerprint.setText(conversation.getOtrFingerprint()); yourprint.setText(account.getOtrFingerprint()); builder.setNegativeButton("Cancel", null); diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index 4e11ee2c..31edec52 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -5,12 +5,14 @@ import java.util.Hashtable; import java.util.List; import eu.siacs.conversations.utils.XmlHelper; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class Element { protected String name; - protected Hashtable<String, String> attributes = new Hashtable<String, String>(); + protected Hashtable<String, String> attributes = new Hashtable<>(); protected String content; - protected List<Element> children = new ArrayList<Element>(); + protected List<Element> children = new ArrayList<>(); public Element(String name) { this.name = name; @@ -103,6 +105,42 @@ public class Element { } } + public Jid getJid() { + final String jid = this.getAttribute("jid"); + if (jid != null && !jid.isEmpty()) { + try { + return Jid.fromString(jid); + } catch (final InvalidJidException e) { + return null; + } + } + return null; + } + + public Jid getTo() { + final String to = this.getAttribute("to"); + if (to != null && !to.isEmpty()) { + try { + return Jid.fromString(to); + } catch (final InvalidJidException e) { + return null; + } + } + return null; + } + + public Jid getFrom() { + final String from = this.getAttribute("from"); + if (from != null && !from.isEmpty()) { + try { + return Jid.fromString(from); + } catch (final InvalidJidException e) { + return null; + } + } + return null; + } + public Hashtable<String, String> getAttributes() { return this.attributes; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index b907ea97..4bd3668a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -18,12 +18,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; +import java.net.IDN; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -38,7 +38,6 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; -import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService; @@ -50,6 +49,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; @@ -65,45 +66,35 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; public class XmppConnection implements Runnable { + private static final int PACKET_IQ = 0; + private static final int PACKET_MESSAGE = 1; + private static final int PACKET_PRESENCE = 2; + private final Context applicationContext; protected Account account; - private WakeLock wakeLock; - - private SecureRandom mRandom; - private Socket socket; private XmlReader tagReader; private TagWriter tagWriter; - private Features features = new Features(this); - 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; private int stanzasSent = 0; - private long lastPaketReceived = 0; private long lastPingSent = 0; private long lastConnect = 0; private long lastSessionStarted = 0; - private int attempt = 0; - - private static final int PACKET_IQ = 0; - private static final int PACKET_MESSAGE = 1; - private static final int PACKET_PRESENCE = 2; - - 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; @@ -111,16 +102,14 @@ public class XmppConnection implements Runnable { private OnStatusChanged statusListener = null; private OnBindListener bindListener = null; private OnMessageAcknowledged acknowledgedListener = null; - private MemorizingTrustManager mMemorizingTrustManager; - private final Context applicationContext; + private XmppConnectionService mXmppConnectionService = null; public XmppConnection(Account account, XmppConnectionService service) { - this.mRandom = service.getRNG(); - this.mMemorizingTrustManager = service.getMemorizingTrustManager(); this.account = account; this.wakeLock = service.getPowerManager().newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, account.getJid()); + PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString()); tagWriter = new TagWriter(); + mXmppConnectionService = service; applicationContext = service.getApplicationContext(); } @@ -143,7 +132,7 @@ public class XmppConnection implements Runnable { } protected void connect() { - Log.d(Config.LOGTAG, account.getJid() + ": connecting"); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting"); usingCompression = false; usingEncryption = false; lastConnect = SystemClock.elapsedRealtime(); @@ -159,7 +148,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().toBareJid().toString() + ": dns timeout"); this.changeStatus(Account.STATUS_OFFLINE); return; } else if (values != null) { @@ -168,18 +157,24 @@ public class XmppConnection implements Runnable { while (socketError && values.size() > i) { Bundle namePort = (Bundle) values.get(i); try { - String srvRecordServer = namePort.getString("name"); + String srvRecordServer; + try { + srvRecordServer=IDN.toASCII(namePort.getString("name")); + } catch (final IllegalArgumentException e) { + // TODO: Handle me?` + srvRecordServer = ""; + } int srvRecordPort = namePort.getInt("port"); String srvIpServer = namePort.getString("ipv4"); InetSocketAddress addr; if (srvIpServer != null) { addr = new InetSocketAddress(srvIpServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid().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().toBareJid().toString() + ": using values from dns " + srvRecordServer + ":" + srvRecordPort); } @@ -187,10 +182,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().toBareJid().toString() + ": " + e.getMessage()); i++; } catch (IOException e) { - Log.d(Config.LOGTAG, account.getJid() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); i++; } } @@ -199,16 +194,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().toBareJid().toString() + ": timeout in DNS resolution"); changeStatus(Account.STATUS_OFFLINE); return; @@ -238,51 +233,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().toBareJid().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().toBareJid().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) { + } catch (final RuntimeException ignored) { } } - 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) { - } - } - 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"))) { @@ -295,7 +277,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().toBareJid().toString() + ": logged in"); tagReader.readTag(); tagReader.reset(); sendStartStream(); @@ -310,17 +292,17 @@ public class XmppConnection implements Runnable { response.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); response.setContent(CryptoHelper.saslDigestMd5(account, - challange, mRandom)); + challange, mXmppConnectionService.getRNG())); tagWriter.writeElement(response); } else if (nextTag.isStart("enabled")) { 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().toBareJid().toString() + ": stream managment(" + smVersion + ") enabled (resumable)"); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": stream managment(" + smVersion + ") enabled"); } this.lastSessionStarted = SystemClock.elapsedRealtime(); @@ -334,11 +316,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().toBareJid().toString() + ": session resumed with lost packages"); stanzasSent = serverCount; } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed"); } if (acknowledgedListener != null) { @@ -350,7 +332,7 @@ public class XmppConnection implements Runnable { } } messageReceipts.clear(); - } catch (NumberFormatException e) { + } catch (final NumberFormatException ignored) { } sendInitialPing(); @@ -373,7 +355,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().toBareJid().toString() + ": resumption failed"); streamId = null; if (account.getStatus() != Account.STATUS_ONLINE) { sendBindRequest(); @@ -388,7 +370,7 @@ public class XmppConnection implements Runnable { nextTag = tagReader.readTag(); } if (account.getStatus() == Account.STATUS_ONLINE) { - account.setStatus(Account.STATUS_OFFLINE); + account. setStatus(Account.STATUS_OFFLINE); if (statusListener != null) { statusListener.onStatusChanged(account); } @@ -396,15 +378,15 @@ public class XmppConnection implements Runnable { } private void sendInitialPing() { - Log.d(Config.LOGTAG, account.getJid() + ": sending intial ping"); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": sending intial ping"); IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - iq.setFrom(account.getFullJid()); + iq.setFrom(account.getJid()); iq.addChild("ping", "urn:xmpp:ping"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": online with resource " + account.getResource()); changeStatus(Account.STATUS_ONLINE); } @@ -523,7 +505,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 @@ -533,7 +515,7 @@ public class XmppConnection implements Runnable { .setInputStream(new ZLibInputStream(tagReader.getInputStream())); sendStartStream(); - Log.d(Config.LOGTAG, account.getJid() + ": compression enabled"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": compression enabled"); usingCompression = true; processStream(tagReader.readTag()); } @@ -553,35 +535,37 @@ 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 { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, - new X509TrustManager[]{this.mMemorizingTrustManager}, - mRandom); + new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()}, + mXmppConnectionService.getRNG()); SSLSocketFactory factory = sc.getSocketFactory(); if (factory == null) { throw new IOException("SSLSocketFactory was null"); } - HostnameVerifier verifier = this.mMemorizingTrustManager - .wrapHostnameVerifier(new StrictHostnameVerifier()); - SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, + final HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier()); + + if (socket == null) { + throw new IOException("socket was null"); + } + 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()]; @@ -590,7 +574,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"); @@ -598,17 +582,15 @@ public class XmppConnection implements Runnable { tagReader.setInputStream(sslSocket.getInputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream()); sendStartStream(); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS connection established"); 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(), @@ -660,7 +642,7 @@ public class XmppConnection implements Runnable { } else if (this.streamFeatures.hasChild("bind") && shouldBind) { sendBindRequest(); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": incompatible server. disconnecting"); disconnect(true); } @@ -689,7 +671,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()); @@ -738,7 +720,7 @@ public class XmppConnection implements Runnable { } else { changeStatus(Account.STATUS_REGISTRATION_FAILED); disconnect(true); - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not register. instructions are" + instructions.getContent()); } @@ -755,10 +737,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); @@ -787,7 +773,7 @@ public class XmppConnection implements Runnable { } }); if (this.streamFeatures.hasChild("session")) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending deprecated session"); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); startSession.addChild("session", @@ -796,24 +782,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(); } } @@ -826,21 +812,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? + } + } + } } }); } @@ -853,10 +843,10 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (!packet.hasChild("error")) { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": successfully enabled carbons"); } else { - Log.d(Config.LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": error enableing carbons " + packet.toString()); } } @@ -867,18 +857,18 @@ 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, - account.getJid() + ": switching resource due to conflict (" + final String resource = account.getResource().split("\\.")[0]; + account.setResource(resource + "." + nextRandomId()); + Log.d(Config.LOGTAG, + account.getJid().toBareJid() + ": switching resource due to conflict (" + account.getResource() + ")"); } } 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().toBareJid().toString()); + stream.setAttribute("to", account.getServer().toString()); stream.setAttribute("version", "1.0"); stream.setAttribute("xml:lang", "en"); stream.setAttribute("xmlns", "jabber:client"); @@ -887,7 +877,7 @@ public class XmppConnection implements Runnable { } private String nextRandomId() { - return new BigInteger(50, mRandom).toString(32); + return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); } public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) { @@ -895,7 +885,7 @@ public class XmppConnection implements Runnable { String id = nextRandomId(); packet.setAttribute("id", id); } - packet.setFrom(account.getFullJid()); + packet.setFrom(account.getJid()); this.sendPacket(packet, callback); } @@ -942,7 +932,7 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); } else { IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - iq.setFrom(account.getFullJid()); + iq.setFrom(account.getJid()); iq.addChild("ping", "urn:xmpp:ping"); this.sendIqPacket(iq, null); } @@ -982,7 +972,7 @@ public class XmppConnection implements Runnable { } public void disconnect(boolean force) { - Log.d(Config.LOGTAG, account.getJid() + ": disconnecting"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting"); try { if (force) { socket.close(); @@ -1016,7 +1006,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()); @@ -1055,6 +1045,36 @@ public class XmppConnection implements Runnable { return this.features; } + public long getLastSessionEstablished() { + long diff; + if (this.lastSessionStarted == 0) { + diff = SystemClock.elapsedRealtime() - this.lastConnect; + } else { + diff = SystemClock.elapsedRealtime() - this.lastSessionStarted; + } + return System.currentTimeMillis() - diff; + } + + public long getLastConnect() { + return this.lastConnect; + } + + public long getLastPingSent() { + return this.lastPingSent; + } + + public long getLastPacketReceived() { + return this.lastPaketReceived; + } + + public void sendActive() { + this.sendPacket(new ActivePacket(), null); + } + + public void sendInactive() { + this.sendPacket(new InactivePacket(), null); + } + public class Features { XmppConnection connection; @@ -1062,12 +1082,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"); @@ -1078,12 +1096,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() { @@ -1091,12 +1104,12 @@ public class XmppConnection implements Runnable { "http://jabber.org/protocol/pubsub#publish"); } + public boolean mam() { + return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0"); + } + 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() { @@ -1108,34 +1121,4 @@ public class XmppConnection implements Runnable { return connection.usingCompression; } } - - public long getLastSessionEstablished() { - long diff; - if (this.lastSessionStarted == 0) { - diff = SystemClock.elapsedRealtime() - this.lastConnect; - } else { - diff = SystemClock.elapsedRealtime() - this.lastSessionStarted; - } - return System.currentTimeMillis() - diff; - } - - public long getLastConnect() { - return this.lastConnect; - } - - public long getLastPingSent() { - return this.lastPingSent; - } - - public long getLastPacketReceived() { - return this.lastPaketReceived; - } - - public void sendActive() { - this.sendPacket(new ActivePacket(), null); - } - - public void sendInactive() { - this.sendPacket(new InactivePacket(), null); - } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/InvalidJidException.java b/src/main/java/eu/siacs/conversations/xmpp/jid/InvalidJidException.java new file mode 100644 index 00000000..f1855263 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/InvalidJidException.java @@ -0,0 +1,48 @@ +package eu.siacs.conversations.xmpp.jid; + +public class InvalidJidException extends Exception { + + // This is probably not the "Java way", but the "Java way" means we'd have a ton of extra tiny, + // annoying classes floating around. I like this. + public final static String INVALID_LENGTH = "JID must be between 0 and 3071 characters"; + public final static String INVALID_PART_LENGTH = "JID part must be between 0 and 1023 characters"; + public final static String INVALID_CHARACTER = "JID contains an invalid character"; + public final static String STRINGPREP_FAIL = "The STRINGPREP operation has failed for the given JID"; + + /** + * Constructs a new {@code Exception} that includes the current stack trace. + */ + public InvalidJidException() { + } + + /** + * Constructs a new {@code Exception} with the current stack trace and the + * specified detail message. + * + * @param detailMessage the detail message for this exception. + */ + public InvalidJidException(final String detailMessage) { + super(detailMessage); + } + + /** + * Constructs a new {@code Exception} with the current stack trace, the + * specified detail message and the specified cause. + * + * @param detailMessage the detail message for this exception. + * @param throwable the cause of this exception. + */ + public InvalidJidException(final String detailMessage, final Throwable throwable) { + super(detailMessage, throwable); + } + + /** + * Constructs a new {@code Exception} with the current stack trace and the + * specified cause. + * + * @param throwable the cause of this exception. + */ + public InvalidJidException(final Throwable throwable) { + super(throwable); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java new file mode 100644 index 00000000..8e9e0400 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -0,0 +1,180 @@ +package eu.siacs.conversations.xmpp.jid; + +import java.net.IDN; + +import gnu.inet.encoding.Stringprep; +import gnu.inet.encoding.StringprepException; + +/** + * The `Jid' class provides an immutable representation of a JID. + */ +public final class Jid { + + private final String localpart; + private final String domainpart; + private final String resourcepart; + + // It's much more efficient to store the ful JID as well as the parts instead of figuring them + // all out every time (since some characters are displayed but aren't used for comparisons). + private final String displayjid; + + public String getLocalpart() { + return localpart; + } + + public String getDomainpart() { + return IDN.toUnicode(domainpart); + } + + public String getResourcepart() { + return resourcepart; + } + + public static Jid fromString(final String jid) throws InvalidJidException { + return new Jid(jid); + } + + public static Jid fromParts(final String localpart, + final String domainpart, + final String resourcepart) throws InvalidJidException { + String out; + if (localpart == null || localpart.isEmpty()) { + out = domainpart; + } else { + out = localpart + "@" + domainpart; + } + if (resourcepart != null && !resourcepart.isEmpty()) { + out = out + "/" + resourcepart; + } + return new Jid(out); + } + + private Jid(final String jid) throws InvalidJidException { + // Hackish Android way to count the number of chars in a string... should work everywhere. + final int atCount = jid.length() - jid.replace("@", "").length(); + final int slashCount = jid.length() - jid.replace("/", "").length(); + + // Throw an error if there's anything obvious wrong with the JID... + if (jid.isEmpty() || jid.length() > 3071) { + throw new InvalidJidException(InvalidJidException.INVALID_LENGTH); + } + if (atCount > 1 || slashCount > 1 || + jid.startsWith("@") || jid.endsWith("@") || + jid.startsWith("/") || jid.endsWith("/")) { + throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); + } + + String finaljid; + + final int domainpartStart; + if (atCount == 1) { + final int atLoc = jid.indexOf("@"); + final String lp = jid.substring(0, atLoc); + try { + localpart = Stringprep.nodeprep(lp); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } + if (localpart.isEmpty() || localpart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + domainpartStart = atLoc + 1; + finaljid = lp + "@"; + } else { + localpart = ""; + finaljid = ""; + domainpartStart = 0; + } + + final String dp; + if (slashCount == 1) { + final int slashLoc = jid.indexOf("/"); + final String rp = jid.substring(slashLoc + 1, jid.length()); + try { + resourcepart = Stringprep.resourceprep(rp); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } + if (resourcepart.isEmpty() || resourcepart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + dp = jid.substring(domainpartStart, slashLoc); + finaljid = finaljid + dp + "/" + rp; + } else { + resourcepart = ""; + dp = jid.substring(domainpartStart, jid.length()); + finaljid = finaljid + dp; + } + + // Remove trailling "." before storing the domain part. + if (dp.endsWith(".")) { + try { + domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES); + } catch (final IllegalArgumentException e) { + throw new InvalidJidException(e); + } + } else { + try { + domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES); + } catch (final IllegalArgumentException e) { + throw new InvalidJidException(e); + } + } + + // TODO: Find a proper domain validation library; validate individual parts, separators, etc. + if (domainpart.isEmpty() || domainpart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + + this.displayjid = finaljid; + } + + public Jid toBareJid() { + try { + return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, ""); + } catch (final InvalidJidException e) { + // 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; + } + } + + @Override + public String toString() { + return displayjid; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Jid jid = (Jid) o; + + return jid.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + int result = localpart.hashCode(); + result = 31 * result + domainpart.hashCode(); + result = 31 * result + resourcepart.hashCode(); + return result; + } + + public boolean hasLocalpart() { + return !localpart.isEmpty(); + } + + public boolean isBareJid() { + return this.resourcepart.isEmpty(); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java index 3e7c7b68..9a0306fc 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; public class JingleCandidate { @@ -17,7 +18,7 @@ public class JingleCandidate { private String host; private int port; private int type; - private String jid; + private Jid jid; private int priority; public JingleCandidate(String cid, boolean ours) { @@ -37,11 +38,11 @@ public class JingleCandidate { return this.host; } - public void setJid(String jid) { + public void setJid(final Jid jid) { this.jid = jid; } - public String getJid() { + public Jid getJid() { return this.jid; } @@ -58,13 +59,17 @@ public class JingleCandidate { } public void setType(String type) { - if ("proxy".equals(type)) { - this.type = TYPE_PROXY; - } else if ("direct".equals(type)) { - this.type = TYPE_DIRECT; - } else { - this.type = TYPE_UNKNOWN; - } + switch (type) { + case "proxy": + this.type = TYPE_PROXY; + break; + case "direct": + this.type = TYPE_DIRECT; + break; + default: + this.type = TYPE_UNKNOWN; + break; + } } public void setPriority(int i) { @@ -93,7 +98,7 @@ public class JingleCandidate { } public static List<JingleCandidate> parse(List<Element> canditates) { - List<JingleCandidate> parsedCandidates = new ArrayList<JingleCandidate>(); + List<JingleCandidate> parsedCandidates = new ArrayList<>(); for (Element c : canditates) { parsedCandidates.add(JingleCandidate.parse(c)); } @@ -104,7 +109,7 @@ public class JingleCandidate { JingleCandidate parsedCandidate = new JingleCandidate( candidate.getAttribute("cid"), false); parsedCandidate.setHost(candidate.getAttribute("host")); - parsedCandidate.setJid(candidate.getAttribute("jid")); + parsedCandidate.setJid(candidate.getJid()); parsedCandidate.setType(candidate.getAttribute("type")); parsedCandidate.setPriority(Integer.parseInt(candidate .getAttribute("priority"))); @@ -118,7 +123,7 @@ public class JingleCandidate { element.setAttribute("cid", this.getCid()); element.setAttribute("host", this.getHost()); element.setAttribute("port", Integer.toString(this.getPort())); - element.setAttribute("jid", this.getJid()); + element.setAttribute("jid", this.getJid().toString()); element.setAttribute("priority", Integer.toString(this.getPriority())); if (this.getType() == TYPE_DIRECT) { element.setAttribute("type", "direct"); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 6b9ca9aa..30e1c7da 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -21,6 +21,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.stanzas.Content; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; @@ -49,10 +50,10 @@ public class JingleConnection implements Downloadable { private Message message; private String sessionId; private Account account; - private String initiator; - private String responder; - private List<JingleCandidate> candidates = new ArrayList<JingleCandidate>(); - private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<String, JingleSocks5Transport>(); + private Jid initiator; + private Jid responder; + private List<JingleCandidate> candidates = new ArrayList<>(); + private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>(); private String transportId; private Element fileOffer; @@ -82,7 +83,7 @@ public class JingleConnection implements Downloadable { @Override public void onFileTransmitted(DownloadableFile file) { - if (responder.equals(account.getFullJid())) { + if (responder.equals(account.getJid())) { sendSuccess(); if (acceptedAutomatically) { message.markUnread(); @@ -121,7 +122,7 @@ public class JingleConnection implements Downloadable { @Override public void success() { - if (initiator.equals(account.getFullJid())) { + if (initiator.equals(account.getJid())) { Log.d(Config.LOGTAG, "we were initiating. sending file"); transport.send(file, onFileTransmissionSatusChanged); } else { @@ -150,7 +151,7 @@ public class JingleConnection implements Downloadable { return this.account; } - public String getCounterPart() { + public Jid getCounterPart() { return this.message.getCounterpart(); } @@ -203,7 +204,7 @@ public class JingleConnection implements Downloadable { this.contentName = this.mJingleConnectionManager.nextRandomId(); this.message = message; this.account = message.getConversation().getAccount(); - this.initiator = this.account.getFullJid(); + this.initiator = this.account.getJid(); this.responder = this.message.getCounterpart(); this.sessionId = this.mJingleConnectionManager.nextRandomId(); if (this.candidates.size() > 0) { @@ -254,17 +255,17 @@ public class JingleConnection implements Downloadable { this.mJingleStatus = JINGLE_STATUS_INITIATED; Conversation conversation = this.mXmppConnectionService .findOrCreateConversation(account, - packet.getFrom().split("/", 2)[0], false); + packet.getFrom().toBareJid(), false); this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message.setStatus(Message.STATUS_RECEIVED); this.message.setType(Message.TYPE_IMAGE); this.mStatus = Downloadable.STATUS_OFFER; this.message.setDownloadable(this); - String[] fromParts = packet.getFrom().split("/", 2); - this.message.setPresence(fromParts[1]); + final Jid from = packet.getFrom(); + this.message.setCounterpart(from); this.account = account; this.initiator = packet.getFrom(); - this.responder = this.account.getFullJid(); + this.responder = this.account.getJid(); this.sessionId = packet.getSessionId(); Content content = packet.getJingleContent(); this.contentCreator = content.getAttribute("creator"); @@ -375,7 +376,7 @@ public class JingleConnection implements Downloadable { } private List<Element> getCandidatesAsElements() { - List<Element> elements = new ArrayList<Element>(); + List<Element> elements = new ArrayList<>(); for (JingleCandidate c : this.candidates) { elements.add(c.toElement()); } @@ -443,7 +444,7 @@ public class JingleConnection implements Downloadable { private JinglePacket bootstrapPacket(String action) { JinglePacket packet = new JinglePacket(); packet.setAction(action); - packet.setFrom(account.getFullJid()); + packet.setFrom(account.getJid()); packet.setTo(this.message.getCounterpart()); packet.setSessionId(this.sessionId); packet.setInitiator(this.initiator); @@ -532,7 +533,7 @@ public class JingleConnection implements Downloadable { if (connection == null) { Log.d(Config.LOGTAG, "could not find suitable candidate"); this.disconnect(); - if (this.initiator.equals(account.getFullJid())) { + if (this.initiator.equals(account.getJid())) { this.sendFallbackToIbb(); } } else { @@ -547,7 +548,7 @@ public class JingleConnection implements Downloadable { activation.query("http://jabber.org/protocol/bytestreams") .setAttribute("sid", this.getSessionId()); activation.query().addChild("activate") - .setContent(this.getCounterPart()); + .setContent(this.getCounterPart().toString()); this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() { @@ -570,7 +571,7 @@ public class JingleConnection implements Downloadable { + " was a proxy. waiting for other party to activate"); } } else { - if (initiator.equals(account.getFullJid())) { + if (initiator.equals(account.getJid())) { Log.d(Config.LOGTAG, "we were initiating. sending file"); connection.send(file, onFileTransmissionSatusChanged); } else { @@ -600,7 +601,7 @@ public class JingleConnection implements Downloadable { } else if (connection.getCandidate().getPriority() == currentConnection .getCandidate().getPriority()) { // Log.d(Config.LOGTAG,"found two candidates with same priority"); - if (initiator.equals(account.getFullJid())) { + if (initiator.equals(account.getJid())) { if (currentConnection.getCandidate().isOurs()) { connection = currentConnection; } @@ -709,7 +710,7 @@ public class JingleConnection implements Downloadable { this.mJingleStatus = JINGLE_STATUS_CANCELED; this.disconnect(); if (this.message != null) { - if (this.responder.equals(account.getFullJid())) { + if (this.responder.equals(account.getJid())) { this.mStatus = Downloadable.STATUS_FAILED; this.mXmppConnectionService.updateConversationUi(); } else { @@ -810,11 +811,11 @@ public class JingleConnection implements Downloadable { this.sendJinglePacket(packet); } - public String getInitiator() { + public Jid getInitiator() { return this.initiator; } - public String getResponder() { + public Jid getResponder() { return this.responder; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index d937146a..05a658be 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -14,13 +14,15 @@ import eu.siacs.conversations.services.AbstractConnectionManager; 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.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class JingleConnectionManager extends AbstractConnectionManager { - private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>(); + private List<JingleConnection> connections = new CopyOnWriteArrayList<>(); - private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>(); + private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>(); @SuppressLint("TrulyRandom") private SecureRandom random = new SecureRandom(); @@ -61,7 +63,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { return connection; } - public JingleConnection createNewConnection(JinglePacket packet) { + public JingleConnection createNewConnection(final JinglePacket packet) { JingleConnection connection = new JingleConnection(this); this.connections.add(connection); return connection; @@ -73,13 +75,13 @@ public class JingleConnectionManager extends AbstractConnectionManager { public void getPrimaryCandidate(Account account, final OnPrimaryCandidateFound listener) { - if (!this.primaryCandidates.containsKey(account.getJid())) { + if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) { String xmlns = "http://jabber.org/protocol/bytestreams"; final String proxy = account.getXmppConnection() .findDiscoItemByFeature(xmlns); if (proxy != null) { IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - iq.setTo(proxy); + iq.setAttribute("to", proxy); iq.query(xmlns); account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() { @@ -101,9 +103,13 @@ public class JingleConnectionManager extends AbstractConnectionManager { .getAttribute("port"))); candidate .setType(JingleCandidate.TYPE_PROXY); - candidate.setJid(proxy); - candidate.setPriority(655360 + 65535); - primaryCandidates.put(account.getJid(), + try { + candidate.setJid(Jid.fromString(proxy)); + } catch (final InvalidJidException e) { + candidate.setJid(null); + } + candidate.setPriority(655360 + 65535); + primaryCandidates.put(account.getJid().toBareJid(), candidate); listener.onPrimaryCandidateFound(true, candidate); @@ -119,7 +125,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { } else { listener.onPrimaryCandidateFound(true, - this.primaryCandidates.get(account.getJid())); + this.primaryCandidates.get(account.getJid().toBareJid())); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index cc1e92f6..e3f4fd61 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -13,12 +13,13 @@ import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class JingleInbandTransport extends JingleTransport { private Account account; - private String counterpart; + private Jid counterpart; private int blockSize; private int bufferSize; private int seq = 0; @@ -44,8 +45,8 @@ public class JingleInbandTransport extends JingleTransport { } }; - public JingleInbandTransport(Account account, String counterpart, - String sid, int blocksize) { + public JingleInbandTransport(final Account account, final Jid counterpart, + final String sid, final int blocksize) { this.account = account; this.counterpart = counterpart; this.blockSize = blocksize; @@ -92,12 +93,10 @@ public class JingleInbandTransport extends JingleTransport { return; } this.remainingSize = file.getExpectedSize(); - } catch (NoSuchAlgorithmException e) { - callback.onFileTransferAborted(); - } catch (IOException e) { + } catch (final NoSuchAlgorithmException | IOException e) { callback.onFileTransferAborted(); } - } + } @Override public void send(DownloadableFile file, diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 1da2f0cd..83b597eb 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -30,11 +30,11 @@ public class JingleSocks5Transport extends JingleTransport { StringBuilder destBuilder = new StringBuilder(); destBuilder.append(jingleConnection.getSessionId()); if (candidate.isOurs()) { - destBuilder.append(jingleConnection.getAccount().getFullJid()); + destBuilder.append(jingleConnection.getAccount().getJid()); destBuilder.append(jingleConnection.getCounterPart()); } else { destBuilder.append(jingleConnection.getCounterPart()); - destBuilder.append(jingleConnection.getAccount().getFullJid()); + destBuilder.append(jingleConnection.getAccount().getJid()); } mDigest.reset(); this.destination = CryptoHelper.bytesToHex(mDigest diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java index 77a73643..4f73a83a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp.jingle.stanzas; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class JinglePacket extends IqPacket { @@ -85,8 +86,8 @@ public class JinglePacket extends IqPacket { return this.jingle.getAttribute("action"); } - public void setInitiator(String initiator) { - this.jingle.setAttribute("initiator", initiator); + public void setInitiator(final Jid initiator) { + this.jingle.setAttribute("initiator", initiator.toString()); } public boolean isAction(String action) { 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..eade220a 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,8 @@ package eu.siacs.conversations.xmpp.stanzas; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public class AbstractStanza extends Element { @@ -8,27 +10,40 @@ public class AbstractStanza extends Element { super(name); } - public String getTo() { - return getAttribute("to"); + public Jid getTo() { + try { + return Jid.fromString(getAttribute("to")); + } catch (final InvalidJidException e) { + return null; + } } - public String getFrom() { - return getAttribute("from"); + public Jid getFrom() { + String from = getAttribute("from"); + if (from == null) { + return null; + } else { + try { + return Jid.fromString(from); + } catch (final InvalidJidException e) { + return null; + } + } } public String getId() { 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); } } diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 47424d00..82cc0a22 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -254,6 +254,23 @@ <string name="pref_enable_legacy_ssl_summary">Habilita soporte SSLv3 para servidores heredados. Advertencia: SSLv3 se considera no seguro.</string> <string name="pref_expert_options">Ajustes avanzados</string> <string name="pref_expert_options_summary">Por favor, cuidado con estas opciones</string> + <string name="title_activity_about">About Conversations</string> + <string name="pref_about_conversations_summary">Build and licensing information</string> + <string name="pref_about_message">Conversations © 2014 Daniel Gultsch\n + \nThis program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + \nhttps://www.gnu.org/licenses/gpl-3.0.html\n + \nOpenPGP API is licensed under the Apache License, Version 2.0 + \nhttps://www.apache.org/licenses/LICENSE-2.0\n + \nMinidns © 2014 Rene Treffer and is provided under the WTFPL + \nhttp://wtfpl.org\n + \nMemorizingTrustManager © 2010 Georg Lukas under the terms of the MIT + License + \nhttp://opensource.org/licenses/MIT\n + \nDownload the full source code at + \nhttps://github.com/siacs/Conversations + </string> <string name="pref_use_larger_font">Incrementar tamaño de fuente</string> <string name="pref_use_larger_font_summary">Usar fuentes grandes en toda la aplicación</string> <string name="pref_use_send_button_to_indicate_status">Botón enviar indica estado</string> @@ -281,5 +298,8 @@ <string name="message_text">Mensaje de texto</string> <string name="url_copied_to_clipboard">URL copiada al portapapeles</string> <string name="message_copied_to_clipboard">Mensaje copiado al portapapeles</string> - + <string name="image_transmission_failed">Falló la transmisión de la imagen</string> + <string name="scan_qr_code">Escanear código QR</string> + <string name="show_qr_code">Mostrar código QR</string> + <string name="account_details">Detalles de la cuenta</string> </resources>
\ No newline at end of file |