// SPDX-FileCopyrightText: Nheko Contributors // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include "Config.h" #include "Logging.h" #include "LoginPage.h" #include "MainWindow.h" #include "MatrixClient.h" #include "RegisterPage.h" #include "ui/UIA.h" RegisterPage::RegisterPage(QObject *parent) : QObject(parent) { connect(this, &RegisterPage::registerOk, this, [] { MainWindow::instance()->showChatPage(); }); } void RegisterPage::setError(const QString &err) { registrationError_ = err; emit errorChanged(); registering_ = false; emit registeringChanged(); } void RegisterPage::setHsError(const QString &err) { hsError_ = err; emit hsErrorChanged(); lookingUpHs_ = false; emit lookingUpHsChanged(); } QString RegisterPage::initialDeviceName() const { return QString::fromStdString(LoginPage::initialDeviceName_()); } void RegisterPage::setServer(const QString &server) { if (server == lastServer) return; lastServer = server; http::client()->set_server(server.toStdString()); http::client()->verify_certificates(!UserSettings::instance()->disableCertificateValidation()); hsError_.clear(); emit hsErrorChanged(); supported_ = false; lookingUpHs_ = true; emit lookingUpHsChanged(); http::client()->well_known( [this, prevServer = server](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) { // server changed in between if (lastServer != prevServer) return; if (err) { if (err->status_code == 404) { nhlog::net()->info("Autodiscovery: No .well-known."); // Check that the homeserver can be reached versionsCheck(); return; } if (!err->parse_error.empty()) { setHsError(tr("Autodiscovery failed. Received malformed response.")); nhlog::net()->error("Autodiscovery failed. Received malformed response. {}", err->parse_error); emit hsErrorChanged(); return; } setHsError(tr("Autodiscovery failed. Unknown error when requesting .well-known.")); nhlog::net()->error("Autodiscovery failed. Unknown error when " "requesting .well-known. {}", *err); return; } nhlog::net()->info("Autodiscovery: Discovered '" + res.homeserver.base_url + "'"); http::client()->set_server(res.homeserver.base_url); emit hsErrorChanged(); // Check that the homeserver can be reached versionsCheck(); }); } void RegisterPage::versionsCheck() { // Make a request to /_matrix/client/versions to check the address // given is a Matrix homeserver. http::client()->versions([this](const mtx::responses::Versions &versions, mtx::http::RequestErr err) { if (err) { if (err->status_code == 404) { setHsError( tr("The required endpoints were not found. Possibly not a Matrix server.")); emit hsErrorChanged(); return; } if (!err->parse_error.empty()) { setHsError( tr("Received malformed response. Make sure the homeserver domain is valid.")); emit hsErrorChanged(); return; } setHsError(tr("An unknown error occured. Make sure the homeserver domain is valid.")); emit hsErrorChanged(); return; } if (std::find_if( versions.versions.cbegin(), versions.versions.cend(), [](const std::string &v) { static const std::set> supported{ "v1.1", "v1.2", "v1.3", "v1.4", "v1.5", }; return supported.count(v) != 0; }) == versions.versions.cend()) { emit setHsError( tr("The selected server does not support a version of the Matrix protocol that " "this client understands (v1.1 to v1.5). You can't register.")); emit hsErrorChanged(); return; } http::client()->registration([this](const mtx::responses::Register &, mtx::http::RequestErr e) { nhlog::net()->debug("Registration check: {}", e); if (!e) { setHsError(tr("Server does not support querying registration flows!")); emit hsErrorChanged(); return; } if (e->status_code != 401) { setHsError(tr("Server does not support registration.")); emit hsErrorChanged(); return; } for (const auto &f : e->matrix_error.unauthorized.flows) nhlog::ui()->debug("Registration flows for server: {}", fmt::join(f.stages, ", ")); supported_ = true; lookingUpHs_ = false; emit lookingUpHsChanged(); }); }); } void RegisterPage::checkUsername(const QString &name) { usernameAvailable_ = usernameUnavailable_ = false; usernameError_.clear(); lookingUpUsername_ = true; emit lookingUpUsernameChanged(); http::client()->register_username_available( name.toStdString(), [this](const mtx::responses::Available &available, mtx::http::RequestErr e) { if (e) { if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_INVALID_USERNAME) { usernameError_ = tr("Invalid username."); } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_USER_IN_USE) { usernameError_ = tr("Name already in use."); } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_EXCLUSIVE) { usernameError_ = tr("Part of the reserved namespace."); } else { } usernameAvailable_ = false; usernameUnavailable_ = true; } else { usernameAvailable_ = available.available; usernameUnavailable_ = !available.available; } lookingUpUsername_ = false; emit lookingUpUsernameChanged(); }); } void RegisterPage::startRegistration(const QString &username, const QString &password, const QString &devicename) { // These inputs should still be alright, but check just in case if (!username.isEmpty() && !password.isEmpty() && usernameAvailable_ && supported_) { registrationError_.clear(); emit errorChanged(); registering_ = true; emit registeringChanged(); connect(UIA::instance(), &UIA::error, this, [this](const QString &msg) { setError(msg); disconnect(UIA::instance(), &UIA::error, this, nullptr); }); http::client()->registration( username.toStdString(), password.toStdString(), ::UIA::instance()->genericHandler(QStringLiteral("Registration")), [this](const mtx::responses::Register &res, mtx::http::RequestErr err) { registering_ = false; emit registeringChanged(); if (!err) { http::client()->set_user(res.user_id); http::client()->set_access_token(res.access_token); emit registerOk(); disconnect(UIA::instance(), &UIA::error, this, nullptr); return; } // The server requires registration flows. if (err->status_code == 401 && err->matrix_error.unauthorized.flows.empty()) { nhlog::net()->warn("failed to retrieve registration flows: {}", *err); setError(QString::fromStdString(err->matrix_error.error)); disconnect(UIA::instance(), &UIA::error, this, nullptr); return; } nhlog::net()->error("failed to register: {}", *err); setError(QString::fromStdString(err->matrix_error.error)); disconnect(UIA::instance(), &UIA::error, this, nullptr); }, devicename.isEmpty() ? LoginPage::initialDeviceName_() : devicename.toStdString()); } }