aboutsummaryrefslogtreecommitdiff
path: root/src/client/QXmppRegistrationManager.h
diff options
context:
space:
mode:
authorLinus Jahn <lnj@kaidan.im>2019-06-25 18:57:43 +0200
committerLNJ <lnj@kaidan.im>2020-02-04 13:07:23 +0100
commit1580a6b1582171b7141b072b774faeb387671282 (patch)
tree325eaf173de03f6f551aa1254270412240537dc4 /src/client/QXmppRegistrationManager.h
parent4adbb82142d3830a7d9d71291895d918a7242e79 (diff)
downloadqxmpp-1580a6b1582171b7141b072b774faeb387671282.tar.gz
Add new QXmppRegistrationManager
Diffstat (limited to 'src/client/QXmppRegistrationManager.h')
-rw-r--r--src/client/QXmppRegistrationManager.h361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/client/QXmppRegistrationManager.h b/src/client/QXmppRegistrationManager.h
new file mode 100644
index 00000000..ece72f34
--- /dev/null
+++ b/src/client/QXmppRegistrationManager.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2008-2020 The QXmpp developers
+ *
+ * Author:
+ * Melvin Keskin
+ * Linus Jahn
+ *
+ * Source:
+ * https://github.com/qxmpp-project/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPREGISTRATIONMANAGER_H
+#define QXMPPREGISTRATIONMANAGER_H
+
+#include "QXmppClientExtension.h"
+#include "QXmppRegisterIq.h"
+
+#include <QScopedPointer>
+
+class QXmppRegistrationManagerPrivate;
+
+///
+/// \brief The QXmppRegistrationManager class manages in-band registration and
+/// account management tasks like changing the password as defined in
+/// \xep{0077}: In-Band Registration.
+///
+/// <h3 id="activation">Activating the manager</h3>
+///
+/// To make use of this manager, you need to instantiate it and load it into
+/// the QXmppClient instance as follows:
+///
+/// \code
+/// auto *registrationManager = new QXmppRegistrationManager;
+/// client->addExtension(registrationManager);
+/// \endcode
+///
+/// <h3>Setting up service discovery correctly for this manager</h3>
+///
+/// This manager automatically recognizes whether the local server supports
+/// \xep{0077} (see supportedByServer()). You just need to request the service
+/// discovery information from the server on connect as below:
+///
+/// \code
+/// connect(client, &QXmppClient::connected, [=]() {
+/// // The service discovery manager is added to the client by default.
+/// auto *discoManager = client->findExtension<QXmppDiscoveryManager>();
+/// discoManager->requestInfo(client->configuration().server());
+/// });
+/// \endcode
+///
+/// As soon as the result is retrieved, the supportedByServer() property should
+/// be correct and could be used to display the user whether account management
+/// tasks can be performed on this server.
+///
+/// However, this is not relevant if you only want to
+/// <a href="#register-account">register a new account on a server</a>.
+///
+/// <h3>Changing the account's password</h3>
+///
+/// To change the password of the current account changePassword() can be used.
+/// Upon that either passwordChanged() or passwordChangeFailed() is emitted.
+///
+/// If changing the password was successful, the new password is automatically
+/// set in the QXmppClient::configuration(), so reconnecting works properly.
+///
+/// Example:
+/// \code
+/// auto *registrationManager = client->findExtension<QXmppRegistrationManager>();
+/// connect(registrationManager, &QXmppRegistrationManager::passwordChanged, [=](const QString &newPassword) {
+/// qDebug() << "Password changed to:" << newPassword;
+/// });
+/// connect(registrationManager, &QXmppRegistrationManager::passwordChangeFailed, [=](const QXmppStanza::Error &error) {
+/// qDebug() << "Couldn't change the password:" << error.text();
+/// });
+///
+/// registrationManager->changePassword(client->configuration().user(), "m1cr0$0ft");
+/// \endcode
+///
+/// <h3>Unregistration with the server</h3>
+///
+/// If you want to delete your account on the server, you can do that using
+/// deleteAccount(). The result of the IQ request is not handled. If you want
+/// to do that manually, you can use the returned ID.
+///
+/// \code
+/// auto *registrationManager = client->findExtension<QXmppRegistrationManager>();
+/// QString deleteId = registrationManager->deleteAccount();
+/// if (deleteId.isEmpty()) {
+/// // stanza could not be sent
+/// } else {
+/// // stanza was sent, you can handle the result using deleteId
+/// }
+/// \endcode
+///
+/// <h3 id="register-account">Registering with a server</h3>
+///
+/// Registering with a server consists of multiple steps:
+/// -# Requesting the registration form from the server
+/// -# Filling out the registration form
+/// -# Sending the completed form to the server
+/// - On failure (e.g. because of a username conflict), the process
+/// continues at step 1 again.
+/// -# Connecting with the newly created account
+///
+/// <h4>Requesting the registration form from the server</h4>
+///
+/// First of all, you need to enable the registration process in the
+/// registration manager, which of course needs to be <a href="#activation">
+/// activated</a> in the client.
+///
+/// \code
+/// auto *registrationManager = client->findExtension<QXmppRegistrationManager>();
+/// registrationManager->setRegisterOnConnectEnabled(true);
+/// \endcode
+///
+/// After that you can start to connect to the server you want to register
+/// with. No JID is set in the QXmppConfiguration for the client and instead
+/// only the server is set.
+///
+/// \code
+/// QXmppConfiguration config;
+/// config.setDomain("example.org");
+///
+/// client->connectToServer(config);
+/// \endcode
+///
+/// Alternatively, you can also provide a domain-only JID and no password to
+/// connectToServer():
+///
+/// \code
+/// client->connectToServer("example.org", QString());
+/// \endcode
+///
+/// Now as soon as (START)TLS was handled, the registration manager interrupts
+/// the normal connection process. The manager checks whether the server
+/// supports in-band registration and whether the server advertises this as a
+/// stream feature.
+///
+/// If the server does not support in-band registration, the manager will abort
+/// the connection at this point and emit the registrationFailed() signal with
+/// a fixed QXmppStanza::Error of type QXmppStanza::Error::Cancel and with a
+/// condition of QXmppStanza::Error::FeatureNotImplemented.
+///
+/// The manager will now request the registration form. This will either result
+/// in an error (reported by registrationFailed()) or, if everything went well,
+/// the registration form is reported by registrationFormReceived().
+///
+/// To handle everything correctly, you need to connect to both signals:
+///
+/// \code
+/// connect(registrationManager, &QXmppRegistrationManager::registrationFormReceived, [=](const QXmppRegisterIq &iq) {
+/// qDebug() << "Form received:" << iq.instructions().
+/// // you now need to complete the form
+/// });
+/// connect(registrationManager, &QXmppRegistrationManager::registrationFailed, [=](const QXmppStanza::Error &error) {
+/// qDebug() << "Requesting the registration form failed:" << error.text();
+/// });
+/// \endcode
+///
+/// <h4>Filling out the registration form</h4>
+///
+/// Now you need to fill out the registration form. If this requires user
+/// interaction, it is recommended that you disconnect from the server at this
+/// point now. This is required, because some servers will kick inactive, not
+/// authorized clients after a few seconds.
+///
+/// If the returned IQ contains a data form, that can be displayed to a user or
+/// can be filled out in another way.
+///
+/// If the server does not support data forms, you can check the standard
+/// fields of the QXmppRegisterIq. You need to search the fields for empty
+/// (non-null) strings. All fields that contain an empty string are required
+/// and can be filled out. You can just set values for those fields and send
+/// the form as described in the next step.
+///
+/// \note QXmpp currently has only implemented the most important default
+/// fields in the QXmppRegisterIq. The other fields are not very widespread,
+/// because data forms are usually used for such purposes.
+///
+/// <h4>Sending the completed form to the server</h4>
+///
+/// <b>Option A</b>: If filling out the form goes very quick, you can set the
+/// filled out form directly using setRegistrationFormToSend() and then
+/// directly trigger the form to be sent using sendCachedRegistrationForm().
+///
+/// \code
+/// registrationManager->setRegistrationFormToSend(completedForm);
+/// registrationManager->sendCachedRegistrationForm();
+/// \endcode
+///
+/// <b>Option B</b>: If filling out the form takes longer, i.e. because user
+/// interaction is required, you should disconnect now. As soon as you have
+/// completed the form, you can set it using setRegistrationFormToSend(). After
+/// that you can reconnect to the server and the registration manager will
+/// automatically send the set form.
+///
+/// \code
+/// client->disconnectFromServer();
+/// // user fills out form ...
+/// registrationManager->setRegistrationFormToSend(completedForm);
+///
+/// // As before, you only need to provide a domain to connectToServer()
+/// client->connectToServer(...);
+/// // the registration manager sends the form automatically
+/// \endcode
+///
+/// The form is now sent to the server. As soon as the result is received,
+/// either registrationSucceeded() or registrationFailed() is emitted.
+///
+/// In case there was a conflict or another error, you should request a new
+/// form and restart the process. This is especially important, if the form can
+/// only be used once as with most CAPTCHA implementations.
+///
+/// <h4>Connecting with the newly created account</h4>
+///
+/// You need to disconnect now. The user can then enter their credentials and
+/// connect as usually.
+///
+/// It is also possible to extract username and password from the sent form,
+/// but that does not work always. There might also be forms that have no clear
+/// username or password fields.
+///
+/// \ingroup Managers
+///
+/// \since QXmpp 1.2
+///
+class QXMPP_EXPORT QXmppRegistrationManager : public QXmppClientExtension
+{
+ Q_OBJECT
+
+ /// Whether support of \xep{0077}: In-band Registration has been discovered on the server.
+ Q_PROPERTY(bool supportedByServer READ supportedByServer NOTIFY supportedByServerChanged)
+
+public:
+ QXmppRegistrationManager();
+ ~QXmppRegistrationManager();
+
+ QStringList discoveryFeatures() const override;
+
+ void changePassword(const QString &newPassword);
+ QString deleteAccount();
+
+ // documentation needs to be here, see https://stackoverflow.com/questions/49192523/
+ ///
+ /// Returns whether the server supports registration.
+ ///
+ /// By default this is set to false and only changes, if you request the
+ /// service discovery info of the connected server using
+ /// QXmppDiscoveryManager::requestInfo().
+ ///
+ /// This is only relevant to actions that happen after authentication.
+ ///
+ /// \sa QXmppRegistrationManager::supportedByServerChanged()
+ ///
+ bool supportedByServer() const;
+
+ void requestRegistrationForm(const QString &service = {});
+
+ void setRegistrationFormToSend(const QXmppRegisterIq &iq);
+ void setRegistrationFormToSend(const QXmppDataForm &dataForm);
+ void sendCachedRegistrationForm();
+
+ bool registerOnConnectEnabled() const;
+ void setRegisterOnConnectEnabled(bool enabled);
+
+ /// \cond
+ bool handleStanza(const QDomElement &stanza) override;
+ /// \endcond
+
+signals:
+ ///
+ /// Emitted, when registrationSupported() changed.
+ ///
+ /// This can happen after the service discovery info of the server was
+ /// retrieved using QXmppDiscoveryManager::requestInfo() or on disconnect.
+ ///
+ void supportedByServerChanged();
+
+ ///
+ /// Emitted, when the password of the account was changed successfully.
+ ///
+ /// The new password is automatically set in QXmppClient::configuration().
+ ///
+ /// \param newPassword The new password that was set on the server.
+ ///
+ void passwordChanged(const QString &newPassword);
+
+ ///
+ /// Emitted, when changing the password did not succeed.
+ ///
+ /// \param error Error returned from the service.
+ ///
+ void passwordChangeFailed(const QXmppStanza::Error &error);
+
+ ///
+ /// Emitted, when a registration form has been received.
+ ///
+ /// When registering an account on the server and user interaction is
+ /// required now to complete the form, it is recommended to disconnect and
+ /// sending the completed registration form on reconnect using
+ /// QXmppRegistrationManager::setRegistrationFormToSend(). Some servers
+ /// (i.e. ejabberd) kick their clients after a timeout when they are not
+ /// active. This can be avoided this way.
+ ///
+ /// \param iq The received form. If it does not contain a valid data form
+ /// (see QXmppRegisterIq::form()), the required fields should be marked by
+ /// empty (but not null) strings in the QXmppRegisterIq (i.e.
+ /// QXmppRegisterIq::password().isNull() => false).
+ ///
+ void registrationFormReceived(const QXmppRegisterIq &iq);
+
+ ///
+ /// Emitted, when the registration with a service completed successfully.
+ ///
+ /// To connect with the account you still need to set the correct
+ /// credentials in QXmppClient::configuration() and reconnect.
+ ///
+ void registrationSucceeded();
+
+ ///
+ /// Emitted, when the registration failed.
+ ///
+ /// \param error The returned error from the service. The reported errors
+ /// might be different from server to server, but the common ones are the
+ /// following:
+ /// \li type=Cancel and condition=Conflict: The username already exists.
+ /// \li type=Cancel and condition=NotAllowed: The CAPTCHA verification
+ /// failed.
+ /// \li type=Modify and condition=NotAcceptable: Some required information
+ /// was missing or the selected password was too weak.
+ /// \li type=Modify and condition=JidMalformed: No username was provided or
+ /// the username has an illegal format.
+ ///
+ void registrationFailed(const QXmppStanza::Error &error);
+
+protected:
+ void setClient(QXmppClient *client) override;
+
+private slots:
+ void handleDiscoInfo(const QXmppDiscoveryIq &iq);
+
+private:
+ void setSupportedByServer(bool supportedByServer);
+
+ QScopedPointer<QXmppRegistrationManagerPrivate> d;
+};
+
+#endif // QXMPPREGISTRATIONMANAGER_H