diff options
Diffstat (limited to 'src/main/java/de')
5 files changed, 197 insertions, 244 deletions
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java index c641902a3..8b2eef3eb 100644 --- a/src/main/java/de/pixart/messenger/entities/Account.java +++ b/src/main/java/de/pixart/messenger/entities/Account.java @@ -49,6 +49,7 @@ public class Account extends AbstractEntity { public static final String PORT = "port"; public static final String STATUS = "status"; public static final String STATUS_MESSAGE = "status_message"; + public static final String RESOURCE = "resource"; public static final String PINNED_MECHANISM_KEY = "pinned_mechanism"; @@ -238,6 +239,7 @@ public class Account extends AbstractEntity { protected String rosterVersion; protected State status = State.OFFLINE; protected final JSONObject keys; + protected String resource; protected String avatar; protected String displayName = null; protected String hostname = null; @@ -266,9 +268,6 @@ public class Account extends AbstractEntity { final Presence.Status status, String statusMessage) { this.uuid = uuid; this.jid = jid; - if (jid.isBareJid()) { - this.setResource("mobile"); - } this.password = password; this.options = options; this.rosterVersion = rosterVersion; @@ -290,8 +289,10 @@ public class Account extends AbstractEntity { public static Account fromCursor(final Cursor cursor) { Jid jid = null; try { - jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)), - cursor.getString(cursor.getColumnIndex(SERVER)), "mobile"); + jid = Jid.fromParts( + cursor.getString(cursor.getColumnIndex(USERNAME)), + cursor.getString(cursor.getColumnIndex(SERVER)), + cursor.getString(cursor.getColumnIndex(RESOURCE))); } catch (final InvalidJidException ignored) { } return new Account(cursor.getString(cursor.getColumnIndex(UUID)), @@ -327,6 +328,7 @@ public class Account extends AbstractEntity { } public boolean setJid(final Jid next) { + final Jid previousFull = this.jid; final Jid prev = this.jid != null ? this.jid.toBareJid() : null; final boolean changed = prev == null || (next != null && !prev.equals(next.toBareJid())); if (changed) { @@ -338,7 +340,7 @@ public class Account extends AbstractEntity { } } this.jid = next; - return changed; + return next != null && next.equals(previousFull); } public Jid getServer() { @@ -493,6 +495,7 @@ public class Account extends AbstractEntity { values.put(PORT, port); values.put(STATUS, presenceStatus.toShowString()); values.put(STATUS_MESSAGE, presenceStatusMessage); + values.put(RESOURCE, jid.getResourcepart()); return values; } diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index cc334addb..c0c74d78b 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -59,7 +59,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; public static final String DATABASE_NAME = "history"; - public static final int DATABASE_VERSION = 40; // = Conversations DATABASE_VERSION + 1 + public static final int DATABASE_VERSION = 41; // = Conversations DATABASE_VERSION + 1 private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -179,6 +179,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Account.AVATAR + " TEXT, " + Account.KEYS + " TEXT, " + Account.HOSTNAME + " TEXT, " + + Account.RESOURCE + " TEXT," + Account.PORT + " NUMBER DEFAULT 5222)"); db.execSQL("create table " + Conversation.TABLENAME + " (" + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME @@ -301,6 +302,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT"); db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT"); } + if (oldVersion < 41 && newVersion >= 41) { + db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.RESOURCE + " TEXT"); + } /* Any migrations that alter the Account table need to happen BEFORE this migration, as it * depends on account de-serialization. */ diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 19266764a..74c6a84f6 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -1262,17 +1262,6 @@ public class XmppConnectionService extends Service { } public XmppConnection createConnection(final Account account) { - final SharedPreferences sharedPref = getPreferences(); - String resource; - try { - resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH); - if (resource.trim().isEmpty()) { - throw new Exception(); - } - } catch (Exception e) { - resource = "Pix-Art Messenger"; - } - account.setResource(resource); final XmppConnection connection = new XmppConnection(account, this); connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index b6b1ba565..c832809d6 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -11,11 +11,11 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.CheckBoxPreference; -import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.LayoutInflater; @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Locale; import de.pixart.messenger.Config; import de.pixart.messenger.R; @@ -37,7 +36,6 @@ import de.pixart.messenger.entities.Account; import de.pixart.messenger.services.ExportLogsService; import de.pixart.messenger.services.MemorizingTrustManager; import de.pixart.messenger.ui.util.Color; -import de.pixart.messenger.xmpp.XmppConnection; import de.pixart.messenger.xmpp.jid.InvalidJidException; import de.pixart.messenger.xmpp.jid.Jid; @@ -96,16 +94,6 @@ public class SettingsActivity extends XmppActivity implements isMultiAccountChecked = ((CheckBoxPreference) multiAccountPreference).isChecked(); } - ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource"); - if (resources != null) { - ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries())); - if (!entries.contains(Build.MODEL)) { - entries.add(0, Build.MODEL); - resources.setEntries(entries.toArray(new CharSequence[entries.size()])); - resources.setEntryValues(entries.toArray(new CharSequence[entries.size()])); - } - } - if (Config.FORCE_ORBOT) { PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options"); PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert"); @@ -303,36 +291,29 @@ public class SettingsActivity extends XmppActivity implements } } final boolean[] checkedItems = new boolean[accounts.size()]; - builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int which, boolean isChecked) { - checkedItems[which] = isChecked; - final AlertDialog alertDialog = (AlertDialog) dialog; - for (boolean item : checkedItems) { - if (item) { - alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true); - return; - } + builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, (dialog, which, isChecked) -> { + checkedItems[which] = isChecked; + final AlertDialog alertDialog = (AlertDialog) dialog; + for (boolean item : checkedItems) { + if (item) { + alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true); + return; } - alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false); } + alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false); }); builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - for (int i = 0; i < checkedItems.length; ++i) { - if (checkedItems[i]) { - try { - Jid jid = Jid.fromString(accounts.get(i).toString()); - Account account = xmppConnectionService.findAccountByJid(jid); - if (account != null) { - account.getAxolotlService().regenerateKeys(true); - } - } catch (InvalidJidException e) { - // + builder.setPositiveButton(R.string.delete_selected_keys, (dialog, which) -> { + for (int i = 0; i < checkedItems.length; ++i) { + if (checkedItems[i]) { + try { + Jid jid = Jid.fromString(accounts.get(i).toString()); + Account account = xmppConnectionService.findAccountByJid(jid); + if (account != null) { + account.getAxolotlService().regenerateKeys(true); } - + } catch (InvalidJidException e) { + // } } } @@ -429,23 +410,7 @@ public class SettingsActivity extends XmppActivity implements TREAT_VIBRATE_AS_SILENT, MANUALLY_CHANGE_PRESENCE, BROADCAST_LAST_ACTIVITY); - if (name.equals("resource")) { - String resource = preferences.getString("resource", "mobile") - .toLowerCase(Locale.US); - if (xmppConnectionServiceBound) { - for (Account account : xmppConnectionService.getAccounts()) { - if (account.setResource(resource)) { - if (account.isEnabled()) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.resetStreamId(); - } - xmppConnectionService.reconnectAccountInBackground(account); - } - } - } - } - } else if (name.equals(SHOW_FOREGROUND_SERVICE)) { + if (name.equals(SHOW_FOREGROUND_SERVICE)) { xmppConnectionService.toggleForegroundService(); } else if (resendPresence.contains(name)) { if (xmppConnectionServiceBound) { @@ -474,7 +439,7 @@ public class SettingsActivity extends XmppActivity implements } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (grantResults.length > 0) if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (requestCode == REQUEST_WRITE_LOGS) { @@ -490,12 +455,7 @@ public class SettingsActivity extends XmppActivity implements } private void displayToast(final String msg) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show(); - } - }); + runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show()); } private void reconnectAccounts() { diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java index 1025c5f1d..154627afe 100644 --- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java @@ -50,9 +50,9 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; -import de.pixart.messenger.crypto.DomainHostnameVerifier; -import de.pixart.messenger.services.MemorizingTrustManager; import de.pixart.messenger.Config; +import de.pixart.messenger.R; +import de.pixart.messenger.crypto.DomainHostnameVerifier; import de.pixart.messenger.crypto.XmppDomainVerifier; import de.pixart.messenger.crypto.axolotl.AxolotlService; import de.pixart.messenger.crypto.sasl.Anonymous; @@ -67,6 +67,7 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.ServiceDiscoveryResult; import de.pixart.messenger.generator.IqGenerator; import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.services.MemorizingTrustManager; import de.pixart.messenger.services.NotificationService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.EditAccountActivity; @@ -104,21 +105,55 @@ 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; + public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + account.setOption(Account.OPTION_REGISTER, false); + throw new StateChangingError(Account.State.REGISTRATION_SUCCESSFUL); + } else { + final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList( + "The password is too weak", + "Please use a longer password."); + final Element error = packet.findChild("error"); + Account.State state = Account.State.REGISTRATION_FAILED; + mXmppConnectionService.deleteAccount(account); + if (error != null) { + if (error.hasChild("text")) { + errorMessage = error.findChildContent("text"); + Log.d(Config.LOGTAG, "Error creating account : " + error.findChildContent("text")); + } + if (error.hasChild("conflict")) { + state = Account.State.REGISTRATION_CONFLICT; + } else if (error.hasChild("resource-constraint") + && "wait".equals(error.getAttribute("type"))) { + state = Account.State.REGISTRATION_PLEASE_WAIT; + } else if (error.hasChild("not-acceptable") + && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) { + state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK; + } + } + Log.d(Config.LOGTAG, "Delete account because of error " + error); + throw new StateChangingError(state); + } + } + }; protected final Account account; + private final Features features = new Features(this); + private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>(); + private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>(); + private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>(); + private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>(); + private final XmppConnectionService mXmppConnectionService; private Socket socket; private XmlReader tagReader; private TagWriter tagWriter = new TagWriter(); - private final Features features = new Features(this); private boolean isBound = false; private boolean shouldAuthenticate = true; private boolean inSmacksSession = false; private Element streamFeatures; - private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>(); - private String streamId = null; private int smVersion = 3; - private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>(); - private int stanzasReceived = 0; private int stanzasSent = 0; private long lastPacketReceived = 0; @@ -133,101 +168,20 @@ public class XmppConnection implements Runnable { private boolean mInteractive = false; private int attempt = 0; public static String errorMessage = null; - private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>(); private OnPresencePacketReceived presenceListener = null; private OnJinglePacketReceived jingleListener = null; private OnIqPacketReceived unregisteredIqListener = null; private OnMessagePacketReceived messageListener = null; private OnStatusChanged statusListener = null; private OnBindListener bindListener = null; - private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>(); private OnMessageAcknowledged acknowledgedListener = null; - private final XmppConnectionService mXmppConnectionService; private EditAccountActivity mEditAccountActivity = null; - private SaslMechanism saslMechanism; private URL redirectionUrl = null; private String verifiedHostname = null; private Thread mThread; private CountDownLatch mStreamCountDownLatch; - private class MyKeyManager implements X509KeyManager { - @Override - public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { - return account.getPrivateKeyAlias(); - } - - @Override - public String chooseServerAlias(String s, Principal[] principals, Socket socket) { - return null; - } - - @Override - public X509Certificate[] getCertificateChain(String alias) { - Log.d(Config.LOGTAG, "getting certificate chain"); - try { - return KeyChain.getCertificateChain(mXmppConnectionService, alias); - } catch (Exception e) { - Log.d(Config.LOGTAG, e.getMessage()); - return new X509Certificate[0]; - } - } - - @Override - public String[] getClientAliases(String s, Principal[] principals) { - final String alias = account.getPrivateKeyAlias(); - return alias != null ? new String[]{alias} : new String[0]; - } - - @Override - public String[] getServerAliases(String s, Principal[] principals) { - return new String[0]; - } - - @Override - public PrivateKey getPrivateKey(String alias) { - try { - return KeyChain.getPrivateKey(mXmppConnectionService, alias); - } catch (Exception e) { - return null; - } - } - } - - public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - account.setOption(Account.OPTION_REGISTER, false); - throw new StateChangingError(Account.State.REGISTRATION_SUCCESSFUL); - } else { - final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList( - "The password is too weak", - "Please use a longer password."); - final Element error = packet.findChild("error"); - Account.State state = Account.State.REGISTRATION_FAILED; - mXmppConnectionService.deleteAccount(account); - if (error != null) { - if (error.hasChild("text")) { - errorMessage = error.findChildContent("text"); - Log.d(Config.LOGTAG, "Error creating account : " + error.findChildContent("text")); - } - if (error.hasChild("conflict")) { - state = Account.State.REGISTRATION_CONFLICT; - } else if (error.hasChild("resource-constraint") - && "wait".equals(error.getAttribute("type"))) { - state = Account.State.REGISTRATION_PLEASE_WAIT; - } else if (error.hasChild("not-acceptable") - && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) { - state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK; - } - } - Log.d(Config.LOGTAG, "Delete account because of error " + error); - throw new StateChangingError(state); - } - } - }; - public XmppConnection(final Account account, final XmppConnectionService service) { this.account = account; final String tag = account.getJid().toBareJid().toPreppedString(); @@ -509,19 +463,6 @@ public class XmppConnection implements Runnable { return tag != null && tag.isStart("stream"); } - private static class TlsFactoryVerifier { - private final SSLSocketFactory factory; - private final DomainHostnameVerifier verifier; - - public TlsFactoryVerifier(final SSLSocketFactory factory, final DomainHostnameVerifier verifier) throws IOException { - this.factory = factory; - this.verifier = verifier; - if (factory == null || verifier == null) { - throw new IOException("could not setup ssl"); - } - } - } - private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException { final SSLContext sc = SSLSocketHelper.getSSLContext(); MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager(); @@ -865,7 +806,6 @@ public class XmppConnection implements Runnable { tagWriter.writeTag(startTLS); } - private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException { tagReader.readTag(); try { @@ -1099,55 +1039,52 @@ public class XmppConnection implements Runnable { return; } clearIqCallbacks(); + if (account.getJid().isBareJid()) { + account.setResource(this.createNewResource()); + } final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); final String resource = Config.USE_RANDOM_RESOURCE_ON_EVERY_BIND ? nextRandomId() : account.getResource(); iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource); - this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - return; - } - final Element bind = packet.findChild("bind"); - if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { - isBound = true; - final Element jid = bind.findChild("jid"); - if (jid != null && jid.getContent() != null) { - try { - Jid assignedJid = Jid.fromString(jid.getContent()); - if (!account.getJid().getDomainpart().equals(assignedJid.getDomainpart())) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server tried to re-assign domain to " + assignedJid.getDomainpart()); - throw new StateChangingError(Account.State.BIND_FAILURE); - } - if (account.setJid(assignedJid)) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": bare jid changed during bind. updating database"); - mXmppConnectionService.databaseBackend.updateAccount(account); - } - if (streamFeatures.hasChild("session") - && !streamFeatures.findChild("session").hasChild("optional")) { - sendStartSession(); - } else { - sendPostBindInitialization(); - } - return; - } catch (final InvalidJidException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server reported invalid jid (" + jid.getContent() + ") on bind"); + this.sendUnmodifiedIqPacket(iq, (account, packet) -> { + if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + return; + } + final Element bind = packet.findChild("bind"); + if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { + isBound = true; + final Element jid = bind.findChild("jid"); + if (jid != null && jid.getContent() != null) { + try { + Jid assignedJid = Jid.fromString(jid.getContent()); + if (!account.getJid().getDomainpart().equals(assignedJid.getDomainpart())) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server tried to re-assign domain to " + assignedJid.getDomainpart()); + throw new StateChangingError(Account.State.BIND_FAILURE); } - } else { - Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)"); + if (account.setJid(assignedJid)) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": jid changed during bind. updating database"); + mXmppConnectionService.databaseBackend.updateAccount(account); + } + if (streamFeatures.hasChild("session") + && !streamFeatures.findChild("session").hasChild("optional")) { + sendStartSession(); + } else { + sendPostBindInitialization(); + } + return; + } catch (final InvalidJidException e) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server reported invalid jid (" + jid.getContent() + ") on bind"); } } else { - Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString()); + Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)"); } - final Element error = packet.findChild("error"); - final String resource = account.getResource().split("\\.")[0]; - if (packet.getType() == IqPacket.TYPE.ERROR && error != null && error.hasChild("conflict")) { - account.setResource(resource + "." + nextRandomId()); - } else { - account.setResource(resource); - } - throw new StateChangingError(Account.State.BIND_FAILURE); + } else { + Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString()); } + final Element error = packet.findChild("error"); + if (packet.getType() == IqPacket.TYPE.ERROR && error != null && error.hasChild("conflict")) { + account.setResource(createNewResource()); + } + throw new StateChangingError(Account.State.BIND_FAILURE); }, true); } @@ -1367,18 +1304,14 @@ public class XmppConnection implements Runnable { }); } - private void processStreamError(final Tag currentTag) - throws XmlPullParserException, IOException { + private void processStreamError(final Tag currentTag) throws XmlPullParserException, IOException { final Element streamError = tagReader.readElement(currentTag); if (streamError == null) { return; } if (streamError.hasChild("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() + ")"); + account.setResource(createNewResource()); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": switching resource due to conflict (" + account.getResource() + ")"); throw new IOException(); } else if (streamError.hasChild("host-unknown")) { throw new StateChangingException(Account.State.HOST_UNKNOWN); @@ -1400,8 +1333,16 @@ public class XmppConnection implements Runnable { tagWriter.writeTag(stream); } + private String createNewResource() { + return mXmppConnectionService.getString(R.string.app_name) + '.' + nextRandomId(true); + } + private String nextRandomId() { - return CryptoHelper.random(10, mXmppConnectionService.getRNG()); + return nextRandomId(false); + } + + private String nextRandomId(boolean s) { + return CryptoHelper.random(s ? 3 : 9, mXmppConnectionService.getRNG()); } public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { @@ -1690,6 +1631,75 @@ public class XmppConnection implements Runnable { return Identity.UNKNOWN; } + private IqGenerator getIqGenerator() { + return mXmppConnectionService.getIqGenerator(); + } + + public enum Identity { + FACEBOOK, + SLACK, + EJABBERD, + PROSODY, + NIMBUZZ, + UNKNOWN + } + + private static class TlsFactoryVerifier { + private final SSLSocketFactory factory; + private final DomainHostnameVerifier verifier; + + public TlsFactoryVerifier(final SSLSocketFactory factory, final DomainHostnameVerifier verifier) throws IOException { + this.factory = factory; + this.verifier = verifier; + if (factory == null || verifier == null) { + throw new IOException("could not setup ssl"); + } + } + } + + private class MyKeyManager implements X509KeyManager { + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { + return account.getPrivateKeyAlias(); + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return null; + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + Log.d(Config.LOGTAG, "getting certificate chain"); + try { + return KeyChain.getCertificateChain(mXmppConnectionService, alias); + } catch (Exception e) { + Log.d(Config.LOGTAG, e.getMessage()); + return new X509Certificate[0]; + } + } + + @Override + public String[] getClientAliases(String s, Principal[] principals) { + final String alias = account.getPrivateKeyAlias(); + return alias != null ? new String[]{alias} : new String[0]; + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return new String[0]; + } + + @Override + public PrivateKey getPrivateKey(String alias) { + try { + return KeyChain.getPrivateKey(mXmppConnectionService, alias); + } catch (Exception e) { + return null; + } + } + } + private class StateChangingError extends Error { private final Account.State state; @@ -1706,15 +1716,6 @@ public class XmppConnection implements Runnable { } } - public enum Identity { - FACEBOOK, - SLACK, - EJABBERD, - PROSODY, - NIMBUZZ, - UNKNOWN - } - public class Features { XmppConnection connection; private boolean carbonsEnabled = false; @@ -1846,8 +1847,4 @@ public class XmppConnection implements Runnable { return hasDiscoFeature(account.getJid().toBareJid(), Namespace.STANZA_IDS); } } - - private IqGenerator getIqGenerator() { - return mXmppConnectionService.getIqGenerator(); - } } |