File indexing completed on 2024-04-14 15:03:03

0001 // SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-License-Identifier: GPL-3.0-only
0003 
0004 #pragma once
0005 
0006 #include "models/pushrulemodel.h"
0007 #include <QObject>
0008 #include <QQuickItem>
0009 
0010 #include <KFormat>
0011 
0012 #include <Quotient/accountregistry.h>
0013 #include <Quotient/jobs/basejob.h>
0014 #include <Quotient/settings.h>
0015 
0016 class NeoChatRoom;
0017 class NeoChatUser;
0018 class TrayIcon;
0019 class QWindow;
0020 class QQuickTextDocument;
0021 
0022 namespace Quotient
0023 {
0024 class Connection;
0025 class Room;
0026 }
0027 
0028 namespace QKeychain
0029 {
0030 class ReadPasswordJob;
0031 }
0032 
0033 /**
0034  * @class Controller
0035  *
0036  * A singleton class designed to help manage the application.
0037  *
0038  * There are also a bunch of helper functions that currently don't fit anywhere
0039  * else.
0040  */
0041 class Controller : public QObject
0042 {
0043     Q_OBJECT
0044 
0045     /**
0046      * @brief The number of logged in accounts.
0047      */
0048     Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged)
0049 
0050     /**
0051      * @brief The current connection for the rest of NeoChat to use.
0052      */
0053     Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
0054 
0055     /**
0056      * @brief The PushRuleModel that has the active connection's push rules.
0057      */
0058     Q_PROPERTY(PushRuleModel *pushRuleModel READ pushRuleModel CONSTANT)
0059 
0060     /**
0061      * @brief The row number in the accounts directory of the active connection.
0062      */
0063     Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged)
0064 
0065     /**
0066      * @brief The account label for the active account.
0067      *
0068      * Account labels are a concept specific to NeoChat, allowing accounts to be
0069      * labelled, e.g. for "Work", "Private", etc.
0070      *
0071      * Set to an empty string to remove the label.
0072      */
0073     Q_PROPERTY(QString activeAccountLabel READ activeAccountLabel WRITE setActiveAccountLabel NOTIFY activeAccountLabelChanged)
0074 
0075     /**
0076      * @brief Whether the OS NeoChat is running on supports sytem tray icons.
0077      */
0078     Q_PROPERTY(bool supportSystemTray READ supportSystemTray CONSTANT)
0079 
0080     /**
0081      * @brief Whether KWindowSystem specific features are available.
0082      */
0083     Q_PROPERTY(bool hasWindowSystem READ hasWindowSystem CONSTANT)
0084 
0085     /**
0086      * @brief Whether NeoChat is currently able to connect to the server.
0087      */
0088     Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged)
0089 
0090     /**
0091      * @brief Whether the ecryption support has been enabled.
0092      */
0093     Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT)
0094 
0095     /**
0096      * @brief The current minor version number of libQuotient being used.
0097      *
0098      * This is the only way to gate NeoChat features by libQuotient version in QML.
0099      *
0100      * @note No major version because libQuotient doesn't have any; All are 0.x.
0101      */
0102     Q_PROPERTY(int quotientMinorVersion READ quotientMinorVersion CONSTANT)
0103 
0104     /**
0105      * @brief Whether NeoChat is running as a flatpak.
0106      *
0107      * This is the only way to gate NeoChat features in flatpaks in QML.
0108      */
0109     Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
0110 
0111 public:
0112     /**
0113      * @brief Defines the status after an attempt to change the password on an account.
0114      */
0115     enum PasswordStatus {
0116         Success, /**< The password was successfully changed. */
0117         Wrong, /**< The current password entered was wrong. */
0118         Other, /**< An unknown problem occurred. */
0119     };
0120     Q_ENUM(PasswordStatus)
0121 
0122     static Controller &instance();
0123 
0124     [[nodiscard]] int accountCount() const;
0125 
0126     void setActiveConnection(Quotient::Connection *connection);
0127     [[nodiscard]] Quotient::Connection *activeConnection() const;
0128 
0129     [[nodiscard]] PushRuleModel *pushRuleModel() const;
0130 
0131     /**
0132      * @brief Add a new connection to the account registry.
0133      */
0134     void addConnection(Quotient::Connection *c);
0135 
0136     /**
0137      * @brief Drop a connection from the account registry.
0138      */
0139     void dropConnection(Quotient::Connection *c);
0140 
0141     int activeConnectionIndex() const;
0142 
0143     [[nodiscard]] QString activeAccountLabel() const;
0144     void setActiveAccountLabel(const QString &label);
0145 
0146     /**
0147      * @brief Save an access token to the keychain for the given account.
0148      */
0149     bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
0150 
0151     /**
0152      * @brief Change the password for an account.
0153      *
0154      * The function emits a passwordStatus signal with a PasswordStatus value when
0155      * complete.
0156      *
0157      * @sa PasswordStatus, passwordStatus
0158      */
0159     Q_INVOKABLE void changePassword(Quotient::Connection *connection, const QString &currentPassword, const QString &newPassword);
0160 
0161     /**
0162      * @brief Change the avatar for an account.
0163      */
0164     Q_INVOKABLE bool setAvatar(Quotient::Connection *connection, const QUrl &avatarSource);
0165 
0166     /**
0167      * @brief Create new room for a group chat.
0168      */
0169     Q_INVOKABLE void createRoom(const QString &name, const QString &topic);
0170 
0171     /**
0172      * @brief Create new space.
0173      */
0174     Q_INVOKABLE void createSpace(const QString &name, const QString &topic);
0175 
0176     /**
0177      * @brief Join a room.
0178      */
0179     Q_INVOKABLE void joinRoom(const QString &alias);
0180 
0181     /**
0182      * @brief Join a direct chat with the given user.
0183      *
0184      * If a direct chat with the user doesn't exist one is created and then joined.
0185      */
0186     Q_INVOKABLE void openOrCreateDirectChat(NeoChatUser *user);
0187 
0188     [[nodiscard]] bool supportSystemTray() const;
0189 
0190     /**
0191      * @brief Set the background blur status of the given item.
0192      */
0193     Q_INVOKABLE void setBlur(QQuickItem *item, bool blur);
0194 
0195     bool isOnline() const;
0196 
0197     bool encryptionSupported() const;
0198 
0199     /**
0200      * @brief Sets the QNetworkProxy for the application.
0201      *
0202      * @sa QNetworkProxy::setApplicationProxy
0203      */
0204     Q_INVOKABLE void setApplicationProxy();
0205 
0206     int quotientMinorVersion() const;
0207 
0208     bool isFlatpak() const;
0209 
0210     /**
0211      * @brief Return a string for the input timestamp.
0212      *
0213      * The output format depends on the KFormat::DurationFormatOptions chosen.
0214      *
0215      * @sa KFormat::DurationFormatOptions
0216      */
0217     Q_INVOKABLE QString formatDuration(quint64 msecs, KFormat::DurationFormatOptions options = KFormat::DefaultDuration) const;
0218 
0219     /**
0220      * @brief Return a human readable string for a given input number of bytes.
0221      */
0222     Q_INVOKABLE QString formatByteSize(double size, int precision = 1) const;
0223 
0224     /**
0225      * @brief Force a QQuickTextDocument to refresh when images are loaded.
0226      *
0227      * HACK: This is a workaround for QTBUG 93281.
0228      */
0229     Q_INVOKABLE void forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item);
0230 
0231     Q_INVOKABLE QVariantList getSupportedRoomVersions(Quotient::Connection *connection);
0232 
0233     Quotient::AccountRegistry &accounts();
0234 
0235 private:
0236     explicit Controller(QObject *parent = nullptr);
0237 
0238     QPointer<Quotient::Connection> m_connection;
0239     TrayIcon *m_trayIcon = nullptr;
0240 
0241     QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
0242 
0243     void loadSettings();
0244     void saveSettings() const;
0245     bool m_isOnline = true;
0246     QMap<Quotient::Room *, int> m_notificationCounts;
0247 
0248     bool hasWindowSystem() const;
0249 
0250     QPointer<PushRuleModel> m_pushRuleModel;
0251     Quotient::AccountRegistry m_accountRegistry;
0252 
0253 private Q_SLOTS:
0254     void invokeLogin();
0255     void showWindow();
0256     void setQuitOnLastWindowClosed();
0257 
0258 Q_SIGNALS:
0259     /// Error occurred because of user inputs
0260     void errorOccured(const QString &error);
0261 
0262     /// Error occurred because of server or bug in NeoChat
0263     void globalErrorOccured(QString error, QString detail);
0264     void syncDone();
0265     void connectionAdded(Quotient::Connection *_t1);
0266     void connectionDropped(Quotient::Connection *_t1);
0267     void accountCountChanged();
0268     void initiated();
0269     void notificationClicked(const QString &_t1, const QString &_t2);
0270     void quitOnLastWindowClosedChanged();
0271     void unreadCountChanged();
0272     void activeConnectionChanged();
0273     void passwordStatus(Controller::PasswordStatus _t1);
0274     void userConsentRequired(QUrl url);
0275     void testConnectionResult(const QString &connection, bool usable);
0276     void isOnlineChanged(bool isOnline);
0277     void keyVerificationRequest(int timeLeft, Quotient::Connection *connection, const QString &transactionId, const QString &deviceId);
0278     void keyVerificationStart();
0279     void keyVerificationAccept(const QString &commitment);
0280     void keyVerificationKey(const QString &sas);
0281     void activeConnectionIndexChanged();
0282     void roomAdded(NeoChatRoom *room);
0283     void activeAccountLabelChanged();
0284 
0285 public Q_SLOTS:
0286     void logout(Quotient::Connection *conn, bool serverSideLogout);
0287     void changeAvatar(Quotient::Connection *conn, const QUrl &localFile);
0288     static void markAllMessagesAsRead(Quotient::Connection *conn);
0289     void saveWindowGeometry();
0290 };
0291 
0292 // TODO libQuotient 0.7: Drop
0293 class NeochatChangePasswordJob : public Quotient::BaseJob
0294 {
0295 public:
0296     explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
0297 };
0298 
0299 class NeochatDeleteDeviceJob : public Quotient::BaseJob
0300 {
0301 public:
0302     explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
0303 };