get rid of customizable resources

This commit is contained in:
Daniel Gultsch 2018-02-27 20:33:21 +01:00
parent fcbbb552f4
commit b393f54a03
8 changed files with 199 additions and 259 deletions

View file

@ -42,6 +42,7 @@ public class Account extends AbstractEntity {
public static final String PORT = "port"; public static final String PORT = "port";
public static final String STATUS = "status"; public static final String STATUS = "status";
public static final String STATUS_MESSAGE = "status_message"; public static final String STATUS_MESSAGE = "status_message";
public static final String RESOURCE = "resource";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism"; public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
@ -229,6 +230,7 @@ public class Account extends AbstractEntity {
protected String rosterVersion; protected String rosterVersion;
protected State status = State.OFFLINE; protected State status = State.OFFLINE;
protected final JSONObject keys; protected final JSONObject keys;
protected String resource;
protected String avatar; protected String avatar;
protected String displayName = null; protected String displayName = null;
protected String hostname = null; protected String hostname = null;
@ -238,7 +240,6 @@ public class Account extends AbstractEntity {
private PgpDecryptionService pgpDecryptionService = null; private PgpDecryptionService pgpDecryptionService = null;
private XmppConnection xmppConnection = null; private XmppConnection xmppConnection = null;
private long mEndGracePeriod = 0L; private long mEndGracePeriod = 0L;
private String otrFingerprint;
private final Roster roster = new Roster(this); private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>(); private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
@ -256,9 +257,6 @@ public class Account extends AbstractEntity {
final Presence.Status status, String statusMessage) { final Presence.Status status, String statusMessage) {
this.uuid = uuid; this.uuid = uuid;
this.jid = jid; this.jid = jid;
if (jid.isBareJid()) {
this.setResource("mobile");
}
this.password = password; this.password = password;
this.options = options; this.options = options;
this.rosterVersion = rosterVersion; this.rosterVersion = rosterVersion;
@ -280,8 +278,10 @@ public class Account extends AbstractEntity {
public static Account fromCursor(final Cursor cursor) { public static Account fromCursor(final Cursor cursor) {
Jid jid = null; Jid jid = null;
try { try {
jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)), jid = Jid.fromParts(
cursor.getString(cursor.getColumnIndex(SERVER)), "mobile"); cursor.getString(cursor.getColumnIndex(USERNAME)),
cursor.getString(cursor.getColumnIndex(SERVER)),
cursor.getString(cursor.getColumnIndex(RESOURCE)));
} catch (final InvalidJidException ignored) { } catch (final InvalidJidException ignored) {
} }
return new Account(cursor.getString(cursor.getColumnIndex(UUID)), return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
@ -317,6 +317,7 @@ public class Account extends AbstractEntity {
} }
public boolean setJid(final Jid next) { public boolean setJid(final Jid next) {
final Jid previousFull = this.jid;
final Jid prev = this.jid != null ? this.jid.toBareJid() : null; final Jid prev = this.jid != null ? this.jid.toBareJid() : null;
final boolean changed = prev == null || (next != null && !prev.equals(next.toBareJid())); final boolean changed = prev == null || (next != null && !prev.equals(next.toBareJid()));
if (changed) { if (changed) {
@ -328,7 +329,7 @@ public class Account extends AbstractEntity {
} }
} }
this.jid = next; this.jid = next;
return changed; return next != null && next.equals(previousFull);
} }
public Jid getServer() { public Jid getServer() {
@ -483,6 +484,7 @@ public class Account extends AbstractEntity {
values.put(PORT, port); values.put(PORT, port);
values.put(STATUS, presenceStatus.toShowString()); values.put(STATUS, presenceStatus.toShowString());
values.put(STATUS_MESSAGE, presenceStatusMessage); values.put(STATUS_MESSAGE, presenceStatusMessage);
values.put(RESOURCE,jid.getResourcepart());
return values; return values;
} }

View file

@ -62,7 +62,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history"; private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 39; private static final int DATABASE_VERSION = 40;
private static String CREATE_CONTATCS_STATEMENT = "create table " private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -184,6 +184,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Account.AVATAR + " TEXT, " + Account.AVATAR + " TEXT, "
+ Account.KEYS + " TEXT, " + Account.KEYS + " TEXT, "
+ Account.HOSTNAME + " TEXT, " + Account.HOSTNAME + " TEXT, "
+ Account.RESOURCE + " TEXT,"
+ Account.PORT + " NUMBER DEFAULT 5222)"); + Account.PORT + " NUMBER DEFAULT 5222)");
db.execSQL("create table " + Conversation.TABLENAME + " (" db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
@ -305,6 +306,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 + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT"); db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
} }
if (oldVersion < 40 && newVersion >= 40) {
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 /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization. * depends on account de-serialization.
*/ */

View file

@ -1125,17 +1125,6 @@ public class XmppConnectionService extends Service {
} }
public XmppConnection createConnection(final Account account) { 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 = "conversations";
}
account.setResource(resource);
final XmppConnection connection = new XmppConnection(account, this); final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener); connection.setOnStatusChangedListener(this.statusListener);

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -68,7 +69,7 @@ public class SettingsActivity extends XmppActivity implements
this.mTheme = findTheme(); this.mTheme = findTheme();
setTheme(this.mTheme); setTheme(this.mTheme);
getWindow().getDecorView().setBackgroundColor(Color.get(this,R.attr.color_background_primary)); getWindow().getDecorView().setBackgroundColor(Color.get(this, R.attr.color_background_primary));
} }
@ -81,15 +82,6 @@ public class SettingsActivity extends XmppActivity implements
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
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) { if (Config.FORCE_ORBOT) {
PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options"); PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
@ -281,37 +273,31 @@ public class SettingsActivity extends XmppActivity implements
} }
} }
final boolean[] checkedItems = new boolean[accounts.size()]; final boolean[] checkedItems = new boolean[accounts.size()];
builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() { builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, (dialog, which, isChecked) -> {
@Override checkedItems[which] = isChecked;
public void onClick(DialogInterface dialog, int which, boolean isChecked) { final AlertDialog alertDialog = (AlertDialog) dialog;
checkedItems[which] = isChecked; for (boolean item : checkedItems) {
final AlertDialog alertDialog = (AlertDialog) dialog; if (item) {
for (boolean item : checkedItems) { alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
if (item) { return;
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.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.delete_selected_keys, (dialog, which) -> {
@Override for (int i = 0; i < checkedItems.length; ++i) {
public void onClick(DialogInterface dialog, int which) { if (checkedItems[i]) {
for (int i = 0; i < checkedItems.length; ++i) { try {
if (checkedItems[i]) { Jid jid = Jid.fromString(accounts.get(i).toString());
try { Account account = xmppConnectionService.findAccountByJid(jid);
Jid jid = Jid.fromString(accounts.get(i).toString()); if (account != null) {
Account account = xmppConnectionService.findAccountByJid(jid); account.getAxolotlService().regenerateKeys(true);
if (account != null) {
account.getAxolotlService().regenerateKeys(true);
}
} catch (InvalidJidException e) {
//
} }
} catch (InvalidJidException e) {
//
} }
} }
} }
}); });
@ -338,23 +324,7 @@ public class SettingsActivity extends XmppActivity implements
TREAT_VIBRATE_AS_SILENT, TREAT_VIBRATE_AS_SILENT,
MANUALLY_CHANGE_PRESENCE, MANUALLY_CHANGE_PRESENCE,
BROADCAST_LAST_ACTIVITY); BROADCAST_LAST_ACTIVITY);
if (name.equals("resource")) { if (name.equals(KEEP_FOREGROUND_SERVICE)) {
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(KEEP_FOREGROUND_SERVICE)) {
xmppConnectionService.toggleForegroundService(); xmppConnectionService.toggleForegroundService();
} else if (resendPresence.contains(name)) { } else if (resendPresence.contains(name)) {
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
@ -383,7 +353,7 @@ public class SettingsActivity extends XmppActivity implements
} }
@Override @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.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_WRITE_LOGS) { if (requestCode == REQUEST_WRITE_LOGS) {
@ -399,12 +369,7 @@ public class SettingsActivity extends XmppActivity implements
} }
private void displayToast(final String msg) { private void displayToast(final String msg) {
runOnUiThread(new Runnable() { runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
@Override
public void run() {
Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
}
});
} }
private void reconnectAccounts() { private void reconnectAccounts() {

View file

@ -50,6 +50,7 @@ import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.DomainHostnameVerifier; import eu.siacs.conversations.crypto.DomainHostnameVerifier;
import eu.siacs.conversations.crypto.XmppDomainVerifier; import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@ -102,94 +103,6 @@ public class XmppConnection implements Runnable {
private static final int PACKET_IQ = 0; private static final int PACKET_IQ = 0;
private static final int PACKET_MESSAGE = 1; private static final int PACKET_MESSAGE = 1;
private static final int PACKET_PRESENCE = 2; private static final int PACKET_PRESENCE = 2;
protected final Account account;
private Socket socket;
private XmlReader tagReader;
private TagWriter tagWriter = new TagWriter();
private final Features features = new Features(this);
private boolean shouldAuthenticate = true;
private boolean inSmacksSession = false;
private boolean isBound = 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;
private long lastPingSent = 0;
private long lastConnect = 0;
private long lastSessionStarted = 0;
private long lastDiscoStarted = 0;
private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
private AtomicBoolean mWaitingForSmCatchup = new AtomicBoolean(false);
private AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0);
private boolean mInteractive = false;
private int attempt = 0;
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 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() { public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
@ -217,6 +130,47 @@ public class XmppConnection implements Runnable {
} }
} }
}; };
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 boolean shouldAuthenticate = true;
private boolean inSmacksSession = false;
private boolean isBound = false;
private Element streamFeatures;
private String streamId = null;
private int smVersion = 3;
private int stanzasReceived = 0;
private int stanzasSent = 0;
private long lastPacketReceived = 0;
private long lastPingSent = 0;
private long lastConnect = 0;
private long lastSessionStarted = 0;
private long lastDiscoStarted = 0;
private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
private AtomicBoolean mWaitingForSmCatchup = new AtomicBoolean(false);
private AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0);
private boolean mInteractive = false;
private int attempt = 0;
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 OnMessageAcknowledged acknowledgedListener = null;
private SaslMechanism saslMechanism;
private URL redirectionUrl = null;
private String verifiedHostname = null;
private Thread mThread;
private CountDownLatch mStreamCountDownLatch;
public XmppConnection(final Account account, final XmppConnectionService service) { public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account; this.account = account;
@ -479,19 +433,6 @@ public class XmppConnection implements Runnable {
return tag != null && tag.isStart("stream"); 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 { private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
final SSLContext sc = SSLSocketHelper.getSSLContext(); final SSLContext sc = SSLSocketHelper.getSSLContext();
MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager(); MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
@ -836,7 +777,6 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS); tagWriter.writeTag(startTLS);
} }
private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException { private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
tagReader.readTag(); tagReader.readTag();
try { try {
@ -1069,55 +1009,52 @@ public class XmppConnection implements Runnable {
return; return;
} }
clearIqCallbacks(); clearIqCallbacks();
if (account.getJid().isBareJid()) {
account.setResource(this.createNewResource());
}
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
final String resource = Config.USE_RANDOM_RESOURCE_ON_EVERY_BIND ? nextRandomId() : account.getResource(); final String resource = Config.USE_RANDOM_RESOURCE_ON_EVERY_BIND ? nextRandomId() : account.getResource();
iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource); iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() { this.sendUnmodifiedIqPacket(iq, (account, packet) -> {
@Override if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
public void onIqPacketReceived(final Account account, final IqPacket packet) { return;
if (packet.getType() == IqPacket.TYPE.TIMEOUT) { }
return; final Element bind = packet.findChild("bind");
} if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
final Element bind = packet.findChild("bind"); isBound = true;
if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { final Element jid = bind.findChild("jid");
isBound = true; if (jid != null && jid.getContent() != null) {
final Element jid = bind.findChild("jid"); try {
if (jid != null && jid.getContent() != null) { Jid assignedJid = Jid.fromString(jid.getContent());
try { if (!account.getJid().getDomainpart().equals(assignedJid.getDomainpart())) {
Jid assignedJid = Jid.fromString(jid.getContent()); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server tried to re-assign domain to "+assignedJid.getDomainpart());
if (!account.getJid().getDomainpart().equals(assignedJid.getDomainpart())) { throw new StateChangingError(Account.State.BIND_FAILURE);
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");
} }
} else { if (account.setJid(assignedJid)) {
Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)"); 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 { } 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"); } else {
final String resource = account.getResource().split("\\.")[0]; Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
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);
} }
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); },true);
} }
@ -1337,18 +1274,14 @@ public class XmppConnection implements Runnable {
}); });
} }
private void processStreamError(final Tag currentTag) private void processStreamError(final Tag currentTag) throws XmlPullParserException, IOException {
throws XmlPullParserException, IOException {
final Element streamError = tagReader.readElement(currentTag); final Element streamError = tagReader.readElement(currentTag);
if (streamError == null) { if (streamError == null) {
return; return;
} }
if (streamError.hasChild("conflict")) { if (streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0]; account.setResource(createNewResource());
account.setResource(resource + "." + nextRandomId()); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": switching resource due to conflict (" + account.getResource() + ")");
Log.d(Config.LOGTAG,
account.getJid().toBareJid() + ": switching resource due to conflict ("
+ account.getResource() + ")");
throw new IOException(); throw new IOException();
} else if (streamError.hasChild("host-unknown")) { } else if (streamError.hasChild("host-unknown")) {
throw new StateChangingException(Account.State.HOST_UNKNOWN); throw new StateChangingException(Account.State.HOST_UNKNOWN);
@ -1370,8 +1303,16 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(stream); tagWriter.writeTag(stream);
} }
private String createNewResource() {
return mXmppConnectionService.getString(R.string.app_name)+'.'+nextRandomId(true);
}
private String nextRandomId() { 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) { public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
@ -1662,6 +1603,75 @@ public class XmppConnection implements Runnable {
return Identity.UNKNOWN; 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 class StateChangingError extends Error {
private final Account.State state; private final Account.State state;
@ -1678,15 +1688,6 @@ public class XmppConnection implements Runnable {
} }
} }
public enum Identity {
FACEBOOK,
SLACK,
EJABBERD,
PROSODY,
NIMBUZZ,
UNKNOWN
}
public class Features { public class Features {
XmppConnection connection; XmppConnection connection;
private boolean carbonsEnabled = false; private boolean carbonsEnabled = false;
@ -1818,8 +1819,4 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getJid().toBareJid(), Namespace.STANZA_IDS); return hasDiscoFeature(account.getJid().toBareJid(), Namespace.STANZA_IDS);
} }
} }
private IqGenerator getIqGenerator() {
return mXmppConnectionService.getIqGenerator();
}
} }

View file

@ -1,13 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="resources">
<item>Mobile</item>
<item>Phone</item>
<item>Tablet</item>
<item>@string/app_name</item>
<item>Android</item>
</string-array>
<string-array name="themes"> <string-array name="themes">
<item>@string/pref_theme_light</item> <item>@string/pref_theme_light</item>
<item>@string/pref_theme_dark</item> <item>@string/pref_theme_dark</item>

View file

@ -100,8 +100,6 @@
<string name="no_pgp_keys">No OpenPGP Keys found</string> <string name="no_pgp_keys">No OpenPGP Keys found</string>
<string name="contacts_have_no_pgp_keys">Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\n<small>Please ask your contacts to setup OpenPGP.</small></string> <string name="contacts_have_no_pgp_keys">Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\n<small>Please ask your contacts to setup OpenPGP.</small></string>
<string name="pref_general">General</string> <string name="pref_general">General</string>
<string name="pref_xmpp_resource">XMPP resource</string>
<string name="pref_xmpp_resource_summary">The name this client identifies itself with</string>
<string name="pref_accept_files">Accept files</string> <string name="pref_accept_files">Accept files</string>
<string name="pref_accept_files_summary">Automatically accept files smaller than…</string> <string name="pref_accept_files_summary">Automatically accept files smaller than…</string>
<string name="pref_attachments">Attachments</string> <string name="pref_attachments">Attachments</string>

View file

@ -9,14 +9,6 @@
android:key="grant_new_contacts" android:key="grant_new_contacts"
android:summary="@string/pref_grant_presence_updates_summary" android:summary="@string/pref_grant_presence_updates_summary"
android:title="@string/pref_grant_presence_updates"/> android:title="@string/pref_grant_presence_updates"/>
<ListPreference
android:defaultValue="@string/default_resource"
android:entries="@array/resources"
android:entryValues="@array/resources"
android:key="resource"
android:summary="@string/pref_xmpp_resource_summary"
android:title="@string/pref_xmpp_resource"/>
<PreferenceScreen <PreferenceScreen
android:key="huawei" android:key="huawei"
android:title="@string/huawei_protected_apps" android:title="@string/huawei_protected_apps"