Skip to content

Commit

Permalink
Set stream errors to be ignored during account creation/deletion
Browse files Browse the repository at this point in the history
https://xmpp.org/extensions/xep-0077.html#usecases-cancel specifies the
usage of the stream error <not-authorized/>.
That is now handled as a valid response to the account deletion request
instead of a result IQ stanza or in addition to it.
The same applies to the stream error <conflict> since some servers send
that instead of the <not-authorized/> stream error.

There are servers closing the connection during account creation with a
<connection-timeout/> stream error.
That is now ignored.
  • Loading branch information
melvo committed Sep 8, 2024
1 parent 5e88c16 commit 41bf709
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 7 deletions.
20 changes: 20 additions & 0 deletions src/client/QXmppClient.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2009 Manjeet Dahiya <manjeetdahiya@gmail.com>
// SPDX-FileCopyrightText: 2019 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2023 Melvin Keskin <melvo@olomono.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

Expand Down Expand Up @@ -123,6 +124,13 @@ QStringList QXmppClientPrivate::discoveryFeatures()

void QXmppClientPrivate::onErrorOccurred(const QString &text, const QXmppOutgoingClient::ConnectionError &err, QXmppClient::Error oldError)
{
// Skip stream errors that are valid during special procedures such as account
// creation/deletion.
if (const auto streamError = std::get_if<QXmpp::StreamError>(&err);
streamError && ignoredStreamErrors.contains(*streamError)) {
return;
}

if (q->configuration().autoReconnectionEnabled()) {
if (oldError == QXmppClient::XmppStreamError) {
// if we receive a resource conflict, inhibit reconnection
Expand Down Expand Up @@ -931,6 +939,18 @@ bool QXmppClient::injectMessage(QXmppMessage &&message)
return handled;
}

///
/// Sets stream errors that are ignored if they occur.
///
/// \param errors stream errors to be ignored
///
/// \since QXmpp 1.9
///
void QXmppClient::setIgnoredStreamErrors(const QVector<QXmpp::StreamError> &errors)
{
d->ignoredStreamErrors = errors;
}

///
/// Give extensions a chance to handle incoming stanzas.
///
Expand Down
5 changes: 5 additions & 0 deletions src/client/QXmppClient.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2009 Manjeet Dahiya <manjeetdahiya@gmail.com>
// SPDX-FileCopyrightText: 2019 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2023 Melvin Keskin <melvo@olomono.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

Expand All @@ -11,6 +12,7 @@
#include "QXmppPresence.h"
#include "QXmppSendResult.h"
#include "QXmppSendStanzaParams.h"
#include "QXmppStreamError.h"

#include <memory>
#include <variant>
Expand Down Expand Up @@ -321,6 +323,8 @@ public Q_SLOTS:
void injectIq(const QDomElement &element, const std::optional<QXmppE2eeMetadata> &e2eeMetadata);
bool injectMessage(QXmppMessage &&message);

void setIgnoredStreamErrors(const QVector<QXmpp::StreamError> &);

private Q_SLOTS:
void _q_elementReceived(const QDomElement &element, bool &handled);
void _q_reconnect();
Expand All @@ -335,6 +339,7 @@ private Q_SLOTS:
friend class QXmppCarbonManagerV2;
friend class QXmppRegistrationManager;
friend class TestClient;
friend class QXmppRegistrationManager;
};

#endif // QXMPPCLIENT_H
2 changes: 2 additions & 0 deletions src/client/QXmppClient_p.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2020 Manjeet Dahiya <manjeetdahiya@gmail.com>
// SPDX-FileCopyrightText: 2020 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2023 Melvin Keskin <melvo@olomono.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

Expand Down Expand Up @@ -40,6 +41,7 @@ class QXmppClientPrivate
QXmppLogger *logger;
/// Pointer to the XMPP stream
QXmppOutgoingClient *stream;
QVector<QXmpp::StreamError> ignoredStreamErrors;

QXmppE2eeExtension *encryptionExtension;

Expand Down
16 changes: 15 additions & 1 deletion src/client/QXmppRegistrationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void QXmppRegistrationManager::deleteAccount()
auto iq = QXmppRegisterIq::createUnregistrationRequest();
d->deleteAccountIqId = iq.id();

client()->setIgnoredStreamErrors({ QXmpp::StreamError::Conflict, QXmpp::StreamError::NotAuthorized });
client()->sendPacket(iq);
}

Expand Down Expand Up @@ -174,6 +175,12 @@ bool QXmppRegistrationManager::registerOnConnectEnabled() const
void QXmppRegistrationManager::setRegisterOnConnectEnabled(bool enabled)
{
d->registerOnConnectEnabled = enabled;

if (enabled) {
client()->setIgnoredStreamErrors({ QXmpp::StreamError::ConnectionTimeout });
} else {
client()->setIgnoredStreamErrors({});
}
}

/// \cond
Expand Down Expand Up @@ -291,8 +298,15 @@ void QXmppRegistrationManager::onRegistered(QXmppClient *client)
connect(disco, &QXmppDiscoveryManager::infoReceived, this, &QXmppRegistrationManager::handleDiscoInfo);
}

connect(client, &QXmppClient::disconnected, this, [this]() {
connect(client, &QXmppClient::disconnected, this, [this, client]() {
setSupportedByServer(false);
client->setIgnoredStreamErrors({});

if (!d->deleteAccountIqId.isEmpty()) {
info(QStringLiteral("Account deleted successfully."));
Q_EMIT accountDeleted();
d->deleteAccountIqId.clear();
}
});
}

Expand Down
10 changes: 4 additions & 6 deletions src/client/QXmppRegistrationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,10 @@ class QXmppRegistrationManagerPrivate;
/// <h4>Filling out the registration form</h4>
///
/// Now you need to fill out the registration form. The server can close the connection during that
/// time. If that happens, QXmppClient::errorOccurred() is emitted with
/// QXmpp::StreamError::ConnectionTimeout as its parameter. That is due to some servers kicking
/// unauthorized clients after some time when the clients are inactive. That is often the case when
/// user interaction is required before the completed form is submitted to the server. In order to
/// support account creation for both servers closing the connection and servers keeping it open,
/// you need to handle those cases appropriately.
/// time. It is due to some servers kicking unauthorized clients after some time when the clients
/// are inactive. That is often the case when user interaction is required before the completed form
/// is submitted to the server. In order to account creation for both servers closing the
/// connection and servers keeping it open, you need to handle those cases appropriately.
///
/// If the returned IQ contains a data form, that can be displayed to a user or
/// can be filled out in another way.
Expand Down

0 comments on commit 41bf709

Please sign in to comment.