File indexing completed on 2025-01-05 04:29:54

0001 /**
0002  * SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
0003  * SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #pragma once
0009 
0010 #include <QObject>
0011 #include <QQmlEngine>
0012 
0013 #include <KFormat>
0014 
0015 #include "error.h"
0016 #include "sync/syncutils.h"
0017 
0018 namespace QKeychain
0019 {
0020 class WritePasswordJob;
0021 class DeletePasswordJob;
0022 }
0023 
0024 class GPodder;
0025 
0026 class Sync : public QObject
0027 {
0028     Q_OBJECT
0029     QML_ELEMENT
0030     QML_SINGLETON
0031 
0032     Q_PROPERTY(bool syncEnabled READ syncEnabled WRITE setSyncEnabled NOTIFY syncEnabledChanged)
0033     Q_PROPERTY(QString username READ username NOTIFY credentialsChanged)
0034     Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY credentialsChanged)
0035     Q_PROPERTY(QString device READ device WRITE setDevice NOTIFY deviceChanged)
0036     Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName NOTIFY deviceNameChanged)
0037     Q_PROPERTY(QString hostname READ hostname WRITE setHostname NOTIFY hostnameChanged)
0038     Q_PROPERTY(SyncUtils::Provider provider READ provider WRITE setProvider NOTIFY providerChanged)
0039 
0040     Q_PROPERTY(QString suggestedDevice READ suggestedDevice CONSTANT)
0041     Q_PROPERTY(QString suggestedDeviceName READ suggestedDeviceName CONSTANT)
0042     Q_PROPERTY(QVector<SyncUtils::Device> deviceList READ deviceList NOTIFY deviceListReceived)
0043 
0044     Q_PROPERTY(SyncUtils::SyncStatus syncStatus MEMBER m_syncStatus NOTIFY syncProgressChanged)
0045     Q_PROPERTY(int syncProgress MEMBER m_syncProgress NOTIFY syncProgressChanged)
0046     Q_PROPERTY(int syncProgressTotal MEMBER m_syncProgressTotal CONSTANT)
0047     Q_PROPERTY(QString syncProgressText MEMBER m_syncProgressText NOTIFY syncProgressChanged)
0048     Q_PROPERTY(QString lastSuccessfulDownloadSync READ lastSuccessfulDownloadSync NOTIFY syncProgressChanged)
0049     Q_PROPERTY(QString lastSuccessfulUploadSync READ lastSuccessfulUploadSync NOTIFY syncProgressChanged)
0050 
0051 public:
0052     static Sync &instance()
0053     {
0054         static Sync _instance;
0055         return _instance;
0056     }
0057     static Sync *create(QQmlEngine *engine, QJSEngine *)
0058     {
0059         engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
0060         return &instance();
0061     }
0062 
0063     bool syncEnabled() const;
0064     QString username() const;
0065     QString password() const;
0066     QString device() const;
0067     QString deviceName() const;
0068     QString hostname() const;
0069     SyncUtils::Provider provider() const;
0070     QString lastSuccessfulSync(const QStringList &matchingLabels = {}) const;
0071     QString lastSuccessfulDownloadSync() const;
0072     QString lastSuccessfulUploadSync() const;
0073 
0074     QString suggestedDevice() const;
0075     QString suggestedDeviceName() const;
0076     QVector<SyncUtils::Device> deviceList() const;
0077 
0078     void setSyncEnabled(bool status);
0079     void setPassword(const QString &password);
0080     void setDevice(const QString &device);
0081     void setDeviceName(const QString &deviceName);
0082     void setHostname(const QString &hostname);
0083     void setProvider(const SyncUtils::Provider provider);
0084 
0085     Q_INVOKABLE void login(const QString &username, const QString &password);
0086     Q_INVOKABLE void retrieveCredentialsFromConfig();
0087     Q_INVOKABLE void logout();
0088     Q_INVOKABLE void registerNewDevice(const QString &id, const QString &caption, const QString &type = QStringLiteral("other"));
0089     Q_INVOKABLE void linkUpAllDevices();
0090 
0091     void doSync(SyncUtils::SyncStatus status, bool forceFetchAll = false); // base method for syncing
0092     Q_INVOKABLE void doRegularSync(bool forceFetchAll = false); // regular sync; can be forced to update all feeds
0093     Q_INVOKABLE void doForceSync(); // force a full re-sync with the server; discarding local eposide acions
0094     Q_INVOKABLE void doSyncPushAll(); // upload all local episode states to the server; this will likely overwrite all actions stored on the server
0095     Q_INVOKABLE void doQuickSync(); // only upload pending local episode actions; intended to be run directly after an episode action has been created
0096 
0097     // Next are some generic methods to store and apply local changes to be synced
0098     void storeAddFeedAction(const QString &url);
0099     void storeRemoveFeedAction(const QString &url);
0100     void storePlayEpisodeAction(const QString &id, const qulonglong started, const qulonglong position);
0101     void storePlayedEpisodeAction(const QString &id);
0102     void applySubscriptionChangesLocally(const QStringList &addList, const QStringList &removeList);
0103     void applyEpisodeActionsLocally(const QHash<QString, QHash<QString, SyncUtils::EpisodeAction>> &episodeActionHash);
0104 
0105 Q_SIGNALS:
0106     void syncEnabledChanged();
0107     void credentialsChanged();
0108     void deviceChanged();
0109     void deviceNameChanged();
0110     void hostnameChanged();
0111     void providerChanged();
0112     void deviceCreated();
0113 
0114     void passwordSaveFinished(bool success);
0115     void passwordRetrievalFinished(QString password);
0116     void passwordInputRequired();
0117 
0118     void loginSucceeded();
0119     void deviceListReceived();
0120 
0121     void error(Error::Type type, const QString &url, const QString &id, const int errorId, const QString &errorString, const QString &title);
0122     void syncProgressChanged();
0123     void abortSync();
0124 
0125 private:
0126     Sync();
0127 
0128     void clearSettings();
0129     void savePasswordToKeyChain(const QString &username, const QString &password);
0130     void savePasswordToFile(const QString &username, const QString &password);
0131     void retrievePasswordFromKeyChain(const QString &username);
0132     QString retrievePasswordFromFile(const QString &username);
0133     void deletePasswordFromKeychain(const QString &username);
0134 
0135     void retrieveAllLocalEpisodeStates();
0136     void onWriteDummyJobFinished(QKeychain::WritePasswordJob *writeDummyJob, const QString &username);
0137     void onWritePasswordJobFinished(QKeychain::WritePasswordJob *job, const QString &username, const QString &password);
0138     void onDeleteJobFinished(QKeychain::DeletePasswordJob *deleteJob, const QString &username);
0139 
0140     GPodder *m_gpodder = nullptr;
0141 
0142     bool m_syncEnabled;
0143     QString m_username;
0144     QString m_password;
0145     QString m_device;
0146     QString m_deviceName;
0147     QString m_hostname;
0148     SyncUtils::Provider m_provider;
0149 
0150     QVector<SyncUtils::Device> m_deviceList;
0151 
0152     KFormat m_kformat;
0153 
0154     // variables needed for linkUpAllDevices()
0155     QStringList m_syncUpAllSubscriptions;
0156     int m_deviceResponses;
0157 
0158     // internal variables used while syncing
0159     bool m_allowSyncActionLogging = true;
0160 
0161     // internal variables used for UI notifications
0162     SyncUtils::SyncStatus m_syncStatus = SyncUtils::SyncStatus::NoSync;
0163     int m_syncProgress = 0;
0164     int m_syncProgressTotal = 7;
0165     QString m_syncProgressText = QLatin1String("");
0166 };