File indexing completed on 2024-10-13 07:29:32

0001 // SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #include "login.h"
0005 
0006 #include <Quotient/accountregistry.h>
0007 #include <Quotient/connection.h>
0008 #include <Quotient/qt_connection_util.h>
0009 
0010 #include "controller.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 using namespace Quotient;
0015 
0016 LoginHelper::LoginHelper(QObject *parent)
0017     : QObject(parent)
0018 {
0019     init();
0020 }
0021 
0022 void LoginHelper::init()
0023 {
0024     m_homeserverReachable = false;
0025     m_connection = new NeoChatConnection();
0026     m_matrixId = QString();
0027     m_password = QString();
0028     m_deviceName = QStringLiteral("NeoChat %1 %2 %3 %4")
0029                        .arg(QSysInfo::machineHostName(), QSysInfo::productType(), QSysInfo::productVersion(), QSysInfo::currentCpuArchitecture());
0030     m_supportsSso = false;
0031     m_supportsPassword = false;
0032     m_ssoUrl = QUrl();
0033 
0034     connect(this, &LoginHelper::matrixIdChanged, this, [this]() {
0035         setHomeserverReachable(false);
0036         QRegularExpression validator(QStringLiteral("^\\@?[a-zA-Z0-9\\._=\\-/]+\\:[a-zA-Z0-9\\-]+(\\.[a-zA-Z0-9\\-]+)*(\\:[0-9]+)?$"));
0037         if (!validator.match(m_matrixId).hasMatch()) {
0038             return;
0039         }
0040 
0041         if (m_matrixId == QLatin1Char('@')) {
0042             return;
0043         }
0044 
0045         m_isLoggedIn = Controller::instance().accounts().isLoggedIn(m_matrixId);
0046         Q_EMIT isLoggedInChanged();
0047         if (m_isLoggedIn) {
0048             return;
0049         }
0050 
0051         m_testing = true;
0052         Q_EMIT testingChanged();
0053         if (!m_connection) {
0054             m_connection = new NeoChatConnection();
0055         }
0056         m_connection->resolveServer(m_matrixId);
0057         connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
0058             setHomeserverReachable(true);
0059             m_testing = false;
0060             Q_EMIT testingChanged();
0061             m_supportsSso = m_connection->supportsSso();
0062             m_supportsPassword = m_connection->supportsPasswordAuth();
0063             Q_EMIT loginFlowsChanged();
0064         });
0065     });
0066     connect(m_connection, &Connection::connected, this, [this] {
0067         Q_EMIT connected();
0068         m_isLoggingIn = false;
0069         Q_EMIT isLoggingInChanged();
0070         Q_ASSERT(m_connection);
0071         AccountSettings account(m_connection->userId());
0072         account.setKeepLoggedIn(true);
0073         account.setHomeserver(m_connection->homeserver());
0074         account.setDeviceId(m_connection->deviceId());
0075         account.setDeviceName(m_deviceName);
0076         if (!Controller::instance().saveAccessTokenToKeyChain(account, m_connection->accessToken())) {
0077             qWarning() << "Couldn't save access token";
0078         }
0079         account.sync();
0080         Controller::instance().addConnection(m_connection);
0081         Controller::instance().setActiveConnection(m_connection);
0082         m_connection = nullptr;
0083     });
0084     connect(m_connection, &Connection::networkError, this, [this](QString error, const QString &, int, int) {
0085         Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
0086         m_isLoggingIn = false;
0087         Q_EMIT isLoggingInChanged();
0088     });
0089     connect(m_connection, &Connection::loginError, this, [this](QString error, const QString &) {
0090         if (error == QStringLiteral("Invalid username or password")) {
0091             setInvalidPassword(true);
0092         } else {
0093             Q_EMIT errorOccured(i18n("Login Failed: %1", error));
0094         }
0095         m_isLoggingIn = false;
0096         Q_EMIT isLoggingInChanged();
0097     });
0098 
0099     connect(m_connection, &Connection::resolveError, this, [](QString error) {
0100         Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
0101     });
0102 
0103     connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
0104         Q_EMIT loaded();
0105     });
0106 }
0107 
0108 void LoginHelper::setHomeserverReachable(bool reachable)
0109 {
0110     m_homeserverReachable = reachable;
0111     Q_EMIT homeserverReachableChanged();
0112 }
0113 
0114 bool LoginHelper::homeserverReachable() const
0115 {
0116     return m_homeserverReachable;
0117 }
0118 
0119 QString LoginHelper::matrixId() const
0120 {
0121     return m_matrixId;
0122 }
0123 
0124 void LoginHelper::setMatrixId(const QString &matrixId)
0125 {
0126     m_matrixId = matrixId;
0127     if (!m_matrixId.startsWith(QLatin1Char('@'))) {
0128         m_matrixId.prepend(QLatin1Char('@'));
0129     }
0130     Q_EMIT matrixIdChanged();
0131 }
0132 
0133 QString LoginHelper::password() const
0134 {
0135     return m_password;
0136 }
0137 
0138 void LoginHelper::setPassword(const QString &password)
0139 {
0140     setInvalidPassword(false);
0141     m_password = password;
0142     Q_EMIT passwordChanged();
0143 }
0144 
0145 QString LoginHelper::deviceName() const
0146 {
0147     return m_deviceName;
0148 }
0149 
0150 void LoginHelper::setDeviceName(const QString &deviceName)
0151 {
0152     m_deviceName = deviceName;
0153     Q_EMIT deviceNameChanged();
0154 }
0155 
0156 void LoginHelper::login()
0157 {
0158     m_isLoggingIn = true;
0159     Q_EMIT isLoggingInChanged();
0160 
0161     // Some servers do not have a .well_known file. So we login via the username part from the mxid,
0162     // rather than with the full mxid, as that would lead to an invalid user.
0163     auto username = m_matrixId.mid(1, m_matrixId.indexOf(QLatin1Char(':')) - 1);
0164     m_connection->loginWithPassword(username, m_password, m_deviceName, QString());
0165 }
0166 
0167 bool LoginHelper::supportsPassword() const
0168 {
0169     return m_supportsPassword;
0170 }
0171 
0172 bool LoginHelper::supportsSso() const
0173 {
0174     return m_supportsSso;
0175 }
0176 
0177 QUrl LoginHelper::ssoUrl() const
0178 {
0179     return m_ssoUrl;
0180 }
0181 
0182 void LoginHelper::loginWithSso()
0183 {
0184     m_connection->resolveServer(m_matrixId);
0185     connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
0186         SsoSession *session = m_connection->prepareForSso(m_deviceName);
0187         m_ssoUrl = session->ssoUrl();
0188         Q_EMIT ssoUrlChanged();
0189     });
0190 }
0191 
0192 bool LoginHelper::testing() const
0193 {
0194     return m_testing;
0195 }
0196 
0197 bool LoginHelper::isLoggingIn() const
0198 {
0199     return m_isLoggingIn;
0200 }
0201 
0202 bool LoginHelper::isLoggedIn() const
0203 {
0204     return m_isLoggedIn;
0205 }
0206 
0207 void LoginHelper::setInvalidPassword(bool invalid)
0208 {
0209     m_invalidPassword = invalid;
0210     Q_EMIT isInvalidPasswordChanged();
0211 }
0212 
0213 bool LoginHelper::isInvalidPassword() const
0214 {
0215     return m_invalidPassword;
0216 }
0217 
0218 #include "moc_login.cpp"