mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
Prevent issues when changing the enum.
Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
parent
8e38739d94
commit
4b0e1b57eb
@ -4,7 +4,6 @@
|
||||
set(common_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/checksums.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/checksumcalculator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/clientstatusreportingrecord.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/filesystembase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ownsql.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/preparedsqlquerymanager.cpp
|
||||
|
||||
@ -121,10 +121,10 @@ set(client_SRCS
|
||||
navigationpanehelper.cpp
|
||||
networksettings.h
|
||||
networksettings.cpp
|
||||
"${CMAKE_SOURCE_DIR}/src/libsync/ocsjob.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/libsync/ocsjob.cpp"
|
||||
ocsnavigationappsjob.h
|
||||
ocsnavigationappsjob.cpp
|
||||
ocsjob.h
|
||||
ocsjob.cpp
|
||||
ocssharejob.h
|
||||
ocssharejob.cpp
|
||||
ocsshareejob.h
|
||||
|
||||
@ -148,13 +148,11 @@ signals:
|
||||
private slots:
|
||||
bool finished() override;
|
||||
|
||||
protected:
|
||||
QByteArray _verb;
|
||||
QNetworkRequest _request;
|
||||
|
||||
private:
|
||||
QVector<int> _passStatusCodes;
|
||||
QByteArray _verb;
|
||||
QHash<QString, QString> _params;
|
||||
QVector<int> _passStatusCodes;
|
||||
QNetworkRequest _request;
|
||||
};
|
||||
}
|
||||
|
||||
@ -24,7 +24,10 @@ set(libsync_SRCS
|
||||
capabilities.cpp
|
||||
clientproxy.h
|
||||
clientproxy.cpp
|
||||
clientstatusreporting.h
|
||||
clientstatusreporting.cpp
|
||||
clientstatusreportingrecord.h
|
||||
clientstatusreportingrecord.cpp
|
||||
cookiejar.h
|
||||
cookiejar.cpp
|
||||
discovery.h
|
||||
@ -54,10 +57,6 @@ set(libsync_SRCS
|
||||
owncloudpropagator.cpp
|
||||
nextcloudtheme.h
|
||||
nextcloudtheme.cpp
|
||||
ocsjob.h
|
||||
ocsjob.cpp
|
||||
ocsclientstatusreportingjob.h
|
||||
ocsclientstatusreportingjob.cpp
|
||||
abstractpropagateremotedeleteencrypted.h
|
||||
abstractpropagateremotedeleteencrypted.cpp
|
||||
deletejob.h
|
||||
|
||||
@ -64,8 +64,7 @@ constexpr int checksumRecalculateRequestServerVersionMinSupportedMajor = 24;
|
||||
constexpr auto isSkipE2eeMetadataChecksumValidationAllowedInClientVersion = MIRALL_VERSION_MAJOR == 3 && MIRALL_VERSION_MINOR == 8;
|
||||
}
|
||||
|
||||
namespace OCC
|
||||
{
|
||||
namespace OCC {
|
||||
Q_LOGGING_CATEGORY(lcAccount, "nextcloud.sync.account", QtInfoMsg)
|
||||
const char app_password[] = "_app-password";
|
||||
|
||||
@ -88,7 +87,7 @@ AccountPtr Account::create()
|
||||
return acc;
|
||||
}
|
||||
|
||||
ClientSideEncryption *Account::e2e()
|
||||
ClientSideEncryption* Account::e2e()
|
||||
{
|
||||
// Qt expects everything in the connect to be a pointer, so return a pointer.
|
||||
return &_e2e;
|
||||
@ -268,10 +267,14 @@ void Account::setCredentials(AbstractCredentials *cred)
|
||||
if (proxy.type() != QNetworkProxy::DefaultProxy) {
|
||||
_am->setProxy(proxy);
|
||||
}
|
||||
connect(_am.data(), &QNetworkAccessManager::sslErrors, this, &Account::slotHandleSslErrors);
|
||||
connect(_am.data(), &QNetworkAccessManager::proxyAuthenticationRequired, this, &Account::proxyAuthenticationRequired);
|
||||
connect(_credentials.data(), &AbstractCredentials::fetched, this, &Account::slotCredentialsFetched);
|
||||
connect(_credentials.data(), &AbstractCredentials::asked, this, &Account::slotCredentialsAsked);
|
||||
connect(_am.data(), &QNetworkAccessManager::sslErrors,
|
||||
this, &Account::slotHandleSslErrors);
|
||||
connect(_am.data(), &QNetworkAccessManager::proxyAuthenticationRequired,
|
||||
this, &Account::proxyAuthenticationRequired);
|
||||
connect(_credentials.data(), &AbstractCredentials::fetched,
|
||||
this, &Account::slotCredentialsFetched);
|
||||
connect(_credentials.data(), &AbstractCredentials::asked,
|
||||
this, &Account::slotCredentialsAsked);
|
||||
|
||||
trySetupPushNotifications();
|
||||
}
|
||||
|
||||
@ -12,16 +12,17 @@
|
||||
* for more details.
|
||||
*/
|
||||
#include "clientstatusreporting.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include "account.h"
|
||||
#include "common/clientstatusreportingrecord.h"
|
||||
#include "common/syncjournaldb.h"
|
||||
#include "clientstatusreportingrecord.h"
|
||||
#include <configfile.h>
|
||||
#include "common/c_jhash.h"
|
||||
#include <networkjobs.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto lastSentReportTimestamp = "lastClientStatusReportSentTime";
|
||||
constexpr auto statusNamesHash = "statusNamesHash";
|
||||
}
|
||||
|
||||
namespace OCC
|
||||
@ -29,8 +30,8 @@ namespace OCC
|
||||
Q_LOGGING_CATEGORY(lcClientStatusReporting, "nextcloud.sync.clientstatusreporting", QtInfoMsg)
|
||||
|
||||
ClientStatusReporting::ClientStatusReporting(Account *account, QObject *parent)
|
||||
: _account(account)
|
||||
, QObject(parent)
|
||||
: QObject(parent)
|
||||
, _account(account)
|
||||
{
|
||||
init();
|
||||
}
|
||||
@ -44,18 +45,18 @@ ClientStatusReporting::~ClientStatusReporting()
|
||||
|
||||
void ClientStatusReporting::init()
|
||||
{
|
||||
Q_ASSERT(!_isInitialized);
|
||||
if (_isInitialized) {
|
||||
qCDebug(lcClientStatusReporting) << "Double call to init";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ClientStatusReporting::Count; ++i) {
|
||||
for (int i = 0; i < ClientStatusReporting::Status::Count; ++i) {
|
||||
const auto statusString = statusStringFromNumber(static_cast<Status>(i));
|
||||
_statusNamesAndHashes[i] = {statusString, SyncJournalDb::getPHash(statusString)};
|
||||
_statusNamesAndHashes[i] = {statusString, c_jhash64((uint8_t *)statusString.data(), statusString.size(), 0)};
|
||||
}
|
||||
|
||||
const auto dbPath = makeDbPath();
|
||||
|
||||
_database = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
|
||||
_database.setDatabaseName(dbPath);
|
||||
|
||||
@ -65,23 +66,42 @@ void ClientStatusReporting::init()
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare(
|
||||
const auto prepareResult = query.prepare(QStringLiteral(
|
||||
"CREATE TABLE IF NOT EXISTS clientstatusreporting("
|
||||
"nHash INTEGER(8) PRIMARY KEY,"
|
||||
"name VARCHAR(4096) PRIMARY KEY,"
|
||||
"status INTEGER(8),"
|
||||
"name VARCHAR(4096),"
|
||||
"count INTEGER,"
|
||||
"lastOccurrence INTEGER(8))");
|
||||
"lastOccurrence INTEGER(8))"));
|
||||
if (!prepareResult || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not setup client clientstatusreporting table:" << query.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!query.prepare("CREATE TABLE IF NOT EXISTS keyvalue(key VARCHAR(4096), value VARCHAR(4096), PRIMARY KEY(key))") || !query.exec()) {
|
||||
if (!query.prepare(QStringLiteral("CREATE INDEX IF NOT EXISTS name ON clientstatusreporting(name);")) || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not create index on clientstatusreporting table:" << query.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!query.prepare(QStringLiteral("CREATE TABLE IF NOT EXISTS keyvalue(key VARCHAR(4096), value VARCHAR(4096), PRIMARY KEY(key))")) || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not setup client keyvalue table:" << query.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent issues in case enum gets changed in future, hash its value and clean the db in case there was a change
|
||||
QByteArray statusNamesContatenated;
|
||||
for (int i = 0; i < ClientStatusReporting::Status::Count; ++i) {
|
||||
statusNamesContatenated += statusStringFromNumber(static_cast<Status>(i));
|
||||
}
|
||||
statusNamesContatenated += QByteArray::number(ClientStatusReporting::Status::Count);
|
||||
const auto statusNamesHashCurrent = QCryptographicHash::hash(statusNamesContatenated, QCryptographicHash::Md5).toHex();
|
||||
const auto statusNamesHashFromDb = getStatusNamesHash();
|
||||
|
||||
if (statusNamesHashCurrent != statusNamesHashFromDb) {
|
||||
deleteClientStatusReportingRecords();
|
||||
setStatusNamesHash(statusNamesHashCurrent);
|
||||
}
|
||||
//
|
||||
|
||||
_clientStatusReportingSendTimer.setInterval(clientStatusReportingTrySendTimerInterval);
|
||||
connect(&_clientStatusReportingSendTimer, &QTimer::timeout, this, &ClientStatusReporting::sendReportToServer);
|
||||
_clientStatusReportingSendTimer.start();
|
||||
@ -96,40 +116,31 @@ QVector<ClientStatusReportingRecord> ClientStatusReporting::getClientStatusRepor
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare("SELECT * FROM clientstatusreporting");
|
||||
|
||||
if (!prepareResult || !query.exec()) {
|
||||
const auto errorMessage = query.lastError().text();
|
||||
qCDebug(lcClientStatusReporting) << "Could not get records from clientstatusreporting:" << errorMessage;
|
||||
if (!query.prepare(QStringLiteral("SELECT * FROM clientstatusreporting")) || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not get records from clientstatusreporting:" << query.lastError().text();
|
||||
return records;
|
||||
}
|
||||
|
||||
while (query.next()) {
|
||||
ClientStatusReportingRecord record;
|
||||
record._nameHash = query.value(query.record().indexOf("nHash")).toLongLong();
|
||||
record._status = query.value(query.record().indexOf("status")).toLongLong();
|
||||
record._name = query.value(query.record().indexOf("name")).toByteArray();
|
||||
record._numOccurences = query.value(query.record().indexOf("count")).toLongLong();
|
||||
record._lastOccurence = query.value(query.record().indexOf("lastOccurrence")).toLongLong();
|
||||
record._status = query.value(query.record().indexOf(QStringLiteral("status"))).toLongLong();
|
||||
record._name = query.value(query.record().indexOf(QStringLiteral("name"))).toByteArray();
|
||||
record._numOccurences = query.value(query.record().indexOf(QStringLiteral("count"))).toLongLong();
|
||||
record._lastOccurence = query.value(query.record().indexOf(QStringLiteral("lastOccurrence"))).toLongLong();
|
||||
records.push_back(record);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
bool ClientStatusReporting::deleteClientStatusReportingRecords()
|
||||
void ClientStatusReporting::deleteClientStatusReportingRecords() const
|
||||
{
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare("DELETE FROM clientstatusreporting");
|
||||
|
||||
if (!prepareResult || !query.exec()) {
|
||||
const auto errorMessage = query.lastError().text();
|
||||
qCDebug(lcClientStatusReporting) << "Could not get records from clientstatusreporting:" << errorMessage;
|
||||
return false;
|
||||
if (!query.prepare(QStringLiteral("DELETE FROM clientstatusreporting")) || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not delete records from clientstatusreporting:" << query.lastError().text();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<void, QString> ClientStatusReporting::setClientStatusReportingRecord(const ClientStatusReportingRecord &record)
|
||||
Result<void, QString> ClientStatusReporting::setClientStatusReportingRecord(const ClientStatusReportingRecord &record) const
|
||||
{
|
||||
Q_ASSERT(record.isValid());
|
||||
if (!record.isValid()) {
|
||||
@ -144,13 +155,12 @@ Result<void, QString> ClientStatusReporting::setClientStatusReportingRecord(cons
|
||||
QSqlQuery query;
|
||||
|
||||
const auto prepareResult = query.prepare(
|
||||
"INSERT OR REPLACE INTO clientstatusreporting (nHash, name, status, count, lastOccurrence) VALUES(:nHash, :name, :status, :count, :lastOccurrence) ON CONFLICT(nHash) "
|
||||
"DO UPDATE SET count = count + 1, lastOccurrence = :lastOccurrence;");
|
||||
query.bindValue(":nHash", recordCopy._nameHash);
|
||||
query.bindValue(":name", recordCopy._name);
|
||||
query.bindValue(":status", recordCopy._status);
|
||||
query.bindValue(":count", 1);
|
||||
query.bindValue(":lastOccurrence", recordCopy._lastOccurence);
|
||||
QStringLiteral("INSERT OR REPLACE INTO clientstatusreporting (name, status, count, lastOccurrence) VALUES(:name, :status, :count, :lastOccurrence) ON CONFLICT(name) "
|
||||
"DO UPDATE SET count = count + 1, lastOccurrence = :lastOccurrence;"));
|
||||
query.bindValue(QStringLiteral(":name"), recordCopy._name);
|
||||
query.bindValue(QStringLiteral(":status"), recordCopy._status);
|
||||
query.bindValue(QStringLiteral(":count"), 1);
|
||||
query.bindValue(QStringLiteral(":lastOccurrence"), recordCopy._lastOccurence);
|
||||
|
||||
if (!prepareResult || !query.exec()) {
|
||||
const auto errorMessage = query.lastError().text();
|
||||
@ -161,10 +171,10 @@ Result<void, QString> ClientStatusReporting::setClientStatusReportingRecord(cons
|
||||
return {};
|
||||
}
|
||||
|
||||
void ClientStatusReporting::reportClientStatus(const Status status)
|
||||
void ClientStatusReporting::reportClientStatus(const Status status) const
|
||||
{
|
||||
if (!_isInitialized) {
|
||||
qCWarning(lcClientStatusReporting) << "Could not report status. Status reporting is not initialized";
|
||||
qCDebug(lcClientStatusReporting) << "Could not report status. Status reporting is not initialized";
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(status >= 0 && status < Count);
|
||||
@ -176,7 +186,6 @@ void ClientStatusReporting::reportClientStatus(const Status status)
|
||||
ClientStatusReportingRecord record;
|
||||
record._name = _statusNamesAndHashes[status].first;
|
||||
record._status = status;
|
||||
record._nameHash = _statusNamesAndHashes[status].second;
|
||||
record._lastOccurence = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch();
|
||||
const auto result = setClientStatusReportingRecord(record);
|
||||
if (!result.isValid()) {
|
||||
@ -207,8 +216,8 @@ void ClientStatusReporting::sendReportToServer()
|
||||
clientStatusReportingJob->setVerb(SimpleApiJob::Verb::Put);
|
||||
connect(clientStatusReportingJob, &JsonApiJob::jsonReceived, [this](const QJsonDocument &json, int statusCode) {
|
||||
if (statusCode == 0 || statusCode == 200 || statusCode == 201 || statusCode == 204) {
|
||||
const auto metaFromJson = json.object().value("ocs").toObject().value("meta").toObject();
|
||||
const auto codeFromJson = metaFromJson.value("statuscode").toInt();
|
||||
const auto metaFromJson = json.object().value(QStringLiteral("ocs")).toObject().value(QStringLiteral("meta")).toObject();
|
||||
const auto codeFromJson = metaFromJson.value(QStringLiteral("statuscode")).toInt();
|
||||
if (codeFromJson == 0 || codeFromJson == 200 || codeFromJson == 201 || codeFromJson == 204) {
|
||||
reportToServerSentSuccessfully();
|
||||
return;
|
||||
@ -221,9 +230,7 @@ void ClientStatusReporting::sendReportToServer()
|
||||
|
||||
void ClientStatusReporting::reportToServerSentSuccessfully()
|
||||
{
|
||||
if (!deleteClientStatusReportingRecords()) {
|
||||
qCDebug(lcClientStatusReporting) << "Error deleting client status report.";
|
||||
}
|
||||
deleteClientStatusReportingRecords();
|
||||
setLastSentReportTimestamp(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
|
||||
}
|
||||
|
||||
@ -238,12 +245,12 @@ QString ClientStatusReporting::makeDbPath() const
|
||||
return ConfigFile().configPath() + QStringLiteral(".userdata_%1.db").arg(QString::fromLatin1(databaseIdHash.left(6).toHex()));
|
||||
}
|
||||
|
||||
qulonglong ClientStatusReporting::getLastSentReportTimestamp() const
|
||||
quint64 ClientStatusReporting::getLastSentReportTimestamp() const
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare("SELECT value FROM keyvalue WHERE key = (:key)");
|
||||
query.bindValue(":key", lastSentReportTimestamp);
|
||||
const auto prepareResult = query.prepare(QStringLiteral("SELECT value FROM keyvalue WHERE key = (:key)"));
|
||||
query.bindValue(QStringLiteral(":key"), lastSentReportTimestamp);
|
||||
if (!prepareResult || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not get last sent report timestamp from keyvalue table. No such record:" << lastSentReportTimestamp;
|
||||
return 0;
|
||||
@ -252,9 +259,37 @@ qulonglong ClientStatusReporting::getLastSentReportTimestamp() const
|
||||
qCDebug(lcClientStatusReporting) << "Could not get last sent report timestamp from keyvalue table:" << query.lastError().text();
|
||||
return 0;
|
||||
}
|
||||
return query.value(query.record().indexOf(QStringLiteral("value"))).toULongLong();
|
||||
}
|
||||
|
||||
int valueIndex = query.record().indexOf("value");
|
||||
return query.value(valueIndex).toULongLong();
|
||||
void ClientStatusReporting::setStatusNamesHash(const QByteArray &hash) const
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare(QStringLiteral("INSERT OR REPLACE INTO keyvalue (key, value) VALUES(:key, :value);"));
|
||||
query.bindValue(QStringLiteral(":key"), statusNamesHash);
|
||||
query.bindValue(QStringLiteral(":value"), hash);
|
||||
if (!prepareResult || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not set status names hash.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray ClientStatusReporting::getStatusNamesHash() const
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare(QStringLiteral("SELECT value FROM keyvalue WHERE key = (:key)"));
|
||||
query.bindValue(QStringLiteral(":key"), statusNamesHash);
|
||||
if (!prepareResult || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not get status names hash. No such record:" << statusNamesHash;
|
||||
return {};
|
||||
}
|
||||
if (!query.next()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not get status names hash:" << query.lastError().text();
|
||||
return {};
|
||||
}
|
||||
return query.value(query.record().indexOf(QStringLiteral("value"))).toByteArray();
|
||||
}
|
||||
|
||||
QVariantMap ClientStatusReporting::prepareReport() const
|
||||
@ -294,13 +329,13 @@ QVariantMap ClientStatusReporting::prepareReport() const
|
||||
return report;
|
||||
}
|
||||
|
||||
void ClientStatusReporting::setLastSentReportTimestamp(const qulonglong timestamp)
|
||||
void ClientStatusReporting::setLastSentReportTimestamp(const quint64 timestamp) const
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
QSqlQuery query;
|
||||
const auto prepareResult = query.prepare("INSERT OR REPLACE INTO keyvalue (key, value) VALUES(:key, :value);");
|
||||
query.bindValue(":key", lastSentReportTimestamp);
|
||||
query.bindValue(":value", timestamp);
|
||||
const auto prepareResult = query.prepare(QStringLiteral("INSERT OR REPLACE INTO keyvalue (key, value) VALUES(:key, :value);"));
|
||||
query.bindValue(QStringLiteral(":key"), lastSentReportTimestamp);
|
||||
query.bindValue(QStringLiteral(":value"), timestamp);
|
||||
if (!prepareResult || !query.exec()) {
|
||||
qCDebug(lcClientStatusReporting) << "Could not set last sent report timestamp from keyvalue table. No such record:" << lastSentReportTimestamp;
|
||||
return;
|
||||
@ -346,7 +381,7 @@ QByteArray ClientStatusReporting::statusStringFromNumber(const Status status)
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ClientStatusReporting::classifyStatus(const Status status)
|
||||
QByteArray ClientStatusReporting::classifyStatus(const Status status)
|
||||
{
|
||||
Q_ASSERT(status >= 0 && status < Count);
|
||||
if (status < 0 || status >= Status::Count) {
|
||||
@ -360,7 +395,7 @@ QString ClientStatusReporting::classifyStatus(const Status status)
|
||||
case DownloadError_ConflictInvalidCharacters:
|
||||
case UploadError_Conflict:
|
||||
case UploadError_ConflictInvalidCharacters:
|
||||
return QStringLiteral("sync_conflicts");
|
||||
return QByteArrayLiteral("sync_conflicts");
|
||||
case DownloadError_Cannot_Create_File:
|
||||
case DownloadError_No_Free_Space:
|
||||
case DownloadError_ServerError:
|
||||
@ -375,6 +410,6 @@ QString ClientStatusReporting::classifyStatus(const Status status)
|
||||
return {};
|
||||
}
|
||||
int ClientStatusReporting::clientStatusReportingTrySendTimerInterval = 1000 * 60 * 2; // check if the time has come, every 2 minutes
|
||||
int ClientStatusReporting::repordSendIntervalMs = 24 * 60 * 60 * 1000; // once every 24 hours
|
||||
quint64 ClientStatusReporting::repordSendIntervalMs = 24 * 60 * 60 * 1000; // once every 24 hours
|
||||
QString ClientStatusReporting::dbPathForTesting;
|
||||
}
|
||||
|
||||
@ -14,19 +14,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "owncloudlib.h"
|
||||
#include "accountfwd.h"
|
||||
#include <common/result.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QtGlobal>
|
||||
#include <QByteArray>
|
||||
#include <qstring.h>
|
||||
#include <QTimer>
|
||||
#include <QtSql>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
class TestClientStatusReporting;
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QtSql>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@ -38,57 +36,73 @@ class OWNCLOUDSYNC_EXPORT ClientStatusReporting : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status {
|
||||
DownloadError_Cannot_Create_File = 100,
|
||||
DownloadError_Conflict = 101,
|
||||
DownloadError_ConflictCaseClash = 102,
|
||||
DownloadError_ConflictInvalidCharacters = 103,
|
||||
DownloadError_No_Free_Space = 104,
|
||||
DownloadError_ServerError = 105,
|
||||
DownloadError_Virtual_File_Hydration_Failure = 106,
|
||||
UploadError_Conflict = 107,
|
||||
UploadError_ConflictInvalidCharacters = 108,
|
||||
UploadError_No_Free_Space = 109,
|
||||
UploadError_No_Write_Permissions = 110,
|
||||
UploadError_ServerError = 111,
|
||||
Count = UploadError_ServerError + 1,
|
||||
DownloadError_Cannot_Create_File = 0,
|
||||
DownloadError_Conflict,
|
||||
DownloadError_ConflictCaseClash,
|
||||
DownloadError_ConflictInvalidCharacters,
|
||||
DownloadError_No_Free_Space,
|
||||
DownloadError_ServerError,
|
||||
DownloadError_Virtual_File_Hydration_Failure,
|
||||
UploadError_Conflict,
|
||||
UploadError_ConflictInvalidCharacters,
|
||||
UploadError_No_Free_Space,
|
||||
UploadError_No_Write_Permissions,
|
||||
UploadError_ServerError,
|
||||
Count,
|
||||
};
|
||||
|
||||
explicit ClientStatusReporting(Account *account, QObject *parent = nullptr);
|
||||
~ClientStatusReporting();
|
||||
~ClientStatusReporting() override;
|
||||
|
||||
static QByteArray statusStringFromNumber(const Status status);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void reportClientStatus(const Status status);
|
||||
// reporting must happen via Account
|
||||
void reportClientStatus(const Status status) const;
|
||||
|
||||
[[nodiscard]] Result<void, QString> setClientStatusReportingRecord(const ClientStatusReportingRecord &record);
|
||||
[[nodiscard]] Result<void, QString> setClientStatusReportingRecord(const ClientStatusReportingRecord &record) const;
|
||||
[[nodiscard]] QVector<ClientStatusReportingRecord> getClientStatusReportingRecords() const;
|
||||
[[nodiscard]] bool deleteClientStatusReportingRecords();
|
||||
void setLastSentReportTimestamp(const qulonglong timestamp);
|
||||
[[nodiscard]] qulonglong getLastSentReportTimestamp() const;
|
||||
void deleteClientStatusReportingRecords() const;
|
||||
|
||||
void setLastSentReportTimestamp(const quint64 timestamp) const;
|
||||
[[nodiscard]] quint64 getLastSentReportTimestamp() const;
|
||||
|
||||
void setStatusNamesHash(const QByteArray &hash) const;
|
||||
[[nodiscard]] QByteArray getStatusNamesHash() const;
|
||||
|
||||
[[nodiscard]] QVariantMap prepareReport() const;
|
||||
void reportToServerSentSuccessfully();
|
||||
|
||||
[[nodiscard]] QString makeDbPath() const;
|
||||
|
||||
private slots:
|
||||
void sendReportToServer();
|
||||
|
||||
private:
|
||||
static QByteArray statusStringFromNumber(const Status status);
|
||||
static QString classifyStatus(const Status status);
|
||||
static QByteArray classifyStatus(const Status status);
|
||||
|
||||
public:
|
||||
static int clientStatusReportingTrySendTimerInterval;
|
||||
static int repordSendIntervalMs;
|
||||
|
||||
static quint64 repordSendIntervalMs;
|
||||
// this must be set in unit tests on init
|
||||
static QString dbPathForTesting;
|
||||
|
||||
private:
|
||||
|
||||
Account *_account = nullptr;
|
||||
QHash<int, QPair<QByteArray, qint64>> _statusNamesAndHashes;
|
||||
|
||||
QSqlDatabase _database;
|
||||
|
||||
bool _isInitialized = false;
|
||||
|
||||
QTimer _clientStatusReportingSendTimer;
|
||||
|
||||
QHash<int, QPair<QByteArray, quint64>> _statusNamesAndHashes;
|
||||
|
||||
// inspired by SyncJournalDb
|
||||
mutable QRecursiveMutex _mutex;
|
||||
|
||||
friend class Account;
|
||||
friend class TestClientStatusReporting;
|
||||
};
|
||||
}
|
||||
|
||||
@ -19,6 +19,6 @@ namespace OCC
|
||||
|
||||
bool ClientStatusReportingRecord::isValid() const
|
||||
{
|
||||
return _status >= 0 && !_name.isEmpty() && _nameHash > 0 && _lastOccurence > 0;
|
||||
return _status >= 0 && !_name.isEmpty() && _lastOccurence > 0;
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#include "ocsynclib.h"
|
||||
#include "owncloudlib.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QByteArray>
|
||||
@ -23,10 +23,10 @@ namespace OCC
|
||||
* @brief The ClientStatusReportingRecord class
|
||||
* @ingroup libsync
|
||||
*/
|
||||
struct OCSYNC_EXPORT ClientStatusReportingRecord {
|
||||
|
||||
struct OWNCLOUDSYNC_EXPORT ClientStatusReportingRecord {
|
||||
QByteArray _name;
|
||||
int _status = -1;
|
||||
quint64 _nameHash = 0;
|
||||
quint64 _numOccurences = 1;
|
||||
quint64 _lastOccurence = 0;
|
||||
|
||||
@ -943,7 +943,7 @@ JsonApiJob::JsonApiJob(const AccountPtr &account, const QString &path, QObject *
|
||||
|
||||
void JsonApiJob::setBody(const QJsonDocument &body)
|
||||
{
|
||||
SimpleApiJob::setBody(body.toJson(QJsonDocument::JsonFormat::Compact));
|
||||
SimpleApiJob::setBody(body.toJson());
|
||||
qCDebug(lcJsonApiJob) << "Set body for request:" << SimpleApiJob::body();
|
||||
if (!SimpleApiJob::body().isEmpty()) {
|
||||
request().setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 by Oleksandr Zolotov <alex@nextcloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#include "ocsclientstatusreportingjob.h"
|
||||
#include "networkjobs.h"
|
||||
#include "account.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
OcsClientStatusReportingJob::OcsClientStatusReportingJob(AccountPtr account)
|
||||
: OcsJob(account)
|
||||
{
|
||||
setPath(QStringLiteral("ocs/v2.php/apps/security_guard/diagnostics"));
|
||||
connect(this, &OcsJob::jobFinished, this, &OcsClientStatusReportingJob::jobDone);
|
||||
}
|
||||
|
||||
void OcsClientStatusReportingJob::sendStatusReport(const QVariant &jsonData)
|
||||
{
|
||||
setVerb("PUT");
|
||||
|
||||
addRawHeader("Ocs-APIREQUEST", "true");
|
||||
addRawHeader("Content-Type", "application/json");
|
||||
|
||||
const auto url = Utility::concatUrlPath(account()->url(), path());
|
||||
sendRequest(_verb, url, _request, QJsonDocument::fromVariant(jsonData.toMap()).toJson());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
void OcsClientStatusReportingJob::jobDone(QJsonDocument reply)
|
||||
{
|
||||
emit jobFinished(reply, {});
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 by Oleksandr Zolotov <alex@nextcloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ocsjob.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QVariant>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* @brief The OcsClientStatusReportingJob class
|
||||
* @ingroup gui
|
||||
*
|
||||
* Handle sending client status reports via OCS Diagnostics API.
|
||||
*/
|
||||
class OcsClientStatusReportingJob : public OcsJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OcsClientStatusReportingJob(AccountPtr account);
|
||||
void sendStatusReport(const QVariant &jsonData);
|
||||
|
||||
signals:
|
||||
void jobFinished(QJsonDocument reply, QVariant value);
|
||||
|
||||
private slots:
|
||||
void jobDone(QJsonDocument reply);
|
||||
};
|
||||
}
|
||||
@ -1282,9 +1282,8 @@ void PropagateDownloadFile::downloadFinished()
|
||||
|
||||
// Maybe what we downloaded was a conflict file? If so, set a conflict record.
|
||||
// (the data was prepared in slotGetFinished above)
|
||||
if (_conflictRecord.isValid()) {
|
||||
if (_conflictRecord.isValid())
|
||||
propagator()->_journal->setConflictRecord(_conflictRecord);
|
||||
}
|
||||
|
||||
if (vfs && vfs->mode() == Vfs::WithSuffix) {
|
||||
// If the virtual file used to have a different name and db
|
||||
|
||||
Loading…
Reference in New Issue
Block a user