summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-02-23 11:42:29 +0100
committerNicolas Werner <nicolas.werner@hotmail.de>2020-02-23 13:44:05 +0100
commit3ef0d9db3cb1fe84c3c905fe7fbd80c3bbb51fd4 (patch)
tree99988bb1803817b1e60899a3bf78072bf4b067a0
parentMerge pull request #128 from adasauce/no-setstylesheet (diff)
downloadnheko-3ef0d9db3cb1fe84c3c905fe7fbd80c3bbb51fd4.tar.xz
Fix Registration
fixes #97
fixes #51
-rw-r--r--.travis.yml1
-rw-r--r--CMakeLists.txt20
-rw-r--r--io.github.NhekoReborn.Nheko.json4
-rw-r--r--src/MainWindow.cpp30
-rw-r--r--src/MainWindow.h31
-rw-r--r--src/RegisterPage.cpp220
-rw-r--r--src/RegisterPage.h7
-rw-r--r--src/dialogs/FallbackAuth.cpp69
-rw-r--r--src/dialogs/FallbackAuth.h26
-rw-r--r--src/dialogs/ReCaptcha.cpp5
-rw-r--r--src/dialogs/ReCaptcha.h1
11 files changed, 316 insertions, 98 deletions
diff --git a/.travis.yml b/.travis.yml
index bea561f1..ac3512bd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,6 +33,7 @@ matrix:
                 - ninja
                 - openssl
                 - qt5
+              update: true # workaround for broken travis homebrew
         - os: linux
           compiler: gcc-7
           env:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eebac250..66e9dcd2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -228,17 +228,18 @@ configure_file(cmake/nheko.h config/nheko.h)
 set(SRC_FILES
 	# Dialogs
 	src/dialogs/CreateRoom.cpp
+	src/dialogs/FallbackAuth.cpp
 	src/dialogs/ImageOverlay.cpp
-	src/dialogs/PreviewUploadOverlay.cpp
 	src/dialogs/InviteUsers.cpp
 	src/dialogs/JoinRoom.cpp
-	src/dialogs/MemberList.cpp
 	src/dialogs/LeaveRoom.cpp
 	src/dialogs/Logout.cpp
-	src/dialogs/UserProfile.cpp
-	src/dialogs/ReadReceipts.cpp
+	src/dialogs/MemberList.cpp
+	src/dialogs/PreviewUploadOverlay.cpp
 	src/dialogs/ReCaptcha.cpp
+	src/dialogs/ReadReceipts.cpp
 	src/dialogs/RoomSettings.cpp
+	src/dialogs/UserProfile.cpp
 
 	# Emoji
 	src/emoji/Category.cpp
@@ -333,7 +334,7 @@ if(USE_BUNDLED_MTXCLIENT)
 	FetchContent_Declare(
 		MatrixClient
 		GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
-		GIT_TAG        5fbee216e640da45c05f25b1f84f03c88bca5910
+		GIT_TAG        5838f607d0e4c7595439249e8b9c213aec0667e9
 		)
 	FetchContent_MakeAvailable(MatrixClient)
 else()
@@ -424,18 +425,19 @@ feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAG
 qt5_wrap_cpp(MOC_HEADERS
 	# Dialogs
 	src/dialogs/CreateRoom.h
+	src/dialogs/FallbackAuth.h
 	src/dialogs/ImageOverlay.h
-	src/dialogs/PreviewUploadOverlay.h
 	src/dialogs/InviteUsers.h
 	src/dialogs/JoinRoom.h
-	src/dialogs/MemberList.h
 	src/dialogs/LeaveRoom.h
 	src/dialogs/Logout.h
-	src/dialogs/UserProfile.h
+	src/dialogs/MemberList.h
+	src/dialogs/PreviewUploadOverlay.h
 	src/dialogs/RawMessage.h
-	src/dialogs/ReadReceipts.h
 	src/dialogs/ReCaptcha.h
+	src/dialogs/ReadReceipts.h
 	src/dialogs/RoomSettings.h
+	src/dialogs/UserProfile.h
 
 	# Emoji
 	src/emoji/Category.h
diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json
index 45007e86..ddc1f1a0 100644
--- a/io.github.NhekoReborn.Nheko.json
+++ b/io.github.NhekoReborn.Nheko.json
@@ -147,9 +147,9 @@
       "name": "mtxclient",
       "sources": [
         {
-          "sha256": "8cf5470570d2ed6affc0bbe0f4b6be9b0a2e2372e9f920b504126841bb73036f",
+          "sha256": "df3fe7e3d59b5fc52ee3ca9a132a55fc325aa799c676e9e420073c56daeb1848",
           "type": "archive",
-          "url": "https://github.com/Nheko-Reborn/mtxclient/archive/5fbee216e640da45c05f25b1f84f03c88bca5910.tar.gz"
+          "url": "https://github.com/Nheko-Reborn/mtxclient/archive/5838f607d0e4c7595439249e8b9c213aec0667e9.tar.gz"
         }
       ]
     },
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 17a04a41..fb64f0fe 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -507,3 +507,33 @@ MainWindow::loadJdenticonPlugin()
         nhlog::ui()->info("jdenticon plugin not found.");
         return false;
 }
+void
+MainWindow::showWelcomePage()
+{
+        removeOverlayProgressBar();
+        pageStack_->addWidget(welcome_page_);
+        pageStack_->setCurrentWidget(welcome_page_);
+}
+
+void
+MainWindow::showLoginPage()
+{
+        if (modal_)
+                modal_->hide();
+
+        pageStack_->addWidget(login_page_);
+        pageStack_->setCurrentWidget(login_page_);
+}
+
+void
+MainWindow::showRegisterPage()
+{
+        pageStack_->addWidget(register_page_);
+        pageStack_->setCurrentWidget(register_page_);
+}
+
+void
+MainWindow::showUserSettingsPage()
+{
+        pageStack_->setCurrentWidget(userSettingsPage_);
+}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 59f29d50..e3e04698 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -24,16 +24,17 @@
 #include <QStackedWidget>
 #include <QSystemTrayIcon>
 
-#include "LoginPage.h"
-#include "RegisterPage.h"
 #include "UserSettingsPage.h"
-#include "WelcomePage.h"
 #include "dialogs/UserProfile.h"
 #include "ui/OverlayModal.h"
 
 #include "jdenticoninterface.h"
 
 class ChatPage;
+class RegisterPage;
+class LoginPage;
+class WelcomePage;
+
 class LoadingIndicator;
 class OverlayModal;
 class SnackBar;
@@ -97,32 +98,16 @@ private slots:
         void iconActivated(QSystemTrayIcon::ActivationReason reason);
 
         //! Show the welcome page in the main window.
-        void showWelcomePage()
-        {
-                removeOverlayProgressBar();
-                pageStack_->addWidget(welcome_page_);
-                pageStack_->setCurrentWidget(welcome_page_);
-        }
+        void showWelcomePage();
 
         //! Show the login page in the main window.
-        void showLoginPage()
-        {
-                if (modal_)
-                        modal_->hide();
-
-                pageStack_->addWidget(login_page_);
-                pageStack_->setCurrentWidget(login_page_);
-        }
+        void showLoginPage();
 
         //! Show the register page in the main window.
-        void showRegisterPage()
-        {
-                pageStack_->addWidget(register_page_);
-                pageStack_->setCurrentWidget(register_page_);
-        }
+        void showRegisterPage();
 
         //! Show user settings page.
-        void showUserSettingsPage() { pageStack_->setCurrentWidget(userSettingsPage_); }
+        void showUserSettingsPage();
 
         //! Show the chat page and start communicating with the given access token.
         void showChatPage();
diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp
index 2688e9a9..39a69a34 100644
--- a/src/RegisterPage.cpp
+++ b/src/RegisterPage.cpp
@@ -15,6 +15,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <QMetaType>
 #include <QPainter>
 #include <QStyleOption>
 #include <QTimer>
@@ -30,11 +31,17 @@
 #include "ui/RaisedButton.h"
 #include "ui/TextField.h"
 
+#include "dialogs/FallbackAuth.h"
 #include "dialogs/ReCaptcha.h"
 
+Q_DECLARE_METATYPE(mtx::user_interactive::Unauthorized)
+Q_DECLARE_METATYPE(mtx::user_interactive::Auth)
+
 RegisterPage::RegisterPage(QWidget *parent)
   : QWidget(parent)
 {
+        qRegisterMetaType<mtx::user_interactive::Unauthorized>();
+        qRegisterMetaType<mtx::user_interactive::Auth>();
         top_layout_ = new QVBoxLayout();
 
         back_layout_ = new QHBoxLayout();
@@ -133,46 +140,139 @@ RegisterPage::RegisterPage(QWidget *parent)
           this,
           &RegisterPage::registrationFlow,
           this,
-          [this](const std::string &user, const std::string &pass, const std::string &session) {
-                  emit errorOccurred();
-
-                  auto captchaDialog =
-                    new dialogs::ReCaptcha(QString::fromStdString(session), this);
-
-                  connect(captchaDialog,
-                          &dialogs::ReCaptcha::confirmation,
-                          this,
-                          [this, user, pass, session, captchaDialog]() {
-                                  captchaDialog->close();
-                                  captchaDialog->deleteLater();
-
-                                  emit registering();
-
-                                  http::client()->flow_response(
-                                    user,
-                                    pass,
-                                    session,
-                                    "m.login.recaptcha",
-                                    [this](const mtx::responses::Register &res,
-                                           mtx::http::RequestErr err) {
-                                            if (err) {
-                                                    nhlog::net()->warn(
-                                                      "failed to retrieve registration flows: {}",
-                                                      err->matrix_error.error);
-                                                    emit errorOccurred();
-                                                    emit registerErrorCb(QString::fromStdString(
-                                                      err->matrix_error.error));
-                                                    return;
-                                            }
-
-                                            http::client()->set_user(res.user_id);
-                                            http::client()->set_access_token(res.access_token);
-
-                                            emit registerOk();
-                                    });
-                          });
-
-                  QTimer::singleShot(1000, this, [captchaDialog]() { captchaDialog->show(); });
+          [this](const std::string &user,
+                 const std::string &pass,
+                 const mtx::user_interactive::Unauthorized &unauthorized) {
+                  auto completed_stages = unauthorized.completed;
+                  auto flows            = unauthorized.flows;
+                  auto session          = unauthorized.session;
+
+                  nhlog::ui()->info("Completed stages: {}", completed_stages.size());
+
+                  if (!completed_stages.empty())
+                          flows.erase(std::remove_if(
+                                        flows.begin(),
+                                        flows.end(),
+                                        [completed_stages](auto flow) {
+                                                if (completed_stages.size() > flow.stages.size())
+                                                        return true;
+                                                for (size_t f = 0; f < completed_stages.size(); f++)
+                                                        if (completed_stages[f] != flow.stages[f])
+                                                                return true;
+                                                return false;
+                                        }),
+                                      flows.end());
+
+                  if (flows.empty()) {
+                          nhlog::net()->error("No available registration flows!");
+                          emit registerErrorCb(tr("No supported registration flows!"));
+                          return;
+                  }
+
+                  auto current_stage = flows.front().stages.at(completed_stages.size());
+
+                  if (current_stage == mtx::user_interactive::auth_types::recaptcha) {
+                          auto captchaDialog =
+                            new dialogs::ReCaptcha(QString::fromStdString(session), this);
+
+                          connect(captchaDialog,
+                                  &dialogs::ReCaptcha::confirmation,
+                                  this,
+                                  [this, user, pass, session, captchaDialog]() {
+                                          captchaDialog->close();
+                                          captchaDialog->deleteLater();
+
+                                          emit registerAuth(
+                                            user,
+                                            pass,
+                                            mtx::user_interactive::Auth{
+                                              session, mtx::user_interactive::auth::Fallback{}});
+                                  });
+                          connect(captchaDialog,
+                                  &dialogs::ReCaptcha::cancel,
+                                  this,
+                                  &RegisterPage::errorOccurred);
+
+                          QTimer::singleShot(
+                            1000, this, [captchaDialog]() { captchaDialog->show(); });
+                  } else if (current_stage == mtx::user_interactive::auth_types::dummy) {
+                          emit registerAuth(user,
+                                            pass,
+                                            mtx::user_interactive::Auth{
+                                              session, mtx::user_interactive::auth::Dummy{}});
+                  } else {
+                          // use fallback
+                          auto dialog =
+                            new dialogs::FallbackAuth(QString::fromStdString(current_stage),
+                                                      QString::fromStdString(session),
+                                                      this);
+
+                          connect(dialog,
+                                  &dialogs::FallbackAuth::confirmation,
+                                  this,
+                                  [this, user, pass, session, dialog]() {
+                                          dialog->close();
+                                          dialog->deleteLater();
+
+                                          emit registerAuth(
+                                            user,
+                                            pass,
+                                            mtx::user_interactive::Auth{
+                                              session, mtx::user_interactive::auth::Fallback{}});
+                                  });
+                          connect(dialog,
+                                  &dialogs::FallbackAuth::cancel,
+                                  this,
+                                  &RegisterPage::errorOccurred);
+
+                          dialog->show();
+                  }
+          });
+
+        connect(
+          this,
+          &RegisterPage::registerAuth,
+          this,
+          [this](const std::string &user,
+                 const std::string &pass,
+                 const mtx::user_interactive::Auth &auth) {
+                  http::client()->registration(
+                    user,
+                    pass,
+                    auth,
+                    [this, user, pass](const mtx::responses::Register &res,
+                                       mtx::http::RequestErr err) {
+                            if (!err) {
+                                    http::client()->set_user(res.user_id);
+                                    http::client()->set_access_token(res.access_token);
+
+                                    emit registerOk();
+                                    return;
+                            }
+
+                            // The server requires registration flows.
+                            if (err->status_code == boost::beast::http::status::unauthorized) {
+                                    if (err->matrix_error.unauthorized.session.empty()) {
+                                            nhlog::net()->warn(
+                                              "failed to retrieve registration flows: ({}) "
+                                              "{}",
+                                              static_cast<int>(err->status_code),
+                                              err->matrix_error.error);
+                                            emit registerErrorCb(
+                                              QString::fromStdString(err->matrix_error.error));
+                                            return;
+                                    }
+
+                                    emit registrationFlow(
+                                      user, pass, err->matrix_error.unauthorized);
+                                    return;
+                            }
+
+                            nhlog::net()->warn("failed to register: status_code ({})",
+                                               static_cast<int>(err->status_code));
+
+                            emit registerErrorCb(QString::fromStdString(err->matrix_error.error));
+                    });
           });
 
         setLayout(top_layout_);
@@ -225,31 +325,27 @@ RegisterPage::onRegisterButtonClicked()
 
                           // The server requires registration flows.
                           if (err->status_code == boost::beast::http::status::unauthorized) {
-                                  http::client()->flow_register(
-                                    username,
-                                    password,
-                                    [this, username, password](
-                                      const mtx::responses::RegistrationFlows &res,
-                                      mtx::http::RequestErr err) {
-                                            if (res.session.empty() && err) {
-                                                    nhlog::net()->warn(
-                                                      "failed to retrieve registration flows: ({}) "
-                                                      "{}",
-                                                      static_cast<int>(err->status_code),
-                                                      err->matrix_error.error);
-                                                    emit errorOccurred();
-                                                    emit registerErrorCb(QString::fromStdString(
-                                                      err->matrix_error.error));
-                                                    return;
-                                            }
-
-                                            emit registrationFlow(username, password, res.session);
-                                    });
+                                  if (err->matrix_error.unauthorized.session.empty()) {
+                                          nhlog::net()->warn(
+                                            "failed to retrieve registration flows: ({}) "
+                                            "{}",
+                                            static_cast<int>(err->status_code),
+                                            err->matrix_error.error);
+                                          emit errorOccurred();
+                                          emit registerErrorCb(
+                                            QString::fromStdString(err->matrix_error.error));
+                                          return;
+                                  }
+
+                                  emit registrationFlow(
+                                    username, password, err->matrix_error.unauthorized);
                                   return;
                           }
 
-                          nhlog::net()->warn("failed to register: status_code ({})",
-                                             static_cast<int>(err->status_code));
+                          nhlog::net()->warn(
+                            "failed to register: status_code ({}), matrix_error({})",
+                            static_cast<int>(err->status_code),
+                            err->matrix_error.error);
 
                           emit registerErrorCb(QString::fromStdString(err->matrix_error.error));
                           emit errorOccurred();
diff --git a/src/RegisterPage.h b/src/RegisterPage.h
index 96a5b3ca..ebc24bb1 100644
--- a/src/RegisterPage.h
+++ b/src/RegisterPage.h
@@ -21,6 +21,8 @@
 #include <QLayout>
 #include <memory>
 
+#include <mtx/user_interactive.hpp>
+
 class FlatButton;
 class RaisedButton;
 class TextField;
@@ -43,7 +45,10 @@ signals:
         void registerErrorCb(const QString &msg);
         void registrationFlow(const std::string &user,
                               const std::string &pass,
-                              const std::string &session);
+                              const mtx::user_interactive::Unauthorized &unauthorized);
+        void registerAuth(const std::string &user,
+                          const std::string &pass,
+                          const mtx::user_interactive::Auth &auth);
 
 private slots:
         void onBackButtonClicked();
diff --git a/src/dialogs/FallbackAuth.cpp b/src/dialogs/FallbackAuth.cpp
new file mode 100644
index 00000000..a0633c1e
--- /dev/null
+++ b/src/dialogs/FallbackAuth.cpp
@@ -0,0 +1,69 @@
+#include <QDesktopServices>
+#include <QLabel>
+#include <QPushButton>
+#include <QUrl>
+#include <QVBoxLayout>
+
+#include "dialogs/FallbackAuth.h"
+
+#include "Config.h"
+#include "MatrixClient.h"
+
+using namespace dialogs;
+
+FallbackAuth::FallbackAuth(const QString &authType, const QString &session, QWidget *parent)
+  : QWidget(parent)
+{
+        setAutoFillBackground(true);
+        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
+        setWindowModality(Qt::WindowModal);
+        setAttribute(Qt::WA_DeleteOnClose, true);
+
+        auto layout = new QVBoxLayout(this);
+        layout->setSpacing(conf::modals::WIDGET_SPACING);
+        layout->setMargin(conf::modals::WIDGET_MARGIN);
+
+        auto buttonLayout = new QHBoxLayout();
+        buttonLayout->setSpacing(8);
+        buttonLayout->setMargin(0);
+
+        openBtn_    = new QPushButton(tr("Open Fallback in Browser"), this);
+        cancelBtn_  = new QPushButton(tr("Cancel"), this);
+        confirmBtn_ = new QPushButton(tr("Confirm"), this);
+        confirmBtn_->setDefault(true);
+
+        buttonLayout->addStretch(1);
+        buttonLayout->addWidget(openBtn_);
+        buttonLayout->addWidget(cancelBtn_);
+        buttonLayout->addWidget(confirmBtn_);
+
+        QFont font;
+        font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
+
+        auto label = new QLabel(
+          tr("Open the fallback, follow the steps and confirm after completing them."), this);
+        label->setFont(font);
+
+        layout->addWidget(label);
+        layout->addLayout(buttonLayout);
+
+        connect(openBtn_, &QPushButton::clicked, [session, authType]() {
+                const auto url = QString("https://%1:%2/_matrix/client/r0/auth/%4/"
+                                         "fallback/web?session=%3")
+                                   .arg(QString::fromStdString(http::client()->server()))
+                                   .arg(http::client()->port())
+                                   .arg(session)
+                                   .arg(authType);
+
+                QDesktopServices::openUrl(url);
+        });
+
+        connect(confirmBtn_, &QPushButton::clicked, this, [this]() {
+                emit confirmation();
+                emit close();
+        });
+        connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
+                emit cancel();
+                emit close();
+        });
+}
diff --git a/src/dialogs/FallbackAuth.h b/src/dialogs/FallbackAuth.h
new file mode 100644
index 00000000..245fa03e
--- /dev/null
+++ b/src/dialogs/FallbackAuth.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <QWidget>
+
+class QPushButton;
+class QLabel;
+
+namespace dialogs {
+
+class FallbackAuth : public QWidget
+{
+        Q_OBJECT
+
+public:
+        FallbackAuth(const QString &authType, const QString &session, QWidget *parent = nullptr);
+
+signals:
+        void confirmation();
+        void cancel();
+
+private:
+        QPushButton *openBtn_;
+        QPushButton *confirmBtn_;
+        QPushButton *cancelBtn_;
+};
+} // dialogs
diff --git a/src/dialogs/ReCaptcha.cpp b/src/dialogs/ReCaptcha.cpp
index 7849aa4f..21dc8c77 100644
--- a/src/dialogs/ReCaptcha.cpp
+++ b/src/dialogs/ReCaptcha.cpp
@@ -60,5 +60,8 @@ ReCaptcha::ReCaptcha(const QString &session, QWidget *parent)
                 emit confirmation();
                 emit close();
         });
-        connect(cancelBtn_, &QPushButton::clicked, this, &dialogs::ReCaptcha::close);
+        connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
+                emit cancel();
+                emit close();
+        });
 }
diff --git a/src/dialogs/ReCaptcha.h b/src/dialogs/ReCaptcha.h
index f8407640..88ff3722 100644
--- a/src/dialogs/ReCaptcha.h
+++ b/src/dialogs/ReCaptcha.h
@@ -15,6 +15,7 @@ public:
 
 signals:
         void confirmation();
+        void cancel();
 
 private:
         QPushButton *openCaptchaBtn_;