modify XmppConnection to change status to online for unbound cons
This commit is contained in:
parent
a204bf9ec1
commit
35360fde91
|
@ -2,11 +2,11 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "219a451e9a1889222b7549c8b3c0a5b3",
|
||||
"identityHash": "1952101c2c0d439fcd6c9d417f126a54",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `address` TEXT NOT NULL, `resource` TEXT, `randomSeed` BLOB, `enabled` INTEGER NOT NULL, `quickStartAvailable` INTEGER NOT NULL, `pendingRegistration` INTEGER NOT NULL, `loggedInSuccessfully` INTEGER NOT NULL, `showErrorNotification` INTEGER NOT NULL, `rosterVersion` TEXT, `hostname` TEXT, `port` INTEGER, `directTls` INTEGER, `proxytype` TEXT, `proxyhostname` TEXT, `proxyport` INTEGER)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `address` TEXT NOT NULL, `resource` TEXT, `randomSeed` BLOB, `enabled` INTEGER NOT NULL, `quickStartAvailable` INTEGER NOT NULL, `loginAndBind` INTEGER NOT NULL, `showErrorNotification` INTEGER NOT NULL, `rosterVersion` TEXT, `hostname` TEXT, `port` INTEGER, `directTls` INTEGER, `proxytype` TEXT, `proxyhostname` TEXT, `proxyport` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -45,14 +45,8 @@
|
|||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pendingRegistration",
|
||||
"columnName": "pendingRegistration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "loggedInSuccessfully",
|
||||
"columnName": "loggedInSuccessfully",
|
||||
"fieldPath": "loginAndBind",
|
||||
"columnName": "loginAndBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
|
@ -2266,7 +2260,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '219a451e9a1889222b7549c8b3c0a5b3')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1952101c2c0d439fcd6c9d417f126a54')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ public final class Namespace {
|
|||
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
|
||||
public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0";
|
||||
public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
|
||||
public static final String CAPTCHA = "urn:xmpp:captcha";
|
||||
public static final String CARBONS = "urn:xmpp:carbons:2";
|
||||
public static final String CHANNEL_BINDING = "urn:xmpp:sasl-cb:0";
|
||||
public static final String CHAT_MARKERS = "urn:xmpp:chat-markers:0";
|
||||
|
|
|
@ -2,6 +2,7 @@ package im.conversations.android.database.dao;
|
|||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
@ -13,7 +14,7 @@ import java.util.List;
|
|||
@Dao
|
||||
public interface AccountDao {
|
||||
|
||||
@Insert
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
long insert(final AccountEntity account);
|
||||
|
||||
@Query("SELECT id,address,randomSeed FROM account WHERE enabled = 1")
|
||||
|
@ -37,11 +38,8 @@ public interface AccountDao {
|
|||
@Query("SELECT quickStartAvailable FROM account where id=:id")
|
||||
boolean quickStartAvailable(long id);
|
||||
|
||||
@Query("SELECT pendingRegistration FROM account where id=:id")
|
||||
boolean pendingRegistration(long id);
|
||||
|
||||
@Query("SELECT loggedInSuccessfully == 0 FROM account where id=:id")
|
||||
boolean isInitialLogin(long id);
|
||||
@Query("SELECT loginAndBind FROM account where id=:id")
|
||||
boolean loginAndBind(long id);
|
||||
|
||||
@Query(
|
||||
"UPDATE account set quickStartAvailable=:available WHERE id=:id AND"
|
||||
|
@ -49,14 +47,9 @@ public interface AccountDao {
|
|||
void setQuickStartAvailable(long id, boolean available);
|
||||
|
||||
@Query(
|
||||
"UPDATE account set pendingRegistration=:pendingRegistration WHERE id=:id AND"
|
||||
+ " pendingRegistration != :pendingRegistration")
|
||||
void setPendingRegistration(long id, boolean pendingRegistration);
|
||||
|
||||
@Query(
|
||||
"UPDATE account set loggedInSuccessfully=:loggedInSuccessfully WHERE id=:id AND"
|
||||
+ " loggedInSuccessfully != :loggedInSuccessfully")
|
||||
int setLoggedInSuccessfully(long id, boolean loggedInSuccessfully);
|
||||
"UPDATE account set loginAndBind=:loginAndBind WHERE id=:id AND"
|
||||
+ " loginAndBind != :loginAndBind")
|
||||
void setLoginAndBind(long id, boolean loginAndBind);
|
||||
|
||||
@Query(
|
||||
"UPDATE account set showErrorNotification=:showErrorNotification WHERE id=:id AND"
|
||||
|
|
|
@ -28,11 +28,7 @@ public class AccountEntity {
|
|||
public boolean enabled;
|
||||
|
||||
public boolean quickStartAvailable = false;
|
||||
public boolean pendingRegistration = false;
|
||||
|
||||
// TODO this is only used during setup; depending on how the setup procedure will look in the
|
||||
// future we might get rid of this property
|
||||
public boolean loggedInSuccessfully = false;
|
||||
public boolean loginAndBind = true;
|
||||
|
||||
public boolean showErrorNotification = true;
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@ import androidx.annotation.NonNull;
|
|||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.IDs;
|
||||
import im.conversations.android.database.CredentialStore;
|
||||
import im.conversations.android.database.entity.AccountEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.xmpp.ConnectionPool;
|
||||
import im.conversations.android.xmpp.XmppConnection;
|
||||
import im.conversations.android.xmpp.manager.RegistrationManager;
|
||||
|
||||
public class AccountRepository extends AbstractRepository {
|
||||
|
||||
|
@ -18,7 +21,8 @@ public class AccountRepository extends AbstractRepository {
|
|||
super(context);
|
||||
}
|
||||
|
||||
private Account createAccount(@NonNull final Jid address, final String password) {
|
||||
private Account createAccount(
|
||||
@NonNull final Jid address, final String password, final boolean loginAndBind) {
|
||||
Preconditions.checkArgument(
|
||||
address.isBareJid(), "Account should be specified without resource");
|
||||
Preconditions.checkArgument(password != null, "Missing password");
|
||||
|
@ -26,6 +30,7 @@ public class AccountRepository extends AbstractRepository {
|
|||
final var entity = new AccountEntity();
|
||||
entity.address = address;
|
||||
entity.enabled = true;
|
||||
entity.loginAndBind = loginAndBind;
|
||||
entity.randomSeed = randomSeed;
|
||||
final long id = database.accountDao().insert(entity);
|
||||
final var account = new Account(id, address, entity.randomSeed);
|
||||
|
@ -38,8 +43,23 @@ public class AccountRepository extends AbstractRepository {
|
|||
return account;
|
||||
}
|
||||
|
||||
public ListenableFuture<Account> createAccountAsync(
|
||||
final @NonNull Jid address, final String password, final boolean loginAndBind) {
|
||||
return Futures.submit(() -> createAccount(address, password, loginAndBind), IO_EXECUTOR);
|
||||
}
|
||||
|
||||
public ListenableFuture<Account> createAccountAsync(
|
||||
final @NonNull Jid address, final String password) {
|
||||
return Futures.submit(() -> createAccount(address, password), IO_EXECUTOR);
|
||||
return createAccountAsync(address, password, true);
|
||||
}
|
||||
|
||||
public ListenableFuture<RegistrationManager.Registration> getRegistration(
|
||||
final Account account) {
|
||||
final ListenableFuture<XmppConnection> connectedFuture =
|
||||
ConnectionPool.getInstance(context).reconfigure(account).asConnectedFuture();
|
||||
return Futures.transformAsync(
|
||||
connectedFuture,
|
||||
xc -> xc.getManager(RegistrationManager.class).getRegistration(),
|
||||
MoreExecutors.directExecutor());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,10 +118,14 @@ import javax.net.ssl.SSLSocketFactory;
|
|||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import okhttp3.HttpUrl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public class XmppConnection implements Runnable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(XmppConnection.class);
|
||||
|
||||
protected final Account account;
|
||||
private final SparseArray<Stanza> mStanzaQueue = new SparseArray<>();
|
||||
private final Hashtable<String, Pair<Iq, Consumer<Iq>>> packetCallbacks = new Hashtable<>();
|
||||
|
@ -289,9 +293,9 @@ public class XmppConnection implements Runnable {
|
|||
try {
|
||||
Socket localSocket;
|
||||
shouldAuthenticate =
|
||||
!ConversationsDatabase.getInstance(context)
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.accountDao()
|
||||
.pendingRegistration(account.id);
|
||||
.loginAndBind(account.id);
|
||||
this.changeStatus(ConnectionState.CONNECTING);
|
||||
// TODO introduce proxy check
|
||||
final boolean useTor = /*fcontext.useTorToConnect() ||*/ account.isOnion();
|
||||
|
@ -1177,7 +1181,7 @@ public class XmppConnection implements Runnable {
|
|||
final SSLSocket sslSocket = upgradeSocketToTls(socket);
|
||||
tagReader.setInputStream(sslSocket.getInputStream());
|
||||
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
||||
Log.d(Config.LOGTAG, account.address + ": TLS connection established");
|
||||
LOGGER.info("TLS connection established");
|
||||
final boolean quickStart;
|
||||
try {
|
||||
quickStart = establishStream(SSLSockets.version(sslSocket));
|
||||
|
@ -1234,13 +1238,10 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
private void processStreamFeatures(final Tag currentTag) throws IOException {
|
||||
final boolean pendingRegistration =
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.accountDao()
|
||||
.pendingRegistration(account.id);
|
||||
final boolean loginAndBind =
|
||||
ConversationsDatabase.getInstance(context).accountDao().loginAndBind(account.id);
|
||||
this.streamFeatures = tagReader.readElement(currentTag, Features.class);
|
||||
final boolean isSecure = isSecure();
|
||||
final boolean needsBinding = !isBound && !pendingRegistration;
|
||||
final boolean needsBinding = !isBound && loginAndBind;
|
||||
if (this.quickStartInProgress) {
|
||||
if (this.streamFeatures.hasChild("authentication", Namespace.SASL_2)) {
|
||||
Log.d(
|
||||
|
@ -1271,29 +1272,23 @@ public class XmppConnection implements Runnable {
|
|||
throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER);
|
||||
}
|
||||
if (this.streamFeatures.hasChild("starttls", Namespace.TLS) && !this.encryptionEnabled) {
|
||||
LOGGER.info("Negotiating TLS (STARTTLS)");
|
||||
sendStartTLS();
|
||||
} else if (this.streamFeatures.hasChild("register", Namespace.REGISTER_STREAM_FEATURE)
|
||||
&& pendingRegistration) {
|
||||
if (isSecure) {
|
||||
register();
|
||||
} else {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.address
|
||||
+ ": unable to find STARTTLS for registration process "
|
||||
+ XmlHelper.printElementNames(this.streamFeatures));
|
||||
throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER);
|
||||
}
|
||||
} else if (!this.streamFeatures.hasChild("register", Namespace.REGISTER_STREAM_FEATURE)
|
||||
&& pendingRegistration) {
|
||||
throw new StateChangingException(ConnectionState.REGISTRATION_NOT_SUPPORTED);
|
||||
return;
|
||||
} else if (!isSecure()) {
|
||||
LOGGER.error("Server does not support STARTTLS");
|
||||
throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER);
|
||||
}
|
||||
|
||||
if (Boolean.FALSE.equals(loginAndBind)) {
|
||||
LOGGER.info("No login and bind required. Connection is considered online");
|
||||
this.lastPacketReceived = SystemClock.elapsedRealtime();
|
||||
this.changeStatus(ConnectionState.ONLINE);
|
||||
} else if (this.streamFeatures.hasChild("authentication", Namespace.SASL_2)
|
||||
&& shouldAuthenticate
|
||||
&& isSecure) {
|
||||
&& shouldAuthenticate) {
|
||||
authenticate(SaslMechanism.Version.SASL_2);
|
||||
} else if (this.streamFeatures.hasChild("mechanisms", Namespace.SASL)
|
||||
&& shouldAuthenticate
|
||||
&& isSecure) {
|
||||
&& shouldAuthenticate) {
|
||||
authenticate(SaslMechanism.Version.SASL);
|
||||
} else if (this.streamFeatures.streamManagement() && streamId != null && !inSmacksSession) {
|
||||
if (Config.EXTENDED_SM_LOGGING) {
|
||||
|
@ -1306,7 +1301,7 @@ public class XmppConnection implements Runnable {
|
|||
this.mWaitingForSmCatchup.set(true);
|
||||
this.tagWriter.writeStanzaAsync(resume);
|
||||
} else if (needsBinding) {
|
||||
if (this.streamFeatures.hasChild("bind", Namespace.BIND) && isSecure) {
|
||||
if (this.streamFeatures.hasChild("bind", Namespace.BIND)) {
|
||||
sendBindRequest();
|
||||
} else {
|
||||
Log.d(
|
||||
|
@ -1516,7 +1511,7 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
final Iq preAuthRequest = new Iq(Iq.Type.SET);
|
||||
preAuthRequest.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth);
|
||||
sendUnmodifiedIqPacket(
|
||||
sendIqPacketUnbound(
|
||||
preAuthRequest,
|
||||
(response) -> {
|
||||
if (response.getType() == Iq.Type.RESULT) {
|
||||
|
@ -1526,15 +1521,14 @@ public class XmppConnection implements Runnable {
|
|||
Log.d(Config.LOGTAG, account.address + ": failed to pre auth. " + error);
|
||||
throw new StateChangingError(ConnectionState.REGISTRATION_INVALID_TOKEN);
|
||||
}
|
||||
},
|
||||
true);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendRegistryRequest() {
|
||||
final Iq retrieveRegistration = new Iq(Iq.Type.GET);
|
||||
retrieveRegistration.addExtension(new Register());
|
||||
retrieveRegistration.setTo(account.address.getDomain());
|
||||
sendUnmodifiedIqPacket(
|
||||
sendIqPacketUnbound(
|
||||
retrieveRegistration,
|
||||
(packet) -> {
|
||||
if (packet.getType() == Iq.Type.TIMEOUT) {
|
||||
|
@ -1562,8 +1556,7 @@ public class XmppConnection implements Runnable {
|
|||
register.addChild(username);
|
||||
register.addChild(password);
|
||||
registrationRequest.setFrom(account.address);
|
||||
sendUnmodifiedIqPacket(
|
||||
registrationRequest, this::handleRegistrationResponse, true);
|
||||
sendIqPacketUnbound(registrationRequest, this::handleRegistrationResponse);
|
||||
} else if (query.hasChild("x", Namespace.DATA)) {
|
||||
final Data data = Data.parse(query.findChild("x", Namespace.DATA));
|
||||
final Element blob = query.findChild("data", "urn:xmpp:bob");
|
||||
|
@ -1621,15 +1614,14 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
throw new StateChangingError(ConnectionState.REGISTRATION_FAILED);
|
||||
}
|
||||
},
|
||||
true);
|
||||
});
|
||||
}
|
||||
|
||||
private void handleRegistrationResponse(final Iq packet) {
|
||||
if (packet.getType() == Iq.Type.RESULT) {
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.accountDao()
|
||||
.setPendingRegistration(account.id, false);
|
||||
.setLoginAndBind(account.id, true);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.address + ": successfully registered new account on server");
|
||||
|
@ -1693,7 +1685,7 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
final Iq iq = new Iq(Iq.Type.SET);
|
||||
iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
|
||||
this.sendUnmodifiedIqPacket(
|
||||
this.sendIqPacketUnbound(
|
||||
iq,
|
||||
(packet) -> {
|
||||
if (packet.getType() == Iq.Type.TIMEOUT) {
|
||||
|
@ -1759,8 +1751,7 @@ public class XmppConnection implements Runnable {
|
|||
+ ")");
|
||||
}
|
||||
throw new StateChangingError(ConnectionState.BIND_FAILURE);
|
||||
},
|
||||
true);
|
||||
});
|
||||
}
|
||||
|
||||
private void setConnectionAddress(final Jid jid) {
|
||||
|
@ -1813,7 +1804,7 @@ public class XmppConnection implements Runnable {
|
|||
Log.d(Config.LOGTAG, account.address + ": sending legacy session to outdated server");
|
||||
final Iq startSession = new Iq(Iq.Type.SET);
|
||||
startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
|
||||
this.sendUnmodifiedIqPacket(
|
||||
this.sendIqPacketUnbound(
|
||||
startSession,
|
||||
(packet) -> {
|
||||
if (packet.getType() == Iq.Type.RESULT) {
|
||||
|
@ -1822,8 +1813,7 @@ public class XmppConnection implements Runnable {
|
|||
} else if (packet.getType() != Iq.Type.TIMEOUT) {
|
||||
throw new StateChangingError(ConnectionState.SESSION_FAILURE);
|
||||
}
|
||||
},
|
||||
true);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO the return value is not used any more
|
||||
|
@ -2009,6 +1999,14 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
public ListenableFuture<Iq> sendIqPacket(final Iq packet) {
|
||||
return sendIqPacket(packet, false);
|
||||
}
|
||||
|
||||
public ListenableFuture<Iq> sendIqPacketUnbound(final Iq packet) {
|
||||
return sendIqPacket(packet, true);
|
||||
}
|
||||
|
||||
private ListenableFuture<Iq> sendIqPacket(final Iq packet, final boolean sendToUnboundStream) {
|
||||
final SettableFuture<Iq> future = SettableFuture.create();
|
||||
sendIqPacket(
|
||||
packet,
|
||||
|
@ -2021,17 +2019,21 @@ public class XmppConnection implements Runnable {
|
|||
} else {
|
||||
future.setException(new IqErrorException(result));
|
||||
}
|
||||
});
|
||||
},
|
||||
sendToUnboundStream);
|
||||
return future;
|
||||
}
|
||||
|
||||
public String sendIqPacket(final Iq packet, final Consumer<Iq> callback) {
|
||||
packet.setFrom(account.address);
|
||||
return this.sendUnmodifiedIqPacket(packet, callback, false);
|
||||
public void sendIqPacket(final Iq packet, final Consumer<Iq> callback) {
|
||||
this.sendIqPacket(packet, callback, false);
|
||||
}
|
||||
|
||||
public synchronized String sendUnmodifiedIqPacket(
|
||||
final Iq packet, final Consumer<Iq> callback, boolean force) {
|
||||
public void sendIqPacketUnbound(final Iq packet, final Consumer<Iq> callback) {
|
||||
this.sendIqPacket(packet, callback, true);
|
||||
}
|
||||
|
||||
private synchronized void sendIqPacket(
|
||||
final Iq packet, final Consumer<Iq> callback, final boolean sendToUnboundStream) {
|
||||
if (Strings.isNullOrEmpty(packet.getId())) {
|
||||
packet.setId(IDs.medium());
|
||||
}
|
||||
|
@ -2040,8 +2042,7 @@ public class XmppConnection implements Runnable {
|
|||
packetCallbacks.put(packet.getId(), new Pair<>(packet, callback));
|
||||
}
|
||||
}
|
||||
this.sendPacket(packet, force);
|
||||
return packet.getId();
|
||||
this.sendPacket(packet, sendToUnboundStream);
|
||||
}
|
||||
|
||||
public void sendResultFor(final Iq request, final Extension... extensions) {
|
||||
|
@ -2079,14 +2080,15 @@ public class XmppConnection implements Runnable {
|
|||
sendPacket(packet, false);
|
||||
}
|
||||
|
||||
private synchronized void sendPacket(final StreamElement packet, final boolean force) {
|
||||
private synchronized void sendPacket(
|
||||
final StreamElement packet, final boolean sendToUnboundStream) {
|
||||
if (stanzasSent == Integer.MAX_VALUE) {
|
||||
resetStreamId();
|
||||
disconnect(true);
|
||||
return;
|
||||
}
|
||||
synchronized (this.mStanzaQueue) {
|
||||
if (force || isBound) {
|
||||
if (sendToUnboundStream || isBound) {
|
||||
tagWriter.writeStanzaAsync(packet);
|
||||
} else {
|
||||
Log.d(
|
||||
|
@ -2131,11 +2133,17 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
public void sendPing() {
|
||||
if (!r()) {
|
||||
if (this.inSmacksSession) {
|
||||
this.tagWriter.writeStanzaAsync(new Request());
|
||||
} else {
|
||||
final Iq iq = new Iq(Iq.Type.GET);
|
||||
iq.setFrom(account.address);
|
||||
iq.addExtension(new Ping());
|
||||
this.sendIqPacket(iq, null);
|
||||
this.sendIqPacket(
|
||||
iq,
|
||||
response -> {
|
||||
LOGGER.info("Server responded to ping");
|
||||
});
|
||||
}
|
||||
this.lastPingSent = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
@ -2213,15 +2221,6 @@ public class XmppConnection implements Runnable {
|
|||
this.streamId = null;
|
||||
}
|
||||
|
||||
public boolean r() {
|
||||
if (this.inSmacksSession) {
|
||||
this.tagWriter.writeStanzaAsync(new Request());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getTimeToNextAttempt() {
|
||||
final int additionalTime =
|
||||
recentErrorConnectionState == ConnectionState.POLICY_VIOLATION ? 3 : 0;
|
||||
|
|
|
@ -10,16 +10,23 @@ import eu.siacs.conversations.xml.Namespace;
|
|||
import im.conversations.android.xmpp.XmppConnection;
|
||||
import im.conversations.android.xmpp.model.data.Data;
|
||||
import im.conversations.android.xmpp.model.oob.OutOfBandData;
|
||||
import im.conversations.android.xmpp.model.pars.PreAuth;
|
||||
import im.conversations.android.xmpp.model.register.Instructions;
|
||||
import im.conversations.android.xmpp.model.register.Password;
|
||||
import im.conversations.android.xmpp.model.register.Register;
|
||||
import im.conversations.android.xmpp.model.register.Remove;
|
||||
import im.conversations.android.xmpp.model.register.Username;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import okhttp3.HttpUrl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RegistrationManager extends AbstractManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationManager.class);
|
||||
|
||||
public RegistrationManager(Context context, XmppConnection connection) {
|
||||
super(context, connection);
|
||||
}
|
||||
|
@ -27,6 +34,7 @@ public class RegistrationManager extends AbstractManager {
|
|||
public ListenableFuture<Void> setPassword(final String password) {
|
||||
final var account = getAccount();
|
||||
final var iq = new Iq(Iq.Type.SET);
|
||||
iq.setTo(account.address.getDomain());
|
||||
final var register = iq.addExtension(new Register());
|
||||
register.addUsername(account.address.getEscapedLocal());
|
||||
register.addPassword(password);
|
||||
|
@ -36,6 +44,7 @@ public class RegistrationManager extends AbstractManager {
|
|||
|
||||
public ListenableFuture<Void> unregister() {
|
||||
final var iq = new Iq(Iq.Type.SET);
|
||||
iq.setTo(getAccount().address.getDomain());
|
||||
final var register = iq.addExtension(new Register());
|
||||
register.addExtension(new Remove());
|
||||
return Futures.transform(
|
||||
|
@ -43,10 +52,11 @@ public class RegistrationManager extends AbstractManager {
|
|||
}
|
||||
|
||||
public ListenableFuture<Registration> getRegistration() {
|
||||
final var iq = new Iq(Iq.Type.SET);
|
||||
final var iq = new Iq(Iq.Type.GET);
|
||||
iq.setTo(getAccount().address.getDomain());
|
||||
iq.addExtension(new Register());
|
||||
return Futures.transform(
|
||||
connection.sendIqPacket(iq),
|
||||
connection.sendIqPacketUnbound(iq),
|
||||
result -> {
|
||||
final var register = result.getExtension(Register.class);
|
||||
if (register == null) {
|
||||
|
@ -58,7 +68,11 @@ public class RegistrationManager extends AbstractManager {
|
|||
return new SimpleRegistration();
|
||||
}
|
||||
final var data = register.getExtension(Data.class);
|
||||
if (data != null && Namespace.REGISTER.equals(data.getFormType())) {
|
||||
// note that the captcha namespace is incorrect here. That namespace is only
|
||||
// used in message challenges. ejabberd uses the incorrect namespace though
|
||||
if (data != null
|
||||
&& Arrays.asList(Namespace.REGISTER, Namespace.CAPTCHA)
|
||||
.contains(data.getFormType())) {
|
||||
return new ExtendedRegistration(data);
|
||||
}
|
||||
final var oob = register.getExtension(OutOfBandData.class);
|
||||
|
@ -67,14 +81,14 @@ public class RegistrationManager extends AbstractManager {
|
|||
instructions == null ? null : instructions.getContent();
|
||||
final String redirectUrl = oob == null ? null : oob.getURL();
|
||||
if (redirectUrl != null) {
|
||||
return new RedirectRegistration(redirectUrl);
|
||||
return RedirectRegistration.ifValid(redirectUrl);
|
||||
}
|
||||
if (instructionsText != null) {
|
||||
final Matcher matcher = Patterns.WEB_URL.matcher(instructionsText);
|
||||
if (matcher.find()) {
|
||||
final String instructionsUrl =
|
||||
instructionsText.substring(matcher.start(), matcher.end());
|
||||
return new RedirectRegistration(instructionsUrl);
|
||||
return RedirectRegistration.ifValid(instructionsUrl);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No supported registration method found");
|
||||
|
@ -82,7 +96,16 @@ public class RegistrationManager extends AbstractManager {
|
|||
MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
private abstract static class Registration {}
|
||||
public ListenableFuture<Void> sendPreAuthentication(final String token) {
|
||||
final var iq = new Iq(Iq.Type.GET);
|
||||
iq.setTo(getAccount().address.getDomain());
|
||||
final var preAuthentication = iq.addExtension(new PreAuth());
|
||||
preAuthentication.setToken(token);
|
||||
return Futures.transform(
|
||||
connection.sendIqPacketUnbound(iq), result -> null, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
public abstract static class Registration {}
|
||||
|
||||
// only requires Username + Password
|
||||
public static class SimpleRegistration extends Registration {}
|
||||
|
@ -102,14 +125,23 @@ public class RegistrationManager extends AbstractManager {
|
|||
|
||||
// Redirection as show here: https://xmpp.org/extensions/xep-0077.html#redirect
|
||||
public static class RedirectRegistration extends Registration {
|
||||
private final String url;
|
||||
private final HttpUrl url;
|
||||
|
||||
public RedirectRegistration(@NonNull final String url) {
|
||||
private RedirectRegistration(@NonNull HttpUrl url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public @NonNull String getURL() {
|
||||
public @NonNull HttpUrl getURL() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public static RedirectRegistration ifValid(final String url) {
|
||||
final HttpUrl httpUrl = HttpUrl.parse(url);
|
||||
if (httpUrl != null && httpUrl.isHttps()) {
|
||||
return new RedirectRegistration(httpUrl);
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"A URL found the registration instructions is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package im.conversations.android.xmpp.model.pars;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
@XmlElement(namespace = Namespace.PARS)
|
||||
public class PreAuth extends Extension {
|
||||
|
||||
public PreAuth() {
|
||||
super(PreAuth.class);
|
||||
}
|
||||
|
||||
public void setToken(final String token) {
|
||||
this.setAttribute("token", token);
|
||||
}
|
||||
}
|
|
@ -24,14 +24,6 @@ public class BindProcessor extends XmppConnection.Delegate implements Consumer<J
|
|||
final var account = getAccount();
|
||||
final var database = getDatabase();
|
||||
|
||||
final boolean firstLogin =
|
||||
database.accountDao().setLoggedInSuccessfully(account.id, true) > 0;
|
||||
|
||||
if (firstLogin) {
|
||||
// TODO publish display name if this is the first attempt
|
||||
// IIRC this is used when the display name is set from a certificate or something
|
||||
}
|
||||
|
||||
database.presenceDao().deletePresences(account.id);
|
||||
|
||||
getManager(RosterManager.class).fetch();
|
||||
|
|
Loading…
Reference in a new issue